@eagleoutice/flowr 2.0.2 → 2.0.4
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/benchmark/slicer.d.ts +1 -0
- package/benchmark/slicer.js +39 -3
- package/benchmark/stats/print.d.ts +1 -0
- package/benchmark/stats/print.js +66 -22
- package/benchmark/stats/size-of.d.ts +3 -0
- package/benchmark/stats/size-of.js +68 -0
- package/benchmark/stats/stats.d.ts +18 -0
- package/benchmark/summarizer/data.d.ts +14 -0
- package/benchmark/summarizer/first-phase/input.d.ts +2 -2
- package/benchmark/summarizer/first-phase/input.js +21 -21
- package/benchmark/summarizer/first-phase/process.d.ts +3 -1
- package/benchmark/summarizer/first-phase/process.js +38 -4
- package/benchmark/summarizer/second-phase/graph.js +7 -0
- package/benchmark/summarizer/second-phase/process.js +47 -25
- package/benchmark/summarizer/summarizer.d.ts +1 -0
- package/benchmark/summarizer/summarizer.js +23 -10
- package/dataflow/environments/environment.d.ts +2 -1
- package/dataflow/graph/edge.d.ts +2 -3
- package/dataflow/graph/edge.js +2 -2
- package/dataflow/graph/graph.js +3 -2
- package/dataflow/internal/process/process-value.js +0 -1
- package/package.json +2 -1
- package/util/mermaid/dfg.d.ts +0 -1
- package/util/mermaid/dfg.js +1 -8
- package/util/version.js +1 -1
package/benchmark/slicer.d.ts
CHANGED
|
@@ -51,6 +51,7 @@ export declare class BenchmarkSlicer {
|
|
|
51
51
|
/** Measures all data recorded *once* per slicer (complete setup up to the dataflow graph creation) */
|
|
52
52
|
private readonly commonMeasurements;
|
|
53
53
|
private readonly perSliceMeasurements;
|
|
54
|
+
private readonly deltas;
|
|
54
55
|
private readonly shell;
|
|
55
56
|
private stats;
|
|
56
57
|
private loadedXml;
|
package/benchmark/slicer.js
CHANGED
|
@@ -19,6 +19,7 @@ const default_pipelines_1 = require("../core/steps/pipeline/default-pipelines");
|
|
|
19
19
|
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
|
+
const size_of_1 = require("./stats/size-of");
|
|
22
23
|
exports.benchmarkLogger = log_1.log.getSubLogger({ name: 'benchmark' });
|
|
23
24
|
/**
|
|
24
25
|
* A slicer that can be used to slice exactly one file (multiple times).
|
|
@@ -33,6 +34,7 @@ class BenchmarkSlicer {
|
|
|
33
34
|
/** Measures all data recorded *once* per slicer (complete setup up to the dataflow graph creation) */
|
|
34
35
|
commonMeasurements = new stopwatch_1.Measurements();
|
|
35
36
|
perSliceMeasurements = new Map();
|
|
37
|
+
deltas = new Map();
|
|
36
38
|
shell;
|
|
37
39
|
stats;
|
|
38
40
|
loadedXml;
|
|
@@ -105,8 +107,8 @@ class BenchmarkSlicer {
|
|
|
105
107
|
const split = loadedContent.split('\n');
|
|
106
108
|
const nonWhitespace = (0, strings_1.withoutWhitespace)(loadedContent).length;
|
|
107
109
|
this.stats = {
|
|
108
|
-
commonMeasurements: new Map(),
|
|
109
110
|
perSliceMeasurements: this.perSliceMeasurements,
|
|
111
|
+
memory: this.deltas,
|
|
110
112
|
request,
|
|
111
113
|
input: {
|
|
112
114
|
numberOfLines: split.length,
|
|
@@ -124,8 +126,15 @@ class BenchmarkSlicer {
|
|
|
124
126
|
numberOfNodes: [...this.dataflow.graph.vertices(true)].length,
|
|
125
127
|
numberOfEdges: numberOfEdges,
|
|
126
128
|
numberOfCalls: numberOfCalls,
|
|
127
|
-
numberOfFunctionDefinitions: numberOfDefinitions
|
|
128
|
-
|
|
129
|
+
numberOfFunctionDefinitions: numberOfDefinitions,
|
|
130
|
+
sizeOfObject: (0, size_of_1.getSizeOfDfGraph)(this.dataflow.graph)
|
|
131
|
+
},
|
|
132
|
+
// these are all properly initialized in finish()
|
|
133
|
+
commonMeasurements: new Map(),
|
|
134
|
+
retrieveTimePerToken: { raw: 0, normalized: 0 },
|
|
135
|
+
normalizeTimePerToken: { raw: 0, normalized: 0 },
|
|
136
|
+
dataflowTimePerToken: { raw: 0, normalized: 0 },
|
|
137
|
+
totalCommonTimePerToken: { raw: 0, normalized: 0 }
|
|
129
138
|
};
|
|
130
139
|
}
|
|
131
140
|
/**
|
|
@@ -177,7 +186,15 @@ class BenchmarkSlicer {
|
|
|
177
186
|
}
|
|
178
187
|
/** Bridging the gap between the new internal and the old names for the benchmarking */
|
|
179
188
|
async measureCommonStep(expectedStep, keyToMeasure) {
|
|
189
|
+
const memoryInit = process.memoryUsage();
|
|
180
190
|
const { result } = await this.commonMeasurements.measureAsync(keyToMeasure, () => this.pipeline.nextStep(expectedStep));
|
|
191
|
+
const memoryEnd = process.memoryUsage();
|
|
192
|
+
this.deltas.set(keyToMeasure, {
|
|
193
|
+
heap: memoryEnd.heapUsed - memoryInit.heapUsed,
|
|
194
|
+
rss: memoryEnd.rss - memoryInit.rss,
|
|
195
|
+
external: memoryEnd.external - memoryInit.external,
|
|
196
|
+
buffs: memoryEnd.arrayBuffers - memoryInit.arrayBuffers
|
|
197
|
+
});
|
|
181
198
|
return result;
|
|
182
199
|
}
|
|
183
200
|
async measureSliceStep(expectedStep, measure, keyToMeasure) {
|
|
@@ -221,6 +238,25 @@ class BenchmarkSlicer {
|
|
|
221
238
|
this.finished = true;
|
|
222
239
|
}
|
|
223
240
|
this.stats.commonMeasurements = this.commonMeasurements.get();
|
|
241
|
+
const retrieveTime = Number(this.stats.commonMeasurements.get('retrieve AST from R code'));
|
|
242
|
+
const normalizeTime = Number(this.stats.commonMeasurements.get('normalize R AST'));
|
|
243
|
+
const dataflowTime = Number(this.stats.commonMeasurements.get('produce dataflow information'));
|
|
244
|
+
this.stats.retrieveTimePerToken = {
|
|
245
|
+
raw: retrieveTime / this.stats.input.numberOfRTokens,
|
|
246
|
+
normalized: retrieveTime / this.stats.input.numberOfNormalizedTokens
|
|
247
|
+
};
|
|
248
|
+
this.stats.normalizeTimePerToken = {
|
|
249
|
+
raw: normalizeTime / this.stats.input.numberOfRTokens,
|
|
250
|
+
normalized: normalizeTime / this.stats.input.numberOfNormalizedTokens
|
|
251
|
+
};
|
|
252
|
+
this.stats.dataflowTimePerToken = {
|
|
253
|
+
raw: dataflowTime / this.stats.input.numberOfRTokens,
|
|
254
|
+
normalized: dataflowTime / this.stats.input.numberOfNormalizedTokens
|
|
255
|
+
};
|
|
256
|
+
this.stats.totalCommonTimePerToken = {
|
|
257
|
+
raw: (retrieveTime + normalizeTime + dataflowTime) / this.stats.input.numberOfRTokens,
|
|
258
|
+
normalized: (retrieveTime + normalizeTime + dataflowTime) / this.stats.input.numberOfNormalizedTokens
|
|
259
|
+
};
|
|
224
260
|
return {
|
|
225
261
|
stats: this.stats,
|
|
226
262
|
parse: this.loadedXml,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { SummarizedSlicerStats, UltimateSlicerStats } from '../summarizer/data';
|
|
2
|
+
export declare function formatNanoseconds(nanoseconds: bigint | number): string;
|
|
2
3
|
/**
|
|
3
4
|
* Converts the given stats to a human-readable string.
|
|
4
5
|
* You may have to {@link summarizeSlicerStats | summarize} the stats first.
|
package/benchmark/stats/print.js
CHANGED
|
@@ -1,26 +1,29 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ultimateStats2String = exports.stats2string = void 0;
|
|
3
|
+
exports.ultimateStats2String = exports.stats2string = exports.formatNanoseconds = void 0;
|
|
4
4
|
const assert_1 = require("../../util/assert");
|
|
5
5
|
const padSize = 15;
|
|
6
6
|
function pad(string) {
|
|
7
7
|
return String(string).padStart(padSize, ' ');
|
|
8
8
|
}
|
|
9
|
-
function divWithRest(dividend, divisor) {
|
|
10
|
-
return [dividend / divisor, dividend % divisor];
|
|
11
|
-
}
|
|
12
9
|
function formatNanoseconds(nanoseconds) {
|
|
13
10
|
if (nanoseconds < 0) {
|
|
14
11
|
return '??';
|
|
15
12
|
}
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
13
|
+
const wholeNanos = typeof nanoseconds === 'bigint' ? nanoseconds : BigInt(Math.round(nanoseconds));
|
|
14
|
+
const nanos = wholeNanos % BigInt(1e+6);
|
|
15
|
+
const wholeMillis = wholeNanos / BigInt(1e+6);
|
|
16
|
+
const millis = wholeMillis % BigInt(1000);
|
|
17
|
+
const wholeSeconds = wholeMillis / BigInt(1000);
|
|
18
|
+
if (wholeSeconds > 0) {
|
|
19
|
+
const nanoString = nanos > 0 ? `:${nanos}` : '';
|
|
20
|
+
return pad(`${wholeSeconds}.${String(millis).padStart(3, '0')}${nanoString} s`);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
return pad(`${millis}:${String(nanos).padStart(6, '0')}ms`);
|
|
24
|
+
}
|
|
23
25
|
}
|
|
26
|
+
exports.formatNanoseconds = formatNanoseconds;
|
|
24
27
|
function print(measurements, key) {
|
|
25
28
|
const time = measurements.get(key);
|
|
26
29
|
(0, assert_1.guard)(time !== undefined, `Measurement for ${JSON.stringify(key)} not found`);
|
|
@@ -61,6 +64,16 @@ function printCountSummarizedMeasurements(stats) {
|
|
|
61
64
|
const range = `${stats.min} - ${stats.max}`.padStart(padSize, ' ');
|
|
62
65
|
return `${range} (median: ${stats.median}, mean: ${stats.mean}, std: ${stats.std})`;
|
|
63
66
|
}
|
|
67
|
+
const units = ['bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
|
|
68
|
+
// based on https://stackoverflow.com/a/39906526
|
|
69
|
+
function convertNumberToNiceBytes(x) {
|
|
70
|
+
let n = Math.abs(x);
|
|
71
|
+
let l = 0;
|
|
72
|
+
while (n >= 1024 && ++l) {
|
|
73
|
+
n = n / 1024;
|
|
74
|
+
}
|
|
75
|
+
return pad((x < 0 ? '-' : '') + n.toFixed(n < 10 && l > 0 ? 1 : 0) + ' ' + units[l]);
|
|
76
|
+
}
|
|
64
77
|
/**
|
|
65
78
|
* Converts the given stats to a human-readable string.
|
|
66
79
|
* You may have to {@link summarizeSlicerStats | summarize} the stats first.
|
|
@@ -70,16 +83,30 @@ function stats2string(stats) {
|
|
|
70
83
|
Request: ${JSON.stringify(stats.request)}
|
|
71
84
|
Shell init time: ${print(stats.commonMeasurements, 'initialize R session')}
|
|
72
85
|
AST retrieval: ${print(stats.commonMeasurements, 'retrieve AST from R code')}
|
|
86
|
+
AST retrieval per token: ${formatNanoseconds(stats.retrieveTimePerToken.normalized)}
|
|
87
|
+
AST retrieval per R token: ${formatNanoseconds(stats.retrieveTimePerToken.raw)}
|
|
73
88
|
AST normalization: ${print(stats.commonMeasurements, 'normalize R AST')}
|
|
89
|
+
AST normalization per token: ${formatNanoseconds(stats.normalizeTimePerToken.normalized)}
|
|
90
|
+
AST normalization per R token:${formatNanoseconds(stats.normalizeTimePerToken.raw)}
|
|
74
91
|
Dataflow creation: ${print(stats.commonMeasurements, 'produce dataflow information')}
|
|
92
|
+
Dataflow creation per token: ${formatNanoseconds(stats.dataflowTimePerToken.normalized)}
|
|
93
|
+
Dataflow creation per R token:${formatNanoseconds(stats.dataflowTimePerToken.raw)}
|
|
94
|
+
Total common time per token: ${formatNanoseconds(stats.totalCommonTimePerToken.normalized)}
|
|
95
|
+
Total common time per R token:${formatNanoseconds(stats.totalCommonTimePerToken.raw)}
|
|
75
96
|
|
|
76
97
|
Slicing summary for ${stats.perSliceMeasurements.numberOfSlices} slice${stats.perSliceMeasurements.numberOfSlices !== 1 ? 's' : ''}:`;
|
|
77
98
|
if (stats.perSliceMeasurements.numberOfSlices > 0) {
|
|
78
99
|
result += `
|
|
79
|
-
Total:
|
|
80
|
-
Slice creation:
|
|
81
|
-
|
|
82
|
-
|
|
100
|
+
Total: ${printSummarizedMeasurements(stats.perSliceMeasurements, 'total')}
|
|
101
|
+
Slice creation: ${printSummarizedMeasurements(stats.perSliceMeasurements, 'static slicing')}
|
|
102
|
+
Slice creation per token in slice: ${formatSummarizedTimeMeasure(stats.perSliceMeasurements.sliceTimePerToken.normalized)}
|
|
103
|
+
Slice creation per R token in slice:${formatSummarizedTimeMeasure(stats.perSliceMeasurements.sliceTimePerToken.raw)}
|
|
104
|
+
Reconstruction: ${printSummarizedMeasurements(stats.perSliceMeasurements, 'reconstruct code')}
|
|
105
|
+
Reconstruction per token in slice: ${formatSummarizedTimeMeasure(stats.perSliceMeasurements.reconstructTimePerToken.normalized)}
|
|
106
|
+
Reconstruction per R token in slice:${formatSummarizedTimeMeasure(stats.perSliceMeasurements.reconstructTimePerToken.raw)}
|
|
107
|
+
Total per token in slice: ${formatSummarizedTimeMeasure(stats.perSliceMeasurements.totalPerSliceTimePerToken.normalized)}
|
|
108
|
+
Total per R token in slice: ${formatSummarizedTimeMeasure(stats.perSliceMeasurements.totalPerSliceTimePerToken.raw)}
|
|
109
|
+
Used Slice Criteria Sizes: ${printCountSummarizedMeasurements(stats.perSliceMeasurements.sliceCriteriaSizes)}
|
|
83
110
|
Result Slice Sizes:
|
|
84
111
|
Number of lines: ${printCountSummarizedMeasurements(stats.perSliceMeasurements.sliceSize.lines)}
|
|
85
112
|
Number of non-empty lines: ${printCountSummarizedMeasurements(stats.perSliceMeasurements.sliceSize.nonEmptyLines)}
|
|
@@ -113,7 +140,8 @@ Dataflow:
|
|
|
113
140
|
Number of nodes: ${pad(stats.dataflow.numberOfNodes)}
|
|
114
141
|
Number of edges: ${pad(stats.dataflow.numberOfEdges)}
|
|
115
142
|
Number of calls: ${pad(stats.dataflow.numberOfCalls)}
|
|
116
|
-
Number of function defs: ${pad(stats.dataflow.numberOfFunctionDefinitions)}
|
|
143
|
+
Number of function defs: ${pad(stats.dataflow.numberOfFunctionDefinitions)}
|
|
144
|
+
Size of graph: ${convertNumberToNiceBytes(stats.dataflow.sizeOfObject)}`;
|
|
117
145
|
}
|
|
118
146
|
exports.stats2string = stats2string;
|
|
119
147
|
function ultimateStats2String(stats) {
|
|
@@ -122,15 +150,29 @@ function ultimateStats2String(stats) {
|
|
|
122
150
|
Summarized: ${stats.totalRequests} requests and ${stats.totalSlices} slices
|
|
123
151
|
Shell init time: ${formatSummarizedTimeMeasure(stats.commonMeasurements.get('initialize R session'))}
|
|
124
152
|
AST retrieval: ${formatSummarizedTimeMeasure(stats.commonMeasurements.get('retrieve AST from R code'))}
|
|
153
|
+
AST retrieval per token: ${formatSummarizedTimeMeasure(stats.retrieveTimePerToken.normalized)}
|
|
154
|
+
AST retrieval per R token: ${formatSummarizedTimeMeasure(stats.retrieveTimePerToken.raw)}
|
|
125
155
|
AST normalization: ${formatSummarizedTimeMeasure(stats.commonMeasurements.get('normalize R AST'))}
|
|
156
|
+
AST normalization per token: ${formatSummarizedTimeMeasure(stats.normalizeTimePerToken.normalized)}
|
|
157
|
+
AST normalization per R token:${formatSummarizedTimeMeasure(stats.normalizeTimePerToken.raw)}
|
|
126
158
|
Dataflow creation: ${formatSummarizedTimeMeasure(stats.commonMeasurements.get('produce dataflow information'))}
|
|
159
|
+
Dataflow creation per token: ${formatSummarizedTimeMeasure(stats.dataflowTimePerToken.normalized)}
|
|
160
|
+
Dataflow creation per R token:${formatSummarizedTimeMeasure(stats.dataflowTimePerToken.raw)}
|
|
161
|
+
Total common time per token: ${formatSummarizedTimeMeasure(stats.totalCommonTimePerToken.normalized)}
|
|
162
|
+
Total common time per R token:${formatSummarizedTimeMeasure(stats.totalCommonTimePerToken.raw)}
|
|
127
163
|
|
|
128
164
|
Slice summary for:
|
|
129
|
-
Total:
|
|
130
|
-
Slice creation:
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
165
|
+
Total: ${formatSummarizedTimeMeasure(stats.perSliceMeasurements.get('total'))}
|
|
166
|
+
Slice creation: ${formatSummarizedTimeMeasure(stats.perSliceMeasurements.get('static slicing'))}
|
|
167
|
+
Slice creation per token in slice: ${formatSummarizedTimeMeasure(stats.sliceTimePerToken.normalized)}
|
|
168
|
+
Slice creation per R token in slice:${formatSummarizedTimeMeasure(stats.sliceTimePerToken.raw)}
|
|
169
|
+
Reconstruction: ${formatSummarizedTimeMeasure(stats.perSliceMeasurements.get('reconstruct code'))}
|
|
170
|
+
Reconstruction per token in slice: ${formatSummarizedTimeMeasure(stats.reconstructTimePerToken.normalized)}
|
|
171
|
+
Reconstruction per R token in slice:${formatSummarizedTimeMeasure(stats.reconstructTimePerToken.raw)}
|
|
172
|
+
Total per token in slice: ${formatSummarizedTimeMeasure(stats.totalPerSliceTimePerToken.normalized)}
|
|
173
|
+
Total per R token in slice: ${formatSummarizedTimeMeasure(stats.totalPerSliceTimePerToken.raw)}
|
|
174
|
+
Failed to Re-Parse: ${pad(stats.failedToRepParse)}/${stats.totalSlices}
|
|
175
|
+
Times hit Threshold: ${pad(stats.timesHitThreshold)}/${stats.totalSlices}
|
|
134
176
|
${reduction2String('Reductions', stats.reduction)}
|
|
135
177
|
${reduction2String('Reductions without comments and empty lines', stats.reductionNoFluff)}
|
|
136
178
|
|
|
@@ -153,7 +195,9 @@ Dataflow:
|
|
|
153
195
|
Number of nodes: ${formatSummarizedMeasure(stats.dataflow.numberOfNodes)}
|
|
154
196
|
Number of edges: ${formatSummarizedMeasure(stats.dataflow.numberOfEdges)}
|
|
155
197
|
Number of calls: ${formatSummarizedMeasure(stats.dataflow.numberOfCalls)}
|
|
156
|
-
Number of function defs: ${formatSummarizedMeasure(stats.dataflow.numberOfFunctionDefinitions)}
|
|
198
|
+
Number of function defs: ${formatSummarizedMeasure(stats.dataflow.numberOfFunctionDefinitions)}
|
|
199
|
+
Size of graph: ${formatSummarizedMeasure(stats.dataflow.sizeOfObject, convertNumberToNiceBytes)}
|
|
200
|
+
`;
|
|
157
201
|
}
|
|
158
202
|
exports.ultimateStats2String = ultimateStats2String;
|
|
159
203
|
function reduction2String(title, reduction) {
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getSizeOfDfGraph = void 0;
|
|
7
|
+
const environment_1 = require("../../dataflow/environments/environment");
|
|
8
|
+
const object_sizeof_1 = __importDefault(require("object-sizeof"));
|
|
9
|
+
/* we have to kill all processors linked in the default environment as they cannot be serialized and they are shared anyway */
|
|
10
|
+
function killBuiltInEnv(env) {
|
|
11
|
+
if (env === undefined) {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
else if (env.id === environment_1.BuiltInEnvironment.id) {
|
|
15
|
+
/* in this case, the reference would be shared for sure */
|
|
16
|
+
return {
|
|
17
|
+
id: env.id,
|
|
18
|
+
parent: killBuiltInEnv(env.parent),
|
|
19
|
+
memory: new Map()
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
const memory = new Map();
|
|
23
|
+
for (const [k, v] of env.memory) {
|
|
24
|
+
memory.set(k, v.filter(v => !v.kind.startsWith('built-in') && !('processor' in v)));
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
id: env.id,
|
|
28
|
+
parent: killBuiltInEnv(env.parent),
|
|
29
|
+
memory
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/** Returns the size of the given df graph in bytes (without sharing in-memory) */
|
|
33
|
+
function getSizeOfDfGraph(df) {
|
|
34
|
+
const verts = [];
|
|
35
|
+
for (const [, v] of df.vertices(true)) {
|
|
36
|
+
let vertex = v;
|
|
37
|
+
if (vertex.environment) {
|
|
38
|
+
vertex = {
|
|
39
|
+
...vertex,
|
|
40
|
+
environment: {
|
|
41
|
+
...vertex.environment,
|
|
42
|
+
current: killBuiltInEnv(v.environment.current)
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
if (vertex.tag === "function-definition" /* VertexType.FunctionDefinition */) {
|
|
47
|
+
vertex = {
|
|
48
|
+
...vertex,
|
|
49
|
+
subflow: {
|
|
50
|
+
...vertex.subflow,
|
|
51
|
+
environment: {
|
|
52
|
+
...vertex.subflow.environment,
|
|
53
|
+
current: killBuiltInEnv(vertex.subflow.environment.current)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
vertex = {
|
|
59
|
+
...vertex,
|
|
60
|
+
/* shared anyway by using constants */
|
|
61
|
+
tag: 0
|
|
62
|
+
};
|
|
63
|
+
verts.push(vertex);
|
|
64
|
+
}
|
|
65
|
+
return (0, object_sizeof_1.default)([...verts, ...df.edges()]);
|
|
66
|
+
}
|
|
67
|
+
exports.getSizeOfDfGraph = getSizeOfDfGraph;
|
|
68
|
+
//# sourceMappingURL=size-of.js.map
|
|
@@ -2,6 +2,8 @@ import type { SingleSlicingCriterion, SlicingCriteria } from '../../slicing/crit
|
|
|
2
2
|
import type { NodeId } from '../../r-bridge/lang-4.x/ast/model/processing/node-id';
|
|
3
3
|
import type { ReconstructionResult } from '../../reconstruct/reconstruct';
|
|
4
4
|
import type { RParseRequestFromFile, RParseRequestFromText } from '../../r-bridge/retriever';
|
|
5
|
+
import type { TimePerToken } from '../summarizer/data';
|
|
6
|
+
import type { MergeableRecord } from '../../util/objects';
|
|
5
7
|
export declare const CommonSlicerMeasurements: readonly ["initialize R session", "retrieve AST from R code", "normalize R AST", "produce dataflow information", "close R session", "total"];
|
|
6
8
|
export type CommonSlicerMeasurements = typeof CommonSlicerMeasurements[number];
|
|
7
9
|
export declare const PerSliceMeasurements: readonly ["static slicing", "reconstruct code", "total"];
|
|
@@ -34,6 +36,17 @@ export interface SlicerStatsDataflow<T = number> {
|
|
|
34
36
|
numberOfEdges: T;
|
|
35
37
|
numberOfCalls: T;
|
|
36
38
|
numberOfFunctionDefinitions: T;
|
|
39
|
+
sizeOfObject: T;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Please note, that these measurement can be negative as there is no guarantee that the memory usage will increase
|
|
43
|
+
* due to, e.g., garbage collection.
|
|
44
|
+
*/
|
|
45
|
+
export interface BenchmarkMemoryMeasurement<T = number> extends MergeableRecord {
|
|
46
|
+
heap: T;
|
|
47
|
+
rss: T;
|
|
48
|
+
external: T;
|
|
49
|
+
buffs: T;
|
|
37
50
|
}
|
|
38
51
|
/**
|
|
39
52
|
* The statistics that are collected by the {@link BenchmarkSlicer} and used for benchmarking.
|
|
@@ -41,7 +54,12 @@ export interface SlicerStatsDataflow<T = number> {
|
|
|
41
54
|
export interface SlicerStats {
|
|
42
55
|
commonMeasurements: Map<CommonSlicerMeasurements, ElapsedTime>;
|
|
43
56
|
perSliceMeasurements: Map<SlicingCriteria, PerSliceStats>;
|
|
57
|
+
memory: Map<CommonSlicerMeasurements, BenchmarkMemoryMeasurement>;
|
|
44
58
|
request: RParseRequestFromFile | RParseRequestFromText;
|
|
45
59
|
input: SlicerStatsInput;
|
|
46
60
|
dataflow: SlicerStatsDataflow;
|
|
61
|
+
retrieveTimePerToken: TimePerToken<number>;
|
|
62
|
+
normalizeTimePerToken: TimePerToken<number>;
|
|
63
|
+
dataflowTimePerToken: TimePerToken<number>;
|
|
64
|
+
totalCommonTimePerToken: TimePerToken<number>;
|
|
47
65
|
}
|
|
@@ -31,12 +31,19 @@ export interface Reduction<T = number> {
|
|
|
31
31
|
numberOfNormalizedTokens: T;
|
|
32
32
|
numberOfDataflowNodes: T;
|
|
33
33
|
}
|
|
34
|
+
export interface TimePerToken<T = SummarizedMeasurement> {
|
|
35
|
+
raw: T;
|
|
36
|
+
normalized: T;
|
|
37
|
+
}
|
|
34
38
|
export interface SummarizedPerSliceStats {
|
|
35
39
|
/** number of total slicing calls */
|
|
36
40
|
numberOfSlices: number;
|
|
37
41
|
/** statistics on the used slicing criteria (number of ids within criteria etc.) */
|
|
38
42
|
sliceCriteriaSizes: SummarizedMeasurement;
|
|
39
43
|
measurements: Map<PerSliceMeasurements, SummarizedMeasurement>;
|
|
44
|
+
sliceTimePerToken: TimePerToken;
|
|
45
|
+
reconstructTimePerToken: TimePerToken;
|
|
46
|
+
totalPerSliceTimePerToken: TimePerToken;
|
|
40
47
|
reduction: Reduction<SummarizedMeasurement>;
|
|
41
48
|
/** reduction, but without taking into account comments and empty lines */
|
|
42
49
|
reductionNoFluff: Reduction<SummarizedMeasurement>;
|
|
@@ -51,6 +58,13 @@ export interface UltimateSlicerStats {
|
|
|
51
58
|
totalSlices: number;
|
|
52
59
|
commonMeasurements: Map<CommonSlicerMeasurements, SummarizedMeasurement>;
|
|
53
60
|
perSliceMeasurements: Map<PerSliceMeasurements, SummarizedMeasurement>;
|
|
61
|
+
retrieveTimePerToken: TimePerToken;
|
|
62
|
+
normalizeTimePerToken: TimePerToken;
|
|
63
|
+
dataflowTimePerToken: TimePerToken;
|
|
64
|
+
totalCommonTimePerToken: TimePerToken;
|
|
65
|
+
sliceTimePerToken: TimePerToken;
|
|
66
|
+
reconstructTimePerToken: TimePerToken;
|
|
67
|
+
totalPerSliceTimePerToken: TimePerToken;
|
|
54
68
|
/** sum */
|
|
55
69
|
failedToRepParse: number;
|
|
56
70
|
/** sum */
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
export declare function processRunMeasurement(line: Buffer, fileNum: number, lineNum: number,
|
|
3
|
-
export declare function
|
|
2
|
+
export declare function processRunMeasurement(line: Buffer, fileNum: number, lineNum: number, textOutputAppendPath: string, rawOutputPath: string): Promise<void>;
|
|
3
|
+
export declare function processSummarizedRunMeasurement(runNum: number, summarizedFiles: string[], appendPath: string): void;
|
|
@@ -3,27 +3,24 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
6
|
+
exports.processSummarizedRunMeasurement = exports.processRunMeasurement = void 0;
|
|
7
7
|
const fs_1 = __importDefault(require("fs"));
|
|
8
8
|
const process_1 = require("../second-phase/process");
|
|
9
9
|
const process_2 = require("./process");
|
|
10
10
|
const assert_1 = require("../../../util/assert");
|
|
11
11
|
const ansi_1 = require("../../../util/ansi");
|
|
12
12
|
const json_1 = require("../../../util/json");
|
|
13
|
-
const files_1 = require("../../../util/files");
|
|
14
13
|
const print_1 = require("../../stats/print");
|
|
15
|
-
async function processRunMeasurement(line, fileNum, lineNum,
|
|
14
|
+
async function processRunMeasurement(line, fileNum, lineNum, textOutputAppendPath, rawOutputPath) {
|
|
16
15
|
let got = JSON.parse(line.toString());
|
|
17
16
|
console.log(`[file ${fileNum}, line ${lineNum}] Summarize for ${got.filename}`);
|
|
18
17
|
// now we have to recover the maps and bigints :C
|
|
19
18
|
got = {
|
|
20
|
-
|
|
21
|
-
'file-id': got['file-id'],
|
|
22
|
-
'run-num': got['run-num'],
|
|
19
|
+
...got,
|
|
23
20
|
stats: {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
...got.stats,
|
|
22
|
+
memory: new Map(got.stats.memory
|
|
23
|
+
.map(([k, v]) => [k, v])),
|
|
27
24
|
commonMeasurements: new Map(got.stats.commonMeasurements
|
|
28
25
|
.map(([k, v]) => {
|
|
29
26
|
(0, assert_1.guard)(v.endsWith('n'), 'Expected a bigint');
|
|
@@ -38,28 +35,31 @@ async function processRunMeasurement(line, fileNum, lineNum, summarizedText, out
|
|
|
38
35
|
let atSliceNumber = 0;
|
|
39
36
|
const summarized = await (0, process_2.summarizeSlicerStats)(got.stats, (criterion, stats) => {
|
|
40
37
|
console.log(`${ansi_1.escape}1F${ansi_1.escape}1G${ansi_1.escape}2K [${++atSliceNumber}/${totalSlices}] Summarizing ${JSON.stringify(criterion)} (reconstructed has ${stats.reconstructedCode.code.length} characters)`);
|
|
38
|
+
if (stats.reconstructedCode.code.length < 50) {
|
|
39
|
+
console.log(`Reconstructed code: ${stats.reconstructedCode.code}`);
|
|
40
|
+
}
|
|
41
41
|
});
|
|
42
|
-
console.log(` -
|
|
43
|
-
fs_1.default.
|
|
42
|
+
console.log(` - Write raw summary to ${rawOutputPath}`);
|
|
43
|
+
fs_1.default.writeFileSync(rawOutputPath, `${JSON.stringify({
|
|
44
44
|
filename: got.filename,
|
|
45
45
|
'file-id': got['file-id'],
|
|
46
46
|
'run-num': got['run-num'],
|
|
47
47
|
summarize: summarized
|
|
48
48
|
}, json_1.jsonReplacer)}\n`);
|
|
49
|
-
console.log(` - Append textual summary to ${
|
|
50
|
-
fs_1.default.appendFileSync(
|
|
49
|
+
console.log(` - Append textual summary to ${textOutputAppendPath}`);
|
|
50
|
+
fs_1.default.appendFileSync(textOutputAppendPath, `${(0, print_1.stats2string)(summarized)}\n`);
|
|
51
51
|
}
|
|
52
52
|
exports.processRunMeasurement = processRunMeasurement;
|
|
53
|
-
function
|
|
54
|
-
console.log(`
|
|
53
|
+
function processSummarizedRunMeasurement(runNum, summarizedFiles, appendPath) {
|
|
54
|
+
console.log(`Summarizing all file statistics for run ${runNum}`);
|
|
55
55
|
const summaries = [];
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
56
|
+
for (const file of summarizedFiles) {
|
|
57
|
+
(0, process_1.processNextSummary)(fs_1.default.readFileSync(file), summaries);
|
|
58
|
+
}
|
|
59
|
+
fs_1.default.appendFileSync(appendPath, `${JSON.stringify((0, process_1.summarizeAllSummarizedStats)(summaries), json_1.jsonReplacer)}\n`);
|
|
60
|
+
console.log(`Appended summary of run ${runNum} to ${appendPath}`);
|
|
61
61
|
}
|
|
62
|
-
exports.
|
|
62
|
+
exports.processSummarizedRunMeasurement = processSummarizedRunMeasurement;
|
|
63
63
|
function mapPerSliceStats(k, v) {
|
|
64
64
|
return [k, {
|
|
65
65
|
reconstructedCode: v.reconstructedCode,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Reduction, SummarizedSlicerStats } from '../data';
|
|
1
|
+
import type { Reduction, SummarizedSlicerStats, TimePerToken } from '../data';
|
|
2
2
|
import type { SummarizedMeasurement } from '../../../util/summarizer';
|
|
3
3
|
import type { PerSliceStats, SlicerStats } from '../../stats/stats';
|
|
4
4
|
import type { SlicingCriteria } from '../../../slicing/criterion/parse';
|
|
@@ -9,3 +9,5 @@ import type { SlicingCriteria } from '../../../slicing/criterion/parse';
|
|
|
9
9
|
export declare function summarizeSlicerStats(stats: SlicerStats, report?: (criteria: SlicingCriteria, stats: PerSliceStats) => void): Promise<Readonly<SummarizedSlicerStats>>;
|
|
10
10
|
export declare function summarizeSummarizedMeasurement(data: SummarizedMeasurement[]): SummarizedMeasurement;
|
|
11
11
|
export declare function summarizeSummarizedReductions(reductions: Reduction<SummarizedMeasurement>[]): Reduction<SummarizedMeasurement>;
|
|
12
|
+
export declare function summarizeSummarizedTimePerToken(times: TimePerToken[]): TimePerToken;
|
|
13
|
+
export declare function summarizeTimePerToken(times: TimePerToken<number>[]): TimePerToken;
|
|
@@ -26,7 +26,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
26
26
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
27
|
};
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
exports.summarizeSummarizedReductions = exports.summarizeSummarizedMeasurement = exports.summarizeSlicerStats = void 0;
|
|
29
|
+
exports.summarizeTimePerToken = exports.summarizeSummarizedTimePerToken = exports.summarizeSummarizedReductions = exports.summarizeSummarizedMeasurement = exports.summarizeSlicerStats = void 0;
|
|
30
30
|
const tmp = __importStar(require("tmp"));
|
|
31
31
|
const fs_1 = __importDefault(require("fs"));
|
|
32
32
|
const defaultmap_1 = require("../../../util/defaultmap");
|
|
@@ -92,10 +92,12 @@ function calculateReductionForSlice(input, dataflow, perSlice, ignoreFluff) {
|
|
|
92
92
|
*/
|
|
93
93
|
async function summarizeSlicerStats(stats, report = () => {
|
|
94
94
|
}) {
|
|
95
|
-
const perSliceStats = stats.perSliceMeasurements;
|
|
96
95
|
const collect = new defaultmap_1.DefaultMap(() => []);
|
|
97
96
|
const sizeOfSliceCriteria = [];
|
|
98
97
|
const reParseShellSession = new shell_1.RShell();
|
|
98
|
+
const sliceTimes = [];
|
|
99
|
+
const reconstructTimes = [];
|
|
100
|
+
const totalTimes = [];
|
|
99
101
|
const reductions = [];
|
|
100
102
|
const reductionsNoFluff = [];
|
|
101
103
|
let failedOutputs = 0;
|
|
@@ -114,7 +116,7 @@ async function summarizeSlicerStats(stats, report = () => {
|
|
|
114
116
|
dataflowNodes: []
|
|
115
117
|
};
|
|
116
118
|
let timesHitThreshold = 0;
|
|
117
|
-
for (const [criteria, perSliceStat] of
|
|
119
|
+
for (const [criteria, perSliceStat] of stats.perSliceMeasurements) {
|
|
118
120
|
report(criteria, perSliceStat);
|
|
119
121
|
for (const measure of perSliceStat.measurements) {
|
|
120
122
|
collect.get(measure[0]).push(Number(measure[1]));
|
|
@@ -177,6 +179,20 @@ async function summarizeSlicerStats(stats, report = () => {
|
|
|
177
179
|
};
|
|
178
180
|
reductions.push(calculateReductionForSlice(stats.input, stats.dataflow, perSlice, false));
|
|
179
181
|
reductionsNoFluff.push(calculateReductionForSlice(stats.input, stats.dataflow, perSlice, true));
|
|
182
|
+
const sliceTime = Number(perSliceStat.measurements.get('static slicing'));
|
|
183
|
+
const reconstructTime = Number(perSliceStat.measurements.get('reconstruct code'));
|
|
184
|
+
sliceTimes.push({
|
|
185
|
+
raw: sliceTime / numberOfRTokens,
|
|
186
|
+
normalized: sliceTime / numberOfNormalizedTokens
|
|
187
|
+
});
|
|
188
|
+
reconstructTimes.push({
|
|
189
|
+
raw: reconstructTime / numberOfRTokens,
|
|
190
|
+
normalized: reconstructTime / numberOfNormalizedTokens
|
|
191
|
+
});
|
|
192
|
+
totalTimes.push({
|
|
193
|
+
raw: (sliceTime + reconstructTime) / numberOfRTokens,
|
|
194
|
+
normalized: (sliceTime + reconstructTime) / numberOfNormalizedTokens
|
|
195
|
+
});
|
|
180
196
|
}
|
|
181
197
|
catch (e) {
|
|
182
198
|
console.error(` ! Failed to re-parse the output of the slicer for ${JSON.stringify(criteria)}`); //, e
|
|
@@ -194,13 +210,16 @@ async function summarizeSlicerStats(stats, report = () => {
|
|
|
194
210
|
return {
|
|
195
211
|
...stats,
|
|
196
212
|
perSliceMeasurements: {
|
|
197
|
-
numberOfSlices:
|
|
213
|
+
numberOfSlices: stats.perSliceMeasurements.size,
|
|
198
214
|
sliceCriteriaSizes: (0, summarizer_1.summarizeMeasurement)(sizeOfSliceCriteria),
|
|
199
215
|
measurements: summarized,
|
|
200
216
|
failedToRepParse: failedOutputs,
|
|
201
217
|
timesHitThreshold,
|
|
202
218
|
reduction: summarizeReductions(reductions),
|
|
203
219
|
reductionNoFluff: summarizeReductions(reductionsNoFluff),
|
|
220
|
+
sliceTimePerToken: summarizeTimePerToken(sliceTimes),
|
|
221
|
+
reconstructTimePerToken: summarizeTimePerToken(reconstructTimes),
|
|
222
|
+
totalPerSliceTimePerToken: summarizeTimePerToken(totalTimes),
|
|
204
223
|
sliceSize: {
|
|
205
224
|
lines: (0, summarizer_1.summarizeMeasurement)(sliceSize.lines),
|
|
206
225
|
nonEmptyLines: (0, summarizer_1.summarizeMeasurement)(sliceSize.nonEmptyLines),
|
|
@@ -220,6 +239,7 @@ async function summarizeSlicerStats(stats, report = () => {
|
|
|
220
239
|
}
|
|
221
240
|
exports.summarizeSlicerStats = summarizeSlicerStats;
|
|
222
241
|
function summarizeSummarizedMeasurement(data) {
|
|
242
|
+
data = data.filter(assert_1.isNotUndefined);
|
|
223
243
|
const min = data.map(d => d.min).filter(assert_1.isNotUndefined).reduce((a, b) => Math.min(a, b), Infinity);
|
|
224
244
|
const max = data.map(d => d.max).filter(assert_1.isNotUndefined).reduce((a, b) => Math.max(a, b), -Infinity);
|
|
225
245
|
// calculate median of medians (don't just average the median!)
|
|
@@ -255,4 +275,18 @@ function summarizeReductions(reductions) {
|
|
|
255
275
|
numberOfDataflowNodes: (0, summarizer_1.summarizeMeasurement)(reductions.map(r => r.numberOfDataflowNodes).filter(assert_1.isNotUndefined))
|
|
256
276
|
};
|
|
257
277
|
}
|
|
278
|
+
function summarizeSummarizedTimePerToken(times) {
|
|
279
|
+
return {
|
|
280
|
+
raw: summarizeSummarizedMeasurement(times.map(t => t.raw)),
|
|
281
|
+
normalized: summarizeSummarizedMeasurement(times.map(t => t.normalized)),
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
exports.summarizeSummarizedTimePerToken = summarizeSummarizedTimePerToken;
|
|
285
|
+
function summarizeTimePerToken(times) {
|
|
286
|
+
return {
|
|
287
|
+
raw: (0, summarizer_1.summarizeMeasurement)(times.map(t => t.raw)),
|
|
288
|
+
normalized: (0, summarizer_1.summarizeMeasurement)(times.map(t => t.normalized)),
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
exports.summarizeTimePerToken = summarizeTimePerToken;
|
|
258
292
|
//# sourceMappingURL=process.js.map
|
|
@@ -47,6 +47,13 @@ function writeGraphOutput(ultimate, outputGraphPath) {
|
|
|
47
47
|
value: ultimate.reduction.numberOfNormalizedTokens.mean,
|
|
48
48
|
extra: `std: ${ultimate.reduction.numberOfNormalizedTokens.std}`
|
|
49
49
|
});
|
|
50
|
+
data.push({
|
|
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)}`
|
|
56
|
+
});
|
|
50
57
|
// write the output file
|
|
51
58
|
fs_1.default.writeFileSync(outputGraphPath, JSON.stringify(data, json_1.jsonReplacer));
|
|
52
59
|
}
|
|
@@ -9,6 +9,14 @@ const stats_1 = require("../../stats/stats");
|
|
|
9
9
|
function summarizeAllSummarizedStats(stats) {
|
|
10
10
|
const commonMeasurements = new defaultmap_1.DefaultMap(() => []);
|
|
11
11
|
const perSliceMeasurements = new defaultmap_1.DefaultMap(() => []);
|
|
12
|
+
const sliceTimesPerToken = [];
|
|
13
|
+
const reconstructTimesPerToken = [];
|
|
14
|
+
const totalPerSliceTimesPerToken = [];
|
|
15
|
+
const retrieveTimesPerToken = [];
|
|
16
|
+
const normalizeTimesPerToken = [];
|
|
17
|
+
const dataflowTimesPerToken = [];
|
|
18
|
+
const totalCommonTimesPerToken = [];
|
|
19
|
+
const memory = new defaultmap_1.DefaultMap(() => []);
|
|
12
20
|
const reductions = [];
|
|
13
21
|
const reductionsNoFluff = [];
|
|
14
22
|
const inputs = [];
|
|
@@ -23,6 +31,16 @@ function summarizeAllSummarizedStats(stats) {
|
|
|
23
31
|
for (const [k, v] of stat.perSliceMeasurements.measurements) {
|
|
24
32
|
perSliceMeasurements.get(k).push(v);
|
|
25
33
|
}
|
|
34
|
+
sliceTimesPerToken.push(stat.perSliceMeasurements.sliceTimePerToken);
|
|
35
|
+
reconstructTimesPerToken.push(stat.perSliceMeasurements.reconstructTimePerToken);
|
|
36
|
+
totalPerSliceTimesPerToken.push(stat.perSliceMeasurements.totalPerSliceTimePerToken);
|
|
37
|
+
retrieveTimesPerToken.push(stat.retrieveTimePerToken);
|
|
38
|
+
normalizeTimesPerToken.push(stat.normalizeTimePerToken);
|
|
39
|
+
dataflowTimesPerToken.push(stat.dataflowTimePerToken);
|
|
40
|
+
totalCommonTimesPerToken.push(stat.totalCommonTimePerToken);
|
|
41
|
+
for (const [k, v] of stat.memory) {
|
|
42
|
+
memory.get(k).push(v);
|
|
43
|
+
}
|
|
26
44
|
reductions.push(stat.perSliceMeasurements.reduction);
|
|
27
45
|
reductionsNoFluff.push(stat.perSliceMeasurements.reductionNoFluff);
|
|
28
46
|
inputs.push(stat.input);
|
|
@@ -36,6 +54,13 @@ function summarizeAllSummarizedStats(stats) {
|
|
|
36
54
|
totalSlices: totalSlices,
|
|
37
55
|
commonMeasurements: new Map([...commonMeasurements.entries()].map(([k, v]) => [k, (0, summarizer_1.summarizeMeasurement)(v)])),
|
|
38
56
|
perSliceMeasurements: new Map([...perSliceMeasurements.entries()].map(([k, v]) => [k, (0, process_1.summarizeSummarizedMeasurement)(v)])),
|
|
57
|
+
sliceTimePerToken: (0, process_1.summarizeSummarizedTimePerToken)(sliceTimesPerToken),
|
|
58
|
+
reconstructTimePerToken: (0, process_1.summarizeSummarizedTimePerToken)(reconstructTimesPerToken),
|
|
59
|
+
totalPerSliceTimePerToken: (0, process_1.summarizeSummarizedTimePerToken)(totalPerSliceTimesPerToken),
|
|
60
|
+
retrieveTimePerToken: (0, process_1.summarizeTimePerToken)(retrieveTimesPerToken),
|
|
61
|
+
normalizeTimePerToken: (0, process_1.summarizeTimePerToken)(normalizeTimesPerToken),
|
|
62
|
+
dataflowTimePerToken: (0, process_1.summarizeTimePerToken)(dataflowTimesPerToken),
|
|
63
|
+
totalCommonTimePerToken: (0, process_1.summarizeTimePerToken)(totalCommonTimesPerToken),
|
|
39
64
|
failedToRepParse,
|
|
40
65
|
timesHitThreshold,
|
|
41
66
|
reduction: (0, process_1.summarizeSummarizedReductions)(reductions),
|
|
@@ -56,7 +81,8 @@ function summarizeAllSummarizedStats(stats) {
|
|
|
56
81
|
numberOfNodes: (0, summarizer_1.summarizeMeasurement)(dataflows.map(d => d.numberOfNodes)),
|
|
57
82
|
numberOfFunctionDefinitions: (0, summarizer_1.summarizeMeasurement)(dataflows.map(d => d.numberOfFunctionDefinitions)),
|
|
58
83
|
numberOfCalls: (0, summarizer_1.summarizeMeasurement)(dataflows.map(d => d.numberOfCalls)),
|
|
59
|
-
numberOfEdges: (0, summarizer_1.summarizeMeasurement)(dataflows.map(d => d.numberOfEdges))
|
|
84
|
+
numberOfEdges: (0, summarizer_1.summarizeMeasurement)(dataflows.map(d => d.numberOfEdges)),
|
|
85
|
+
sizeOfObject: (0, summarizer_1.summarizeMeasurement)(dataflows.map(d => d.sizeOfObject))
|
|
60
86
|
}
|
|
61
87
|
};
|
|
62
88
|
}
|
|
@@ -71,6 +97,13 @@ function summarizeAllUltimateStats(stats) {
|
|
|
71
97
|
// average out / summarize other measurements
|
|
72
98
|
commonMeasurements: new Map(stats_1.CommonSlicerMeasurements.map(m => [m, (0, process_1.summarizeSummarizedMeasurement)(stats.map(s => s.commonMeasurements.get(m)))])),
|
|
73
99
|
perSliceMeasurements: new Map(stats_1.PerSliceMeasurements.map(m => [m, (0, process_1.summarizeSummarizedMeasurement)(stats.map(s => s.perSliceMeasurements.get(m)))])),
|
|
100
|
+
sliceTimePerToken: (0, process_1.summarizeSummarizedTimePerToken)(stats.map(s => s.sliceTimePerToken)),
|
|
101
|
+
reconstructTimePerToken: (0, process_1.summarizeSummarizedTimePerToken)(stats.map(s => s.reconstructTimePerToken)),
|
|
102
|
+
totalPerSliceTimePerToken: (0, process_1.summarizeSummarizedTimePerToken)(stats.map(s => s.totalPerSliceTimePerToken)),
|
|
103
|
+
retrieveTimePerToken: (0, process_1.summarizeSummarizedTimePerToken)(stats.map(s => s.retrieveTimePerToken)),
|
|
104
|
+
normalizeTimePerToken: (0, process_1.summarizeSummarizedTimePerToken)(stats.map(s => s.normalizeTimePerToken)),
|
|
105
|
+
dataflowTimePerToken: (0, process_1.summarizeSummarizedTimePerToken)(stats.map(s => s.dataflowTimePerToken)),
|
|
106
|
+
totalCommonTimePerToken: (0, process_1.summarizeSummarizedTimePerToken)(stats.map(s => s.totalCommonTimePerToken)),
|
|
74
107
|
reduction: (0, process_1.summarizeSummarizedReductions)(stats.map(s => s.reduction)),
|
|
75
108
|
reductionNoFluff: (0, process_1.summarizeSummarizedReductions)(stats.map(s => s.reductionNoFluff)),
|
|
76
109
|
input: {
|
|
@@ -89,7 +122,8 @@ function summarizeAllUltimateStats(stats) {
|
|
|
89
122
|
numberOfNodes: (0, process_1.summarizeSummarizedMeasurement)(stats.map(s => s.dataflow.numberOfNodes)),
|
|
90
123
|
numberOfFunctionDefinitions: (0, process_1.summarizeSummarizedMeasurement)(stats.map(s => s.dataflow.numberOfFunctionDefinitions)),
|
|
91
124
|
numberOfCalls: (0, process_1.summarizeSummarizedMeasurement)(stats.map(s => s.dataflow.numberOfCalls)),
|
|
92
|
-
numberOfEdges: (0, process_1.summarizeSummarizedMeasurement)(stats.map(s => s.dataflow.numberOfEdges))
|
|
125
|
+
numberOfEdges: (0, process_1.summarizeSummarizedMeasurement)(stats.map(s => s.dataflow.numberOfEdges)),
|
|
126
|
+
sizeOfObject: (0, process_1.summarizeSummarizedMeasurement)(stats.map(s => s.dataflow.sizeOfObject))
|
|
93
127
|
}
|
|
94
128
|
};
|
|
95
129
|
}
|
|
@@ -98,23 +132,19 @@ function processNextSummary(line, allSummarized) {
|
|
|
98
132
|
let got = JSON.parse(line.toString());
|
|
99
133
|
got = {
|
|
100
134
|
summarize: {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
135
|
+
...got.summarize,
|
|
136
|
+
// restore maps
|
|
137
|
+
memory: new Map(got.summarize.memory
|
|
138
|
+
.map(([k, v]) => [k, v])),
|
|
104
139
|
commonMeasurements: new Map(got.summarize.commonMeasurements
|
|
105
140
|
.map(([k, v]) => {
|
|
106
141
|
(0, assert_1.guard)(v.endsWith('n'), 'Expected a bigint');
|
|
107
142
|
return [k, BigInt(v.slice(0, -1))];
|
|
108
143
|
})),
|
|
109
144
|
perSliceMeasurements: {
|
|
110
|
-
|
|
111
|
-
|
|
145
|
+
...got.summarize.perSliceMeasurements,
|
|
146
|
+
// restore maps
|
|
112
147
|
measurements: new Map(got.summarize.perSliceMeasurements.measurements),
|
|
113
|
-
reduction: got.summarize.perSliceMeasurements.reduction,
|
|
114
|
-
reductionNoFluff: got.summarize.perSliceMeasurements.reductionNoFluff,
|
|
115
|
-
timesHitThreshold: got.summarize.perSliceMeasurements.timesHitThreshold,
|
|
116
|
-
failedToRepParse: got.summarize.perSliceMeasurements.failedToRepParse,
|
|
117
|
-
sliceSize: got.summarize.perSliceMeasurements.sliceSize
|
|
118
148
|
}
|
|
119
149
|
}
|
|
120
150
|
};
|
|
@@ -124,20 +154,12 @@ exports.processNextSummary = processNextSummary;
|
|
|
124
154
|
function processNextUltimateSummary(line, allSummarized) {
|
|
125
155
|
let got = JSON.parse(line.toString());
|
|
126
156
|
got = {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
perSliceMeasurements: new Map(got.summarize.perSliceMeasurements),
|
|
132
|
-
failedToRepParse: got.summarize.failedToRepParse,
|
|
133
|
-
timesHitThreshold: got.summarize.timesHitThreshold,
|
|
134
|
-
reduction: got.summarize.reduction,
|
|
135
|
-
reductionNoFluff: got.summarize.reductionNoFluff,
|
|
136
|
-
input: got.summarize.input,
|
|
137
|
-
dataflow: got.summarize.dataflow,
|
|
138
|
-
}
|
|
157
|
+
...got,
|
|
158
|
+
// restore maps
|
|
159
|
+
commonMeasurements: new Map(got.commonMeasurements),
|
|
160
|
+
perSliceMeasurements: new Map(got.perSliceMeasurements),
|
|
139
161
|
};
|
|
140
|
-
allSummarized.push(got
|
|
162
|
+
allSummarized.push(got);
|
|
141
163
|
}
|
|
142
164
|
exports.processNextUltimateSummary = processNextUltimateSummary;
|
|
143
165
|
//# sourceMappingURL=process.js.map
|
|
@@ -13,31 +13,41 @@ const summarizer_1 = require("../../util/summarizer");
|
|
|
13
13
|
const files_1 = require("../../util/files");
|
|
14
14
|
const json_1 = require("../../util/json");
|
|
15
15
|
const print_1 = require("../stats/print");
|
|
16
|
+
const defaultmap_1 = require("../../util/defaultmap");
|
|
16
17
|
class BenchmarkSummarizer extends summarizer_1.Summarizer {
|
|
17
18
|
constructor(config) {
|
|
18
19
|
super(config);
|
|
19
20
|
}
|
|
20
21
|
async preparationPhase() {
|
|
21
|
-
this.removeIfExists(
|
|
22
|
+
this.removeIfExists(this.summaryFile());
|
|
22
23
|
this.removeIfExists(this.config.intermediateOutputPath);
|
|
23
24
|
fs_1.default.mkdirSync(this.config.intermediateOutputPath);
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const
|
|
25
|
+
const filesToSummarize = fs_1.default.readdirSync(this.config.inputPath);
|
|
26
|
+
const outputPathsPerRun = new defaultmap_1.DefaultMap(() => []);
|
|
27
|
+
for (let i = 0; i < filesToSummarize.length; i++) {
|
|
28
|
+
const fileInputPath = path_1.default.join(this.config.inputPath, filesToSummarize[i]);
|
|
29
|
+
const outputDir = path_1.default.join(this.config.intermediateOutputPath, path_1.default.parse(filesToSummarize[i]).name);
|
|
30
|
+
fs_1.default.mkdirSync(outputDir);
|
|
31
|
+
const textOutputPath = path_1.default.join(outputDir, 'summary.log');
|
|
28
32
|
// generate measurements for each run
|
|
29
|
-
await (0, files_1.readLineByLine)(
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
await (0, files_1.readLineByLine)(fileInputPath, (line, lineNumber) => {
|
|
34
|
+
const runOutputPath = path_1.default.join(outputDir, `run-${lineNumber}.json`);
|
|
35
|
+
outputPathsPerRun.get(lineNumber).push(runOutputPath);
|
|
36
|
+
return (0, input_1.processRunMeasurement)(line, i, lineNumber, textOutputPath, runOutputPath);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
// generate combined measurements for each file per run
|
|
40
|
+
for (const [run, paths] of outputPathsPerRun.entries()) {
|
|
41
|
+
(0, input_1.processSummarizedRunMeasurement)(run, paths, this.summaryFile());
|
|
32
42
|
}
|
|
33
43
|
this.log('Done summarizing');
|
|
34
44
|
}
|
|
35
45
|
// eslint-disable-next-line @typescript-eslint/require-await -- just to obey the structure
|
|
36
46
|
async summarizePhase() {
|
|
37
|
-
this.log(`Summarizing all summaries from ${this.
|
|
47
|
+
this.log(`Summarizing all summaries from ${this.summaryFile()}...`);
|
|
38
48
|
this.removeIfExists(this.config.outputPath);
|
|
39
49
|
const summaries = [];
|
|
40
|
-
(0, files_1.readLineByLineSync)(
|
|
50
|
+
(0, files_1.readLineByLineSync)(this.summaryFile(), (l) => (0, process_1.processNextUltimateSummary)(l, summaries));
|
|
41
51
|
const ultimate = (0, process_1.summarizeAllUltimateStats)(summaries);
|
|
42
52
|
this.log(`Writing ultimate summary to ${this.config.outputPath}`);
|
|
43
53
|
fs_1.default.writeFileSync(this.config.outputPath, JSON.stringify(ultimate, json_1.jsonReplacer));
|
|
@@ -53,6 +63,9 @@ class BenchmarkSummarizer extends summarizer_1.Summarizer {
|
|
|
53
63
|
fs_1.default.rmSync(path, { recursive: true });
|
|
54
64
|
}
|
|
55
65
|
}
|
|
66
|
+
summaryFile() {
|
|
67
|
+
return `${this.config.intermediateOutputPath}.json`;
|
|
68
|
+
}
|
|
56
69
|
}
|
|
57
70
|
exports.BenchmarkSummarizer = BenchmarkSummarizer;
|
|
58
71
|
//# sourceMappingURL=summarizer.js.map
|
|
@@ -9,6 +9,7 @@ import type { DataflowGraph } from '../graph/graph';
|
|
|
9
9
|
import type { ControlDependency } from '../info';
|
|
10
10
|
export declare function makeReferenceMaybe(ref: IdentifierReference, graph: DataflowGraph, environments: REnvironmentInformation, includeDefs: boolean, defaultCd?: ControlDependency | undefined): IdentifierReference;
|
|
11
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;
|
|
@@ -17,7 +18,7 @@ export interface IEnvironment {
|
|
|
17
18
|
/**
|
|
18
19
|
* Maps to exactly one definition of an identifier if the source is known, otherwise to a list of all possible definitions
|
|
19
20
|
*/
|
|
20
|
-
memory:
|
|
21
|
+
memory: EnvironmentMemory;
|
|
21
22
|
}
|
|
22
23
|
export declare class Environment implements IEnvironment {
|
|
23
24
|
readonly id: string;
|
package/dataflow/graph/edge.d.ts
CHANGED
|
@@ -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 = "
|
|
44
|
-
DefinedByOnCall = "
|
|
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"
|
package/dataflow/graph/edge.js
CHANGED
|
@@ -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 */, "
|
|
10
|
-
[32 /* EdgeType.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 */]
|
package/dataflow/graph/graph.js
CHANGED
|
@@ -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
|
}
|
|
@@ -159,7 +160,6 @@ class DataflowGraph {
|
|
|
159
160
|
const environment = vertex.environment === undefined ? fallback : (0, clone_1.cloneEnvironmentInformation)(vertex.environment);
|
|
160
161
|
this.vertexInformation.set(vertex.id, {
|
|
161
162
|
...vertex,
|
|
162
|
-
when: vertex.controlDependencies ?? 'always',
|
|
163
163
|
environment
|
|
164
164
|
});
|
|
165
165
|
if (asRoot) {
|
|
@@ -171,11 +171,12 @@ class DataflowGraph {
|
|
|
171
171
|
* Will insert a new edge into the graph,
|
|
172
172
|
* if the direction of the edge is of no importance (`same-read-read` or `same-def-def`), source
|
|
173
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.
|
|
174
175
|
*/
|
|
175
176
|
addEdge(from, to, edgeInfo) {
|
|
176
177
|
const { fromId, toId } = extractEdgeIds(from, to);
|
|
177
178
|
const { type, ...rest } = edgeInfo;
|
|
178
|
-
if (fromId === toId) {
|
|
179
|
+
if (fromId === toId || toId === built_in_1.BuiltIn) {
|
|
179
180
|
return this;
|
|
180
181
|
}
|
|
181
182
|
/* we now that we pass all required arguments */
|
|
@@ -11,7 +11,6 @@ function processValue(value, data) {
|
|
|
11
11
|
graph: new graph_1.DataflowGraph(data.completeAst.idMap).addVertex({
|
|
12
12
|
tag: "value" /* VertexType.Value */,
|
|
13
13
|
id: value.info.id,
|
|
14
|
-
value: value.lexeme,
|
|
15
14
|
controlDependencies: data.controlDependencies
|
|
16
15
|
}),
|
|
17
16
|
exitPoints: [{ nodeId: value.info.id, type: 0 /* ExitPointType.Default */, controlDependencies: data.controlDependencies }],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eagleoutice/flowr",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.4",
|
|
4
4
|
"description": "Static Dataflow Analyzer and Program Slicer for the R Programming Language",
|
|
5
5
|
"types": "dist/src/index.d.ts",
|
|
6
6
|
"repository": {
|
|
@@ -228,6 +228,7 @@
|
|
|
228
228
|
"n-readlines": "^1.0.1",
|
|
229
229
|
"n3": "^1.17.2",
|
|
230
230
|
"object-hash": "^3.0.0",
|
|
231
|
+
"object-sizeof": "^2.6.4",
|
|
231
232
|
"rotating-file-stream": "^3.1.1",
|
|
232
233
|
"semver": "^7.5.4",
|
|
233
234
|
"tar": "^7.1.0",
|
package/util/mermaid/dfg.d.ts
CHANGED
|
@@ -7,7 +7,6 @@ type Mark = MarkVertex | MarkEdge;
|
|
|
7
7
|
interface MermaidGraph {
|
|
8
8
|
nodeLines: string[];
|
|
9
9
|
edgeLines: string[];
|
|
10
|
-
hasBuiltIn: boolean;
|
|
11
10
|
includeEnvironments: boolean;
|
|
12
11
|
mark: ReadonlySet<Mark> | undefined;
|
|
13
12
|
/** in the form of from-\>to because I am lazy, see {@link encodeEdge} */
|
package/util/mermaid/dfg.js
CHANGED
|
@@ -7,7 +7,6 @@ const graph_1 = require("../../dataflow/graph/graph");
|
|
|
7
7
|
const r_function_call_1 = require("../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
|
|
8
8
|
const edge_1 = require("../../dataflow/graph/edge");
|
|
9
9
|
const environment_1 = require("../../dataflow/environments/environment");
|
|
10
|
-
const built_in_1 = require("../../dataflow/environments/built-in");
|
|
11
10
|
function formatRange(range) {
|
|
12
11
|
if (range === undefined) {
|
|
13
12
|
return '??-??';
|
|
@@ -151,9 +150,6 @@ function vertexToMermaid(info, mermaid, id, idPrefix, mark) {
|
|
|
151
150
|
if (edgeTypes.has('CD-True')) {
|
|
152
151
|
mermaid.edgeLines.push(` linkStyle ${mermaid.presentEdges.size - 1} stroke:gray,color:gray;`);
|
|
153
152
|
}
|
|
154
|
-
if (target === built_in_1.BuiltIn) {
|
|
155
|
-
mermaid.hasBuiltIn = true;
|
|
156
|
-
}
|
|
157
153
|
}
|
|
158
154
|
}
|
|
159
155
|
if (info.tag === 'function-definition') {
|
|
@@ -162,15 +158,12 @@ function vertexToMermaid(info, mermaid, id, idPrefix, mark) {
|
|
|
162
158
|
}
|
|
163
159
|
// make the passing of root ids more performant again
|
|
164
160
|
function graphToMermaidGraph(rootIds, { graph, prefix = 'flowchart TD', idPrefix = '', includeEnvironments = true, mark, rootGraph, presentEdges = new Set() }) {
|
|
165
|
-
const mermaid = { nodeLines: prefix === null ? [] : [prefix], edgeLines: [], presentEdges,
|
|
161
|
+
const mermaid = { nodeLines: prefix === null ? [] : [prefix], edgeLines: [], presentEdges, mark, rootGraph: rootGraph ?? graph, includeEnvironments };
|
|
166
162
|
for (const [id, info] of graph.vertices(true)) {
|
|
167
163
|
if (rootIds.has(id)) {
|
|
168
164
|
vertexToMermaid(info, mermaid, id, idPrefix, mark);
|
|
169
165
|
}
|
|
170
166
|
}
|
|
171
|
-
if (mermaid.hasBuiltIn) {
|
|
172
|
-
mermaid.nodeLines.push(` ${idPrefix}${built_in_1.BuiltIn}["Built-in"]`);
|
|
173
|
-
}
|
|
174
167
|
return mermaid;
|
|
175
168
|
}
|
|
176
169
|
function graphToMermaid(config) {
|
package/util/version.js
CHANGED
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.flowrVersion = void 0;
|
|
4
4
|
const semver_1 = require("semver");
|
|
5
5
|
// this is automatically replaced with the current version by release-it
|
|
6
|
-
const version = '2.0.
|
|
6
|
+
const version = '2.0.4';
|
|
7
7
|
function flowrVersion() {
|
|
8
8
|
return new semver_1.SemVer(version);
|
|
9
9
|
}
|