@eagleoutice/flowr 2.0.14 → 2.0.15
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 +2 -1
- package/benchmark/slicer.js +3 -2
- package/cli/common/options.js +1 -0
- package/cli/flowr.js +1 -2
- package/cli/repl/commands/commands.js +1 -1
- package/cli/repl/core.js +1 -1
- package/cli/repl/server/connection.js +6 -1
- package/cli/repl/server/messages/slice.d.ts +4 -0
- package/cli/repl/server/messages/slice.js +1 -1
- package/cli/slicer-app.d.ts +1 -0
- package/cli/slicer-app.js +5 -1
- package/core/steps/all/static-slicing/10-reconstruct.d.ts +1 -1
- package/core/steps/pipeline/default-pipelines.d.ts +1 -1
- package/core/steps/pipeline/default-pipelines.js +2 -2
- package/dataflow/graph/diff.d.ts +4 -3
- package/dataflow/graph/diff.js +58 -26
- package/dataflow/internal/linker.d.ts +6 -1
- package/dataflow/internal/linker.js +30 -21
- package/dataflow/internal/process/functions/process-argument.js +28 -0
- package/package.json +1 -1
- package/r-bridge/lang-4.x/ast/parser/json/format.js +5 -5
- package/reconstruct/auto-select/auto-select-defaults.d.ts +21 -0
- package/reconstruct/auto-select/auto-select-defaults.js +23 -0
- package/reconstruct/auto-select/magic-comments.d.ts +17 -0
- package/reconstruct/auto-select/magic-comments.js +86 -0
- package/reconstruct/reconstruct.d.ts +3 -7
- package/reconstruct/reconstruct.js +15 -22
- package/slicing/static/static-slicer.d.ts +6 -1
- package/slicing/static/static-slicer.js +10 -5
- package/util/diff.d.ts +12 -0
- package/util/diff.js +6 -3
- package/util/mermaid/dfg.d.ts +3 -0
- package/util/mermaid/dfg.js +3 -0
- package/util/version.js +1 -1
package/benchmark/slicer.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ 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
|
+
import type { AutoSelectPredicate } from '../reconstruct/auto-select/auto-select-defaults';
|
|
14
15
|
/**
|
|
15
16
|
* The logger to be used for benchmarking as a global object.
|
|
16
17
|
*/
|
|
@@ -68,7 +69,7 @@ export declare class BenchmarkSlicer {
|
|
|
68
69
|
* Initialize the slicer on the given request.
|
|
69
70
|
* Can only be called once for each instance.
|
|
70
71
|
*/
|
|
71
|
-
init(request: RParseRequestFromFile | RParseRequestFromText): Promise<void>;
|
|
72
|
+
init(request: RParseRequestFromFile | RParseRequestFromText, autoSelectIf?: AutoSelectPredicate): Promise<void>;
|
|
72
73
|
private calculateStatsAfterInit;
|
|
73
74
|
/**
|
|
74
75
|
* Slice for the given {@link SlicingCriteria}.
|
package/benchmark/slicer.js
CHANGED
|
@@ -55,12 +55,13 @@ class BenchmarkSlicer {
|
|
|
55
55
|
* Initialize the slicer on the given request.
|
|
56
56
|
* Can only be called once for each instance.
|
|
57
57
|
*/
|
|
58
|
-
async init(request) {
|
|
58
|
+
async init(request, autoSelectIf) {
|
|
59
59
|
(0, assert_1.guard)(this.stats === undefined, 'cannot initialize the slicer twice');
|
|
60
60
|
this.pipeline = new pipeline_executor_1.PipelineExecutor(default_pipelines_1.DEFAULT_SLICING_PIPELINE, {
|
|
61
61
|
shell: this.shell,
|
|
62
62
|
request: { ...request },
|
|
63
|
-
criterion: []
|
|
63
|
+
criterion: [],
|
|
64
|
+
autoSelectIf
|
|
64
65
|
});
|
|
65
66
|
this.loadedXml = await this.measureCommonStep('parse', 'retrieve AST from R code');
|
|
66
67
|
this.normalizedAst = await this.measureCommonStep('normalize', 'normalize R AST');
|
package/cli/common/options.js
CHANGED
|
@@ -46,6 +46,7 @@ exports.slicerOptions = [
|
|
|
46
46
|
{ name: 'criterion', alias: 'c', type: String, description: '(Required) Slicing criterion either in the form {underline line:col} or {underline line@variable}, multiple can be separated by \'{bold ;}\'. If you do not want to slice but only process the file, pass an empty string.', multiple: false },
|
|
47
47
|
{ name: 'stats', alias: 's', type: Boolean, description: 'Print stats and write them to {italic <output>.stats} (runtimes etc.)', multiple: false },
|
|
48
48
|
{ name: 'output', alias: 'o', type: String, description: 'File to write all the generated quads to (defaults to the commandline)', typeLabel: '{underline file}' },
|
|
49
|
+
{ name: 'no-magic-comments', alias: 'm', type: Boolean, description: 'Disable the effects of magic comments which force lines to be included.' },
|
|
49
50
|
{ name: 'api', type: Boolean, description: 'Instead of human-readable output, dump a lot of json with the results of all intermediate steps.' },
|
|
50
51
|
];
|
|
51
52
|
const featureNameList = [...feature_1.allFeatureNames].map(s => `"${s}"`).join(', ');
|
package/cli/flowr.js
CHANGED
|
@@ -87,10 +87,9 @@ function retrieveShell() {
|
|
|
87
87
|
}
|
|
88
88
|
async function mainRepl() {
|
|
89
89
|
if (options.script) {
|
|
90
|
-
|
|
90
|
+
const target = scripts_info_1.scripts[options.script].target;
|
|
91
91
|
(0, assert_1.guard)(target !== undefined, `Unknown script ${options.script}, pick one of ${getScriptsText()}.`);
|
|
92
92
|
console.log(`Running script '${ansi_1.formatter.format(options.script, { style: 1 /* FontStyles.Bold */ })}'`);
|
|
93
|
-
target = `cli/${target}`;
|
|
94
93
|
log_1.log.debug(`Script maps to "${target}"`);
|
|
95
94
|
await (0, execute_1.waitOnScript)(`${__dirname}/${target}`, process.argv.slice(3), undefined, true);
|
|
96
95
|
process.exit(0);
|
|
@@ -30,7 +30,7 @@ exports.helpCommand = {
|
|
|
30
30
|
fn: output => {
|
|
31
31
|
initCommandMapping();
|
|
32
32
|
output.stdout(`
|
|
33
|
-
|
|
33
|
+
If enabled, you can just enter R expressions which get evaluated right away:
|
|
34
34
|
${prompt_1.rawPrompt} ${(0, ansi_1.bold)('1 + 1', output.formatter)}
|
|
35
35
|
${(0, ansi_1.italic)('[1] 2', output.formatter)}
|
|
36
36
|
|
package/cli/repl/core.js
CHANGED
|
@@ -115,7 +115,7 @@ async function replProcessStatement(output, statement, shell, allowRSessionAcces
|
|
|
115
115
|
await (0, execute_1.executeRShellCommand)(output, shell, statement);
|
|
116
116
|
}
|
|
117
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 */ })}
|
|
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
119
|
}
|
|
120
120
|
}
|
|
121
121
|
/**
|
|
@@ -46,6 +46,8 @@ const default_pipelines_1 = require("../../../core/steps/pipeline/default-pipeli
|
|
|
46
46
|
const graph_1 = require("../../../dataflow/graph/graph");
|
|
47
47
|
const tmp = __importStar(require("tmp"));
|
|
48
48
|
const fs_1 = __importDefault(require("fs"));
|
|
49
|
+
const auto_select_defaults_1 = require("../../../reconstruct/auto-select/auto-select-defaults");
|
|
50
|
+
const magic_comments_1 = require("../../../reconstruct/auto-select/magic-comments");
|
|
49
51
|
/**
|
|
50
52
|
* Each connection handles a single client, answering to its requests.
|
|
51
53
|
* There is no need to construct this class manually, {@link FlowRServer} will do it for you.
|
|
@@ -211,7 +213,10 @@ class FlowRServerConnection {
|
|
|
211
213
|
});
|
|
212
214
|
return;
|
|
213
215
|
}
|
|
214
|
-
fileInformation.pipeline.updateRequest({
|
|
216
|
+
fileInformation.pipeline.updateRequest({
|
|
217
|
+
criterion: request.criterion,
|
|
218
|
+
autoSelectIf: request.noMagicComments ? auto_select_defaults_1.autoSelectLibrary : (0, magic_comments_1.makeMagicCommentHandler)(auto_select_defaults_1.autoSelectLibrary)
|
|
219
|
+
});
|
|
215
220
|
void fileInformation.pipeline.allRemainingSteps(true).then(results => {
|
|
216
221
|
(0, send_1.sendMessage)(this.socket, {
|
|
217
222
|
type: 'response-slice',
|
|
@@ -13,6 +13,10 @@ export interface SliceRequestMessage extends IdMessageBase {
|
|
|
13
13
|
filetoken: string;
|
|
14
14
|
/** The slicing criteria to use */
|
|
15
15
|
criterion: SlicingCriteria;
|
|
16
|
+
/**
|
|
17
|
+
* Should the magic comments (force-including lines within the slice) be ignord?
|
|
18
|
+
*/
|
|
19
|
+
noMagicComments?: boolean;
|
|
16
20
|
}
|
|
17
21
|
export declare const requestSliceMessage: MessageDefinition<SliceRequestMessage>;
|
|
18
22
|
/**
|
|
@@ -31,7 +31,7 @@ exports.requestSliceMessage = {
|
|
|
31
31
|
type: Joi.string().valid('request-slice').required(),
|
|
32
32
|
id: Joi.string().optional(),
|
|
33
33
|
filetoken: Joi.string().required(),
|
|
34
|
-
criterion: Joi.array().items(Joi.string()
|
|
34
|
+
criterion: Joi.array().items(Joi.string()).min(0).required()
|
|
35
35
|
})
|
|
36
36
|
};
|
|
37
37
|
//# sourceMappingURL=slice.js.map
|
package/cli/slicer-app.d.ts
CHANGED
package/cli/slicer-app.js
CHANGED
|
@@ -12,6 +12,8 @@ const json_1 = require("../util/json");
|
|
|
12
12
|
const script_1 = require("./common/script");
|
|
13
13
|
const slicer_1 = require("../benchmark/slicer");
|
|
14
14
|
const print_1 = require("../benchmark/stats/print");
|
|
15
|
+
const auto_select_defaults_1 = require("../reconstruct/auto-select/auto-select-defaults");
|
|
16
|
+
const magic_comments_1 = require("../reconstruct/auto-select/magic-comments");
|
|
15
17
|
const options = (0, script_1.processCommandLineArgs)('slicer', ['input', 'criterion'], {
|
|
16
18
|
subtitle: 'Slice R code based on a given slicing criterion',
|
|
17
19
|
examples: [
|
|
@@ -26,7 +28,9 @@ async function getSlice() {
|
|
|
26
28
|
const slicer = new slicer_1.BenchmarkSlicer();
|
|
27
29
|
(0, assert_1.guard)(options.input !== undefined, 'input must be given');
|
|
28
30
|
(0, assert_1.guard)(options.criterion !== undefined, 'a slicing criterion must be given');
|
|
29
|
-
await slicer.init(options['input-is-text']
|
|
31
|
+
await slicer.init(options['input-is-text']
|
|
32
|
+
? { request: 'text', content: options.input }
|
|
33
|
+
: { request: 'file', content: options.input }, options['no-magic-comments'] ? auto_select_defaults_1.autoSelectLibrary : (0, magic_comments_1.makeMagicCommentHandler)(auto_select_defaults_1.autoSelectLibrary));
|
|
30
34
|
let mappedSlices = [];
|
|
31
35
|
let reconstruct = undefined;
|
|
32
36
|
const doSlicing = options.criterion.trim() !== '';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { internalPrinter } from '../../../print/print';
|
|
2
2
|
import { PipelineStepStage } from '../../pipeline-step';
|
|
3
3
|
import type { SliceResult } from '../../../../slicing/static/slicer-types';
|
|
4
|
-
import type { AutoSelectPredicate } from '../../../../reconstruct/reconstruct';
|
|
5
4
|
import type { NormalizedAst } from '../../../../r-bridge/lang-4.x/ast/model/processing/decorate';
|
|
5
|
+
import type { AutoSelectPredicate } from '../../../../reconstruct/auto-select/auto-select-defaults';
|
|
6
6
|
export interface ReconstructRequiredInput {
|
|
7
7
|
autoSelectIf?: AutoSelectPredicate;
|
|
8
8
|
}
|
|
@@ -76,7 +76,7 @@ export declare const DEFAULT_SLICING_PIPELINE: import("./pipeline").Pipeline<{
|
|
|
76
76
|
readonly dependencies: readonly ["slice"];
|
|
77
77
|
readonly requiredInput: import("../all/static-slicing/10-reconstruct").ReconstructRequiredInput;
|
|
78
78
|
}>;
|
|
79
|
-
export declare const
|
|
79
|
+
export declare const DEFAULT_SLICE_AND_RECONSTRUCT_PIPELINE: import("./pipeline").Pipeline<{
|
|
80
80
|
readonly name: "parse";
|
|
81
81
|
readonly humanReadableName: "parse with R shell";
|
|
82
82
|
readonly description: "Parse the given R code into an AST";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DEFAULT_PARSE_PIPELINE = exports.DEFAULT_NORMALIZE_PIPELINE = exports.DEFAULT_DATAFLOW_PIPELINE = exports.
|
|
3
|
+
exports.DEFAULT_PARSE_PIPELINE = exports.DEFAULT_NORMALIZE_PIPELINE = exports.DEFAULT_DATAFLOW_PIPELINE = exports.DEFAULT_SLICE_AND_RECONSTRUCT_PIPELINE = exports.DEFAULT_SLICING_PIPELINE = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Contains the default pipeline for working with flowr
|
|
6
6
|
*/
|
|
@@ -11,7 +11,7 @@ const _20_dataflow_1 = require("../all/core/20-dataflow");
|
|
|
11
11
|
const _00_slice_1 = require("../all/static-slicing/00-slice");
|
|
12
12
|
const _10_reconstruct_1 = require("../all/static-slicing/10-reconstruct");
|
|
13
13
|
exports.DEFAULT_SLICING_PIPELINE = (0, pipeline_1.createPipeline)(_00_parse_1.PARSE_WITH_R_SHELL_STEP, _10_normalize_1.NORMALIZE, _20_dataflow_1.STATIC_DATAFLOW, _00_slice_1.STATIC_SLICE, _10_reconstruct_1.NAIVE_RECONSTRUCT);
|
|
14
|
-
exports.
|
|
14
|
+
exports.DEFAULT_SLICE_AND_RECONSTRUCT_PIPELINE = exports.DEFAULT_SLICING_PIPELINE;
|
|
15
15
|
exports.DEFAULT_DATAFLOW_PIPELINE = (0, pipeline_1.createPipeline)(_00_parse_1.PARSE_WITH_R_SHELL_STEP, _10_normalize_1.NORMALIZE, _20_dataflow_1.STATIC_DATAFLOW);
|
|
16
16
|
exports.DEFAULT_NORMALIZE_PIPELINE = (0, pipeline_1.createPipeline)(_00_parse_1.PARSE_WITH_R_SHELL_STEP, _10_normalize_1.NORMALIZE);
|
|
17
17
|
exports.DEFAULT_PARSE_PIPELINE = (0, pipeline_1.createPipeline)(_00_parse_1.PARSE_WITH_R_SHELL_STEP);
|
package/dataflow/graph/diff.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { DataflowGraph, FunctionArgument, OutgoingEdges } from './graph';
|
|
2
|
-
import type { GenericDifferenceInformation, WriteableDifferenceReport } from '../../util/diff';
|
|
2
|
+
import type { GenericDiffConfiguration, GenericDifferenceInformation, WriteableDifferenceReport } from '../../util/diff';
|
|
3
3
|
import type { NodeId } from '../../r-bridge/lang-4.x/ast/model/processing/node-id';
|
|
4
4
|
interface ProblematicVertex {
|
|
5
5
|
tag: 'vertex';
|
|
@@ -14,7 +14,7 @@ export type ProblematicDiffInfo = ProblematicVertex | ProblematicEdge;
|
|
|
14
14
|
export declare class DataflowDifferenceReport implements WriteableDifferenceReport {
|
|
15
15
|
_comments: string[] | undefined;
|
|
16
16
|
_problematic: ProblematicDiffInfo[] | undefined;
|
|
17
|
-
addComment(comment: string, ...related: ProblematicDiffInfo[]): void;
|
|
17
|
+
addComment(comment: string, ...related: readonly ProblematicDiffInfo[]): void;
|
|
18
18
|
comments(): readonly string[] | undefined;
|
|
19
19
|
problematic(): readonly ProblematicDiffInfo[] | undefined;
|
|
20
20
|
isEqual(): boolean;
|
|
@@ -26,8 +26,9 @@ export interface NamedGraph {
|
|
|
26
26
|
interface DataflowDiffContext extends GenericDifferenceInformation<DataflowDifferenceReport> {
|
|
27
27
|
left: DataflowGraph;
|
|
28
28
|
right: DataflowGraph;
|
|
29
|
+
config: GenericDiffConfiguration;
|
|
29
30
|
}
|
|
30
|
-
export declare function diffOfDataflowGraphs(left: NamedGraph, right: NamedGraph): DataflowDifferenceReport;
|
|
31
|
+
export declare function diffOfDataflowGraphs(left: NamedGraph, right: NamedGraph, config?: Partial<GenericDiffConfiguration>): DataflowDifferenceReport;
|
|
31
32
|
export declare function equalFunctionArguments(fn: NodeId, a: false | readonly FunctionArgument[], b: false | readonly FunctionArgument[]): boolean;
|
|
32
33
|
export declare function diffFunctionArguments(fn: NodeId, a: false | readonly FunctionArgument[], b: false | readonly FunctionArgument[], ctx: GenericDifferenceInformation<DataflowDifferenceReport>): void;
|
|
33
34
|
export declare function diffVertices(ctx: DataflowDiffContext): void;
|
package/dataflow/graph/diff.js
CHANGED
|
@@ -40,31 +40,40 @@ class DataflowDifferenceReport {
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
exports.DataflowDifferenceReport = DataflowDifferenceReport;
|
|
43
|
-
function initDiffContext(left, right) {
|
|
43
|
+
function initDiffContext(left, right, config) {
|
|
44
44
|
return {
|
|
45
45
|
left: left.graph,
|
|
46
46
|
leftname: left.name,
|
|
47
47
|
right: right.graph,
|
|
48
48
|
rightname: right.name,
|
|
49
49
|
report: new DataflowDifferenceReport(),
|
|
50
|
-
position: ''
|
|
50
|
+
position: '',
|
|
51
|
+
config: {
|
|
52
|
+
rightIsSubgraph: false,
|
|
53
|
+
leftIsSubgraph: false,
|
|
54
|
+
...config
|
|
55
|
+
}
|
|
51
56
|
};
|
|
52
57
|
}
|
|
53
58
|
function diff(ctx) {
|
|
54
59
|
diffRootVertices(ctx);
|
|
55
60
|
diffVertices(ctx);
|
|
56
61
|
diffOutgoingEdges(ctx);
|
|
57
|
-
return true;
|
|
58
62
|
}
|
|
59
63
|
function diffOutgoingEdges(ctx) {
|
|
60
64
|
const lEdges = new Map([...ctx.left.edges()]);
|
|
61
65
|
const rEdges = new Map([...ctx.right.edges()]);
|
|
62
|
-
if (lEdges.size
|
|
66
|
+
if (lEdges.size < rEdges.size && !ctx.config.leftIsSubgraph || lEdges.size > rEdges.size && !ctx.config.rightIsSubgraph) {
|
|
63
67
|
ctx.report.addComment(`Detected different number of edges! ${ctx.leftname} has ${lEdges.size} (${JSON.stringify(lEdges, json_1.jsonReplacer)}). ${ctx.rightname} has ${rEdges.size} ${JSON.stringify(rEdges, json_1.jsonReplacer)}`);
|
|
64
68
|
}
|
|
65
69
|
for (const [id, edge] of lEdges) {
|
|
70
|
+
/* This has nothing to do with the subset relation as we verify this in the same graph.
|
|
71
|
+
* Yet we still do the check as a subgraph may not have to have all source vertices for edges.
|
|
72
|
+
*/
|
|
66
73
|
if (!ctx.left.hasVertex(id)) {
|
|
67
|
-
ctx.
|
|
74
|
+
if (!ctx.config.leftIsSubgraph) {
|
|
75
|
+
ctx.report.addComment(`The source ${id} of edges ${JSON.stringify(edge, json_1.jsonReplacer)} is not present in ${ctx.leftname}. This means that the graph contains an edge but not the corresponding vertex.`);
|
|
76
|
+
}
|
|
68
77
|
continue;
|
|
69
78
|
}
|
|
70
79
|
diffEdges(ctx, id, edge, rEdges.get(id));
|
|
@@ -72,22 +81,25 @@ function diffOutgoingEdges(ctx) {
|
|
|
72
81
|
// just to make it both ways in case the length differs
|
|
73
82
|
for (const [id, edge] of rEdges) {
|
|
74
83
|
if (!ctx.right.hasVertex(id)) {
|
|
75
|
-
ctx.
|
|
84
|
+
if (!ctx.config.rightIsSubgraph) {
|
|
85
|
+
ctx.report.addComment(`The source ${id} of edges ${JSON.stringify(edge, json_1.jsonReplacer)} is not present in ${ctx.rightname}. This means that the graph contains an edge but not the corresponding vertex.`);
|
|
86
|
+
}
|
|
76
87
|
continue;
|
|
77
88
|
}
|
|
78
|
-
if (!lEdges.has(id)) {
|
|
89
|
+
if (!ctx.config.leftIsSubgraph && !lEdges.has(id)) {
|
|
79
90
|
diffEdges(ctx, id, undefined, edge);
|
|
80
91
|
}
|
|
92
|
+
/* otherwise, we already cover the edge above */
|
|
81
93
|
}
|
|
82
94
|
}
|
|
83
95
|
function diffRootVertices(ctx) {
|
|
84
96
|
(0, diff_1.setDifference)(ctx.left.rootIds(), ctx.right.rootIds(), { ...ctx, position: `${ctx.position}Root vertices differ in graphs. ` });
|
|
85
97
|
}
|
|
86
|
-
function diffOfDataflowGraphs(left, right) {
|
|
98
|
+
function diffOfDataflowGraphs(left, right, config) {
|
|
87
99
|
if (left.graph === right.graph) {
|
|
88
100
|
return new DataflowDifferenceReport();
|
|
89
101
|
}
|
|
90
|
-
const ctx = initDiffContext(left, right);
|
|
102
|
+
const ctx = initDiffContext(left, right, config);
|
|
91
103
|
diff(ctx);
|
|
92
104
|
return ctx.report;
|
|
93
105
|
}
|
|
@@ -106,7 +118,8 @@ function equalFunctionArguments(fn, a, b) {
|
|
|
106
118
|
report: new DataflowDifferenceReport(),
|
|
107
119
|
leftname: 'left',
|
|
108
120
|
rightname: 'right',
|
|
109
|
-
position: ''
|
|
121
|
+
position: '',
|
|
122
|
+
config: {}
|
|
110
123
|
};
|
|
111
124
|
diffFunctionArguments(fn, a, b, ctx);
|
|
112
125
|
return ctx.report.isEqual();
|
|
@@ -155,13 +168,16 @@ function diffVertices(ctx) {
|
|
|
155
168
|
// collect vertices from both sides
|
|
156
169
|
const lVert = [...ctx.left.vertices(true)].map(([id, info]) => [id, info]);
|
|
157
170
|
const rVert = [...ctx.right.vertices(true)].map(([id, info]) => [id, info]);
|
|
158
|
-
if (lVert.length
|
|
171
|
+
if (lVert.length < rVert.length && !ctx.config.leftIsSubgraph
|
|
172
|
+
|| lVert.length > rVert.length && !ctx.config.rightIsSubgraph) {
|
|
159
173
|
ctx.report.addComment(`Detected different number of vertices! ${ctx.leftname} has ${lVert.length}, ${ctx.rightname} has ${rVert.length}`);
|
|
160
174
|
}
|
|
161
175
|
for (const [id, lInfo] of lVert) {
|
|
162
176
|
const rInfoMay = ctx.right.get(id);
|
|
163
177
|
if (rInfoMay === undefined) {
|
|
164
|
-
ctx.
|
|
178
|
+
if (!ctx.config.rightIsSubgraph) {
|
|
179
|
+
ctx.report.addComment(`Vertex ${id} is not present in ${ctx.rightname}`, { tag: 'vertex', id });
|
|
180
|
+
}
|
|
165
181
|
continue;
|
|
166
182
|
}
|
|
167
183
|
const [rInfo] = rInfoMay;
|
|
@@ -173,15 +189,21 @@ function diffVertices(ctx) {
|
|
|
173
189
|
const lname = lInfo.name ?? (0, node_id_1.recoverName)(id, ctx.left.idMap) ?? '??';
|
|
174
190
|
const rname = rInfo.name ?? (0, node_id_1.recoverName)(id, ctx.right.idMap) ?? '??';
|
|
175
191
|
if (lname !== rname) {
|
|
176
|
-
|
|
177
|
-
ctx.report.addComment(`Vertex ${id} differs in names. ${ctx.leftname}: ${lname} vs ${ctx.rightname}: ${rname}`, {
|
|
192
|
+
ctx.report.addComment(`Vertex ${id} differs in names. ${ctx.leftname}: ${String(lname)} vs ${ctx.rightname}: ${String(rname)}`, {
|
|
178
193
|
tag: 'vertex',
|
|
179
194
|
id
|
|
180
195
|
});
|
|
181
196
|
}
|
|
182
197
|
}
|
|
183
198
|
(0, info_1.diffControlDependencies)(lInfo.controlDependencies, rInfo.controlDependencies, { ...ctx, position: `Vertex ${id} differs in controlDependencies. ` });
|
|
184
|
-
|
|
199
|
+
if ((lInfo.environment === undefined && rInfo.environment !== undefined && !ctx.config.leftIsSubgraph)
|
|
200
|
+
|| (lInfo.environment !== undefined && rInfo.environment === undefined && !ctx.config.rightIsSubgraph)) {
|
|
201
|
+
/* only diff them if specified at all */
|
|
202
|
+
(0, diff_2.diffEnvironmentInformation)(lInfo.environment, rInfo.environment, {
|
|
203
|
+
...ctx,
|
|
204
|
+
position: `${ctx.position}Vertex ${id} differs in environment. `
|
|
205
|
+
});
|
|
206
|
+
}
|
|
185
207
|
if (lInfo.tag === "function-call" /* VertexType.FunctionCall */) {
|
|
186
208
|
if (rInfo.tag !== "function-call" /* VertexType.FunctionCall */) {
|
|
187
209
|
ctx.report.addComment(`Vertex ${id} differs in tags. ${ctx.leftname}: ${lInfo.tag} vs. ${ctx.rightname}: ${rInfo.tag}`);
|
|
@@ -190,10 +212,13 @@ function diffVertices(ctx) {
|
|
|
190
212
|
if (lInfo.onlyBuiltin !== rInfo.onlyBuiltin) {
|
|
191
213
|
ctx.report.addComment(`Vertex ${id} differs in onlyBuiltin. ${ctx.leftname}: ${lInfo.onlyBuiltin} vs ${ctx.rightname}: ${rInfo.onlyBuiltin}`, { tag: 'vertex', id });
|
|
192
214
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
215
|
+
if ((lInfo.args.length === 0 && rInfo.args.length !== 0 && !ctx.config.leftIsSubgraph)
|
|
216
|
+
|| (lInfo.args.length !== 0 && rInfo.args.length === 0 && !ctx.config.rightIsSubgraph)) {
|
|
217
|
+
diffFunctionArguments(lInfo.id, lInfo.args, rInfo.args, {
|
|
218
|
+
...ctx,
|
|
219
|
+
position: `${ctx.position}Vertex ${id} (function call) differs in arguments. `
|
|
220
|
+
});
|
|
221
|
+
}
|
|
197
222
|
}
|
|
198
223
|
}
|
|
199
224
|
if (lInfo.tag === 'function-definition') {
|
|
@@ -204,10 +229,13 @@ function diffVertices(ctx) {
|
|
|
204
229
|
if (!(0, arrays_1.arrayEqual)(lInfo.exitPoints, rInfo.exitPoints)) {
|
|
205
230
|
ctx.report.addComment(`Vertex ${id} differs in exit points. ${ctx.leftname}: ${JSON.stringify(lInfo.exitPoints, json_1.jsonReplacer)} vs ${ctx.rightname}: ${JSON.stringify(rInfo.exitPoints, json_1.jsonReplacer)}`, { tag: 'vertex', id });
|
|
206
231
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
232
|
+
if ((lInfo.subflow.environment === undefined && rInfo.subflow.environment !== undefined && !ctx.config.leftIsSubgraph)
|
|
233
|
+
|| (lInfo.subflow.environment !== undefined && rInfo.subflow.environment === undefined && !ctx.config.rightIsSubgraph)) {
|
|
234
|
+
(0, diff_2.diffEnvironmentInformation)(lInfo.subflow.environment, rInfo.subflow.environment, {
|
|
235
|
+
...ctx,
|
|
236
|
+
position: `${ctx.position}Vertex ${id} (function definition) differs in subflow environments. `
|
|
237
|
+
});
|
|
238
|
+
}
|
|
211
239
|
(0, diff_1.setDifference)(lInfo.subflow.graph, rInfo.subflow.graph, {
|
|
212
240
|
...ctx,
|
|
213
241
|
position: `${ctx.position}Vertex ${id} differs in subflow graph. `
|
|
@@ -229,19 +257,23 @@ function diffEdge(edge, otherEdge, ctx, id, target) {
|
|
|
229
257
|
}
|
|
230
258
|
function diffEdges(ctx, id, lEdges, rEdges) {
|
|
231
259
|
if (lEdges === undefined || rEdges === undefined) {
|
|
232
|
-
if (lEdges
|
|
260
|
+
if ((lEdges === undefined && !ctx.config.leftIsSubgraph)
|
|
261
|
+
|| (rEdges === undefined && !ctx.config.rightIsSubgraph)) {
|
|
233
262
|
ctx.report.addComment(`Vertex ${id} has undefined outgoing edges. ${ctx.leftname}: ${JSON.stringify(lEdges, json_1.jsonReplacer)} vs ${ctx.rightname}: ${JSON.stringify(rEdges, json_1.jsonReplacer)}`, { tag: 'vertex', id });
|
|
234
263
|
}
|
|
235
264
|
return;
|
|
236
265
|
}
|
|
237
|
-
if (lEdges.size
|
|
266
|
+
if (lEdges.size < rEdges.size && !ctx.config.leftIsSubgraph
|
|
267
|
+
|| lEdges.size > rEdges.size && !ctx.config.rightIsSubgraph) {
|
|
238
268
|
ctx.report.addComment(`Vertex ${id} differs in number of outgoing edges. ${ctx.leftname}: [${[...lEdges.keys()].join(',')}] vs ${ctx.rightname}: [${[...rEdges.keys()].join(',')}] `, { tag: 'vertex', id });
|
|
239
269
|
}
|
|
240
270
|
// order independent compare
|
|
241
271
|
for (const [target, edge] of lEdges) {
|
|
242
272
|
const otherEdge = rEdges.get(target);
|
|
243
273
|
if (otherEdge === undefined) {
|
|
244
|
-
|
|
274
|
+
if (!ctx.config.rightIsSubgraph) {
|
|
275
|
+
ctx.report.addComment(`Target of ${id}->${target} in ${ctx.leftname} is not present in ${ctx.rightname}`, { tag: 'edge', from: id, to: target });
|
|
276
|
+
}
|
|
245
277
|
continue;
|
|
246
278
|
}
|
|
247
279
|
diffEdge(edge, otherEdge, ctx, id, target);
|
|
@@ -4,14 +4,19 @@ import type { IdentifierReference } from '../environments/identifier';
|
|
|
4
4
|
import type { DataflowGraph, FunctionArgument } from '../graph/graph';
|
|
5
5
|
import type { RParameter } from '../../r-bridge/lang-4.x/ast/model/nodes/r-parameter';
|
|
6
6
|
import type { AstIdMap, ParentInformation } from '../../r-bridge/lang-4.x/ast/model/processing/decorate';
|
|
7
|
-
import type { DataflowGraphVertexInfo } from '../graph/vertex';
|
|
7
|
+
import type { DataflowGraphVertexFunctionCall, DataflowGraphVertexFunctionDefinition, DataflowGraphVertexInfo } from '../graph/vertex';
|
|
8
8
|
import type { REnvironmentInformation } from '../environments/environment';
|
|
9
9
|
export type NameIdMap = DefaultMap<string, IdentifierReference[]>;
|
|
10
10
|
export declare function produceNameSharedIdMap(references: IdentifierReference[]): NameIdMap;
|
|
11
11
|
export declare function linkArgumentsOnCall(args: FunctionArgument[], params: RParameter<ParentInformation>[], graph: DataflowGraph): void;
|
|
12
|
+
export declare function linkFunctionCallWithSingleTarget(graph: DataflowGraph, def: DataflowGraphVertexFunctionDefinition, info: DataflowGraphVertexFunctionCall, idMap: AstIdMap): void;
|
|
12
13
|
/**
|
|
13
14
|
* Returns the called functions within the current graph, which can be used to merge the environments with the call.
|
|
14
15
|
* Furthermore, it links the corresponding arguments.
|
|
16
|
+
*
|
|
17
|
+
* @param graph - The graph to use for search and resolution traversals (ideally a superset of the `thisGraph`)
|
|
18
|
+
* @param idMap - The map to resolve ids to names
|
|
19
|
+
* @param thisGraph - The graph to search for function calls in
|
|
15
20
|
*/
|
|
16
21
|
export declare function linkFunctionCalls(graph: DataflowGraph, idMap: AstIdMap, thisGraph: DataflowGraph): {
|
|
17
22
|
functionCall: NodeId;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.linkCircularRedefinitionsWithinALoop = exports.linkInputs = exports.getAllLinkedFunctionDefinitions = exports.linkFunctionCalls = exports.linkArgumentsOnCall = exports.produceNameSharedIdMap = void 0;
|
|
3
|
+
exports.linkCircularRedefinitionsWithinALoop = exports.linkInputs = exports.getAllLinkedFunctionDefinitions = exports.linkFunctionCalls = exports.linkFunctionCallWithSingleTarget = exports.linkArgumentsOnCall = exports.produceNameSharedIdMap = void 0;
|
|
4
4
|
const defaultmap_1 = require("../../util/defaultmap");
|
|
5
5
|
const assert_1 = require("../../util/assert");
|
|
6
6
|
const log_1 = require("../../util/log");
|
|
@@ -79,6 +79,30 @@ function linkFunctionCallArguments(targetId, idMap, functionCallName, functionRo
|
|
|
79
79
|
(0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `linking arguments for ${functionCallName} (${functionRootId}) to ${JSON.stringify(linkedFunction.location)}`);
|
|
80
80
|
linkArgumentsOnCall(callArgs, linkedFunction.parameters, finalGraph);
|
|
81
81
|
}
|
|
82
|
+
function linkFunctionCallWithSingleTarget(graph, def, info, idMap) {
|
|
83
|
+
const id = info.id;
|
|
84
|
+
if (info.environment !== undefined) {
|
|
85
|
+
// for each open ingoing reference, try to resolve it here, and if so, add a read edge from the call to signal that it reads it
|
|
86
|
+
for (const ingoing of def.subflow.in) {
|
|
87
|
+
const defs = ingoing.name ? (0, resolve_by_name_1.resolveByName)(ingoing.name, info.environment) : undefined;
|
|
88
|
+
if (defs === undefined) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
for (const def of defs) {
|
|
92
|
+
graph.addEdge(id, def, { type: 1 /* EdgeType.Reads */ });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const exitPoints = def.exitPoints;
|
|
97
|
+
for (const exitPoint of exitPoints) {
|
|
98
|
+
graph.addEdge(id, exitPoint, { type: 8 /* EdgeType.Returns */ });
|
|
99
|
+
}
|
|
100
|
+
const defName = (0, node_id_1.recoverName)(def.id, idMap);
|
|
101
|
+
(0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `recording expression-list-level call from ${(0, node_id_1.recoverName)(info.id, idMap)} to ${defName}`);
|
|
102
|
+
graph.addEdge(id, def.id, { type: 4 /* EdgeType.Calls */ });
|
|
103
|
+
linkFunctionCallArguments(def.id, idMap, defName, id, info.args, graph);
|
|
104
|
+
}
|
|
105
|
+
exports.linkFunctionCallWithSingleTarget = linkFunctionCallWithSingleTarget;
|
|
82
106
|
function linkFunctionCall(graph, id, info, idMap, thisGraph, calledFunctionDefinitions) {
|
|
83
107
|
const edges = graph.outgoingEdges(id);
|
|
84
108
|
if (edges === undefined) {
|
|
@@ -91,26 +115,7 @@ function linkFunctionCall(graph, id, info, idMap, thisGraph, calledFunctionDefin
|
|
|
91
115
|
const functionDefs = getAllLinkedFunctionDefinitions(new Set(functionDefinitionReadIds), graph);
|
|
92
116
|
for (const def of functionDefs.values()) {
|
|
93
117
|
(0, assert_1.guard)(def.tag === "function-definition" /* VertexType.FunctionDefinition */, () => `expected function definition, but got ${def.tag}`);
|
|
94
|
-
|
|
95
|
-
// for each open ingoing reference, try to resolve it here, and if so, add a read edge from the call to signal that it reads it
|
|
96
|
-
for (const ingoing of def.subflow.in) {
|
|
97
|
-
const defs = ingoing.name ? (0, resolve_by_name_1.resolveByName)(ingoing.name, info.environment) : undefined;
|
|
98
|
-
if (defs === undefined) {
|
|
99
|
-
continue;
|
|
100
|
-
}
|
|
101
|
-
for (const def of defs) {
|
|
102
|
-
graph.addEdge(id, def, { type: 1 /* EdgeType.Reads */ });
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
const exitPoints = def.exitPoints;
|
|
107
|
-
for (const exitPoint of exitPoints) {
|
|
108
|
-
graph.addEdge(id, exitPoint, { type: 8 /* EdgeType.Returns */ });
|
|
109
|
-
}
|
|
110
|
-
const defName = (0, node_id_1.recoverName)(def.id, idMap);
|
|
111
|
-
(0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `recording expression-list-level call from ${(0, node_id_1.recoverName)(info.id, idMap)} to ${defName}`);
|
|
112
|
-
graph.addEdge(id, def.id, { type: 4 /* EdgeType.Calls */ });
|
|
113
|
-
linkFunctionCallArguments(def.id, idMap, defName, id, info.args, graph);
|
|
118
|
+
linkFunctionCallWithSingleTarget(graph, def, info, idMap);
|
|
114
119
|
}
|
|
115
120
|
if (thisGraph.isRoot(id)) {
|
|
116
121
|
calledFunctionDefinitions.push({ functionCall: id, called: [...functionDefs.values()] });
|
|
@@ -119,6 +124,10 @@ function linkFunctionCall(graph, id, info, idMap, thisGraph, calledFunctionDefin
|
|
|
119
124
|
/**
|
|
120
125
|
* Returns the called functions within the current graph, which can be used to merge the environments with the call.
|
|
121
126
|
* Furthermore, it links the corresponding arguments.
|
|
127
|
+
*
|
|
128
|
+
* @param graph - The graph to use for search and resolution traversals (ideally a superset of the `thisGraph`)
|
|
129
|
+
* @param idMap - The map to resolve ids to names
|
|
130
|
+
* @param thisGraph - The graph to search for function calls in
|
|
122
131
|
*/
|
|
123
132
|
function linkFunctionCalls(graph, idMap, thisGraph) {
|
|
124
133
|
const functionCalls = [...thisGraph.vertices(true)]
|
|
@@ -4,6 +4,8 @@ exports.processFunctionArgument = exports.linkReadsForArgument = void 0;
|
|
|
4
4
|
const processor_1 = require("../../../processor");
|
|
5
5
|
const collect_1 = require("../../../../r-bridge/lang-4.x/ast/model/collect");
|
|
6
6
|
const graph_1 = require("../../../graph/graph");
|
|
7
|
+
const edge_1 = require("../../../graph/edge");
|
|
8
|
+
const resolve_by_name_1 = require("../../../environments/resolve-by-name");
|
|
7
9
|
function linkReadsForArgument(root, ingoingRefs, graph) {
|
|
8
10
|
const allIdsBeforeArguments = new Set((0, collect_1.collectAllIds)(root, n => n.type === "RArgument" /* RType.Argument */ && n.info.id !== root.info.id));
|
|
9
11
|
const ingoingBeforeArgs = ingoingRefs.filter(r => allIdsBeforeArguments.has(r.nodeId));
|
|
@@ -13,6 +15,18 @@ function linkReadsForArgument(root, ingoingRefs, graph) {
|
|
|
13
15
|
}
|
|
14
16
|
}
|
|
15
17
|
exports.linkReadsForArgument = linkReadsForArgument;
|
|
18
|
+
function hasNoOutgoingCallEdge(graph, id) {
|
|
19
|
+
const outgoings = graph.outgoingEdges(id);
|
|
20
|
+
if (outgoings === undefined) {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
for (const [_, edge] of outgoings) {
|
|
24
|
+
if ((0, edge_1.edgeIncludesType)(edge.types, 4 /* EdgeType.Calls */)) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
16
30
|
function processFunctionArgument(argument, data) {
|
|
17
31
|
const name = argument.name === undefined ? undefined : (0, processor_1.processDataflowFor)(argument.name, data);
|
|
18
32
|
const value = argument.value === undefined ? undefined : (0, processor_1.processDataflowFor)(argument.value, data);
|
|
@@ -29,6 +43,20 @@ function processFunctionArgument(argument, data) {
|
|
|
29
43
|
entryPoint = argument.info.id;
|
|
30
44
|
}
|
|
31
45
|
const ingoingRefs = [...value?.unknownReferences ?? [], ...value?.in ?? [], ...(name === undefined ? [] : [...name.in])];
|
|
46
|
+
/* potentially link all function calls here (as maybes with a maybe cd) as the called function may employ calling-env-semantics if unknown */
|
|
47
|
+
if (value) {
|
|
48
|
+
const functionCalls = [...graph.vertices(true)]
|
|
49
|
+
.filter(([_, info]) => info.tag === "function-call" /* VertexType.FunctionCall */)
|
|
50
|
+
.filter(([id]) => hasNoOutgoingCallEdge(graph, id));
|
|
51
|
+
// try to resolve them against the current environment
|
|
52
|
+
for (const [id, info] of functionCalls) {
|
|
53
|
+
const resolved = (0, resolve_by_name_1.resolveByName)(info.name, data.environment) ?? [];
|
|
54
|
+
/* first, only link a read ref */
|
|
55
|
+
for (const resolve of resolved) {
|
|
56
|
+
graph.addEdge(id, resolve.nodeId, { type: 1 /* EdgeType.Reads */ });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
32
60
|
if (entryPoint && argument.value?.type === "RFunctionDefinition" /* RType.FunctionDefinition */) {
|
|
33
61
|
graph.addEdge(entryPoint, argument.value.info.id, { type: 1 /* EdgeType.Reads */ });
|
|
34
62
|
}
|
package/package.json
CHANGED
|
@@ -23,11 +23,11 @@ function prepareParsedData(data) {
|
|
|
23
23
|
// iterate a second time to set parent-child relations (since they may be out of order in the csv)
|
|
24
24
|
for (const entry of ret.values()) {
|
|
25
25
|
if (entry.parent != exports.RootId) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
/** it turns out that comments may return a negative id pair to their parent */
|
|
27
|
+
const parent = ret.get(Math.abs(entry.parent));
|
|
28
|
+
(0, assert_1.guard)(parent !== undefined, () => `Could not find parent ${entry.parent} for entry ${entry.id}`);
|
|
29
|
+
parent.children ??= [];
|
|
30
|
+
parent.children.push(entry);
|
|
31
31
|
}
|
|
32
32
|
else {
|
|
33
33
|
roots.push(entry);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { NoInfo, RNode } from '../../r-bridge/lang-4.x/ast/model/model';
|
|
2
|
+
import type { ParentInformation, NormalizedAst } from '../../r-bridge/lang-4.x/ast/model/processing/decorate';
|
|
3
|
+
/**
|
|
4
|
+
* The structure of the predicate that should be used to determine
|
|
5
|
+
* if a given normalized node should be included in the reconstructed code,
|
|
6
|
+
* independent of if it is selected by the slice or not.
|
|
7
|
+
*
|
|
8
|
+
* @see reconstructToCode
|
|
9
|
+
* @see doNotAutoSelect
|
|
10
|
+
* @see autoSelectLibrary
|
|
11
|
+
*/
|
|
12
|
+
export type AutoSelectPredicate = (node: RNode<ParentInformation>, fullAst: NormalizedAst) => boolean;
|
|
13
|
+
/**
|
|
14
|
+
* A variant of the {@link AutoSelectPredicate} which does not select any additional statements (~> false)
|
|
15
|
+
*/
|
|
16
|
+
export declare function doNotAutoSelect(_node: RNode): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* A variant of the {@link AutoSelectPredicate} which does its best
|
|
19
|
+
* to select any kind of library import automatically.
|
|
20
|
+
*/
|
|
21
|
+
export declare function autoSelectLibrary<Info = NoInfo>(node: RNode<Info>): boolean;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.autoSelectLibrary = exports.doNotAutoSelect = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* A variant of the {@link AutoSelectPredicate} which does not select any additional statements (~> false)
|
|
6
|
+
*/
|
|
7
|
+
function doNotAutoSelect(_node) {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
exports.doNotAutoSelect = doNotAutoSelect;
|
|
11
|
+
const libraryFunctionCall = /^(library|require|((require|load|attach)Namespace))$/;
|
|
12
|
+
/**
|
|
13
|
+
* A variant of the {@link AutoSelectPredicate} which does its best
|
|
14
|
+
* to select any kind of library import automatically.
|
|
15
|
+
*/
|
|
16
|
+
function autoSelectLibrary(node) {
|
|
17
|
+
if (node.type !== "RFunctionCall" /* RType.FunctionCall */ || !node.named) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
return libraryFunctionCall.test(node.functionName.content);
|
|
21
|
+
}
|
|
22
|
+
exports.autoSelectLibrary = autoSelectLibrary;
|
|
23
|
+
//# sourceMappingURL=auto-select-defaults.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { AutoSelectPredicate } from './auto-select-defaults';
|
|
2
|
+
/**
|
|
3
|
+
* This takes an {@link NormalizedAst} and returns an auto-select predicate for {@link reconstructToCode},
|
|
4
|
+
* which will automatically include lines marked by these special comments!
|
|
5
|
+
* Please make sure to create one per source as it will use it to cache.
|
|
6
|
+
*
|
|
7
|
+
* We support two formats:
|
|
8
|
+
* - Line comments in the form of `# flowr@include_next_line` or `# flowr@include_this_line`.
|
|
9
|
+
* - Block comments which start with `# flowr@include_start` and end with `# flowr@include_end`.
|
|
10
|
+
* This supports nesting, but they have to appear on a single line.
|
|
11
|
+
*
|
|
12
|
+
* Please note that these comments have to start exactly with this content to work.
|
|
13
|
+
*
|
|
14
|
+
* @param and - Predicate to composite this one with, If you do not pass a predicate, you may assume composition with
|
|
15
|
+
* {@link doNotAutoSelect}.
|
|
16
|
+
*/
|
|
17
|
+
export declare function makeMagicCommentHandler(and?: AutoSelectPredicate): AutoSelectPredicate;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.makeMagicCommentHandler = void 0;
|
|
4
|
+
const visitor_1 = require("../../r-bridge/lang-4.x/ast/model/processing/visitor");
|
|
5
|
+
const assert_1 = require("../../util/assert");
|
|
6
|
+
function getLoc({ location, info: { fullRange } }) {
|
|
7
|
+
const loc = location ?? fullRange;
|
|
8
|
+
(0, assert_1.guard)(loc !== undefined, 'TODO: support location-less nodes!');
|
|
9
|
+
return loc;
|
|
10
|
+
}
|
|
11
|
+
const magicCommentIdMapper = {
|
|
12
|
+
'include_next_line': (n) => {
|
|
13
|
+
return [getLoc(n)[0] + 1];
|
|
14
|
+
},
|
|
15
|
+
'include_this_line': (n) => {
|
|
16
|
+
return [getLoc(n)[0]];
|
|
17
|
+
},
|
|
18
|
+
'include_start': (n, stack) => {
|
|
19
|
+
stack.push(getLoc(n)[0] + 1);
|
|
20
|
+
return undefined;
|
|
21
|
+
},
|
|
22
|
+
'include_end': (n, stack) => {
|
|
23
|
+
const to = getLoc(n)[0];
|
|
24
|
+
(0, assert_1.guard)(stack.length >= 1, `mismatched magic start and end at ${to}`);
|
|
25
|
+
const from = stack.pop();
|
|
26
|
+
const ret = new Array(to - from - 1);
|
|
27
|
+
for (let i = from; i < to; i++) {
|
|
28
|
+
ret[i - from] = i;
|
|
29
|
+
}
|
|
30
|
+
return ret;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const commentTriggerRegex = / flowr@(\w+)/;
|
|
34
|
+
/**
|
|
35
|
+
* This takes an {@link NormalizedAst} and returns an auto-select predicate for {@link reconstructToCode},
|
|
36
|
+
* which will automatically include lines marked by these special comments!
|
|
37
|
+
* Please make sure to create one per source as it will use it to cache.
|
|
38
|
+
*
|
|
39
|
+
* We support two formats:
|
|
40
|
+
* - Line comments in the form of `# flowr@include_next_line` or `# flowr@include_this_line`.
|
|
41
|
+
* - Block comments which start with `# flowr@include_start` and end with `# flowr@include_end`.
|
|
42
|
+
* This supports nesting, but they have to appear on a single line.
|
|
43
|
+
*
|
|
44
|
+
* Please note that these comments have to start exactly with this content to work.
|
|
45
|
+
*
|
|
46
|
+
* @param and - Predicate to composite this one with, If you do not pass a predicate, you may assume composition with
|
|
47
|
+
* {@link doNotAutoSelect}.
|
|
48
|
+
*/
|
|
49
|
+
function makeMagicCommentHandler(and) {
|
|
50
|
+
let lines = undefined;
|
|
51
|
+
return (node, normalizedAst) => {
|
|
52
|
+
if (!lines) {
|
|
53
|
+
lines = new Set();
|
|
54
|
+
const startLineStack = [];
|
|
55
|
+
(0, visitor_1.visitAst)(normalizedAst.ast, n => {
|
|
56
|
+
const comments = n.info.additionalTokens;
|
|
57
|
+
if (!comments) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
for (const c of comments) {
|
|
61
|
+
if (c.type !== "RComment" /* RType.Comment */ || !c.content.startsWith(' flowr@')) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const match = commentTriggerRegex.exec(c.content);
|
|
65
|
+
(0, assert_1.guard)(match !== null, `invalid magic comment: ${c.content}`);
|
|
66
|
+
const idMapper = magicCommentIdMapper[match[1]];
|
|
67
|
+
(0, assert_1.guard)(idMapper !== undefined, `unknown magic comment: ${match[1]}`);
|
|
68
|
+
const ls = idMapper(c, startLineStack);
|
|
69
|
+
if (ls !== undefined) {
|
|
70
|
+
for (const l of ls) {
|
|
71
|
+
lines.add(l);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
(0, assert_1.guard)(startLineStack.length === 0, `mismatched magic start and end at end of file (${JSON.stringify(startLineStack)})`);
|
|
77
|
+
}
|
|
78
|
+
const loc = node.location ?? node.info.fullRange;
|
|
79
|
+
if (loc && lines.has(loc[0])) {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
return and?.(node, normalizedAst) ?? false;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
exports.makeMagicCommentHandler = makeMagicCommentHandler;
|
|
86
|
+
//# sourceMappingURL=magic-comments.js.map
|
|
@@ -3,15 +3,11 @@
|
|
|
3
3
|
* as the file itself is way too long). See {@link reconstructToCode}.
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
|
-
import type { NormalizedAst
|
|
7
|
-
import type { RNode } from '../r-bridge/lang-4.x/ast/model/model';
|
|
6
|
+
import type { NormalizedAst } from '../r-bridge/lang-4.x/ast/model/processing/decorate';
|
|
8
7
|
import type { NodeId } from '../r-bridge/lang-4.x/ast/model/processing/node-id';
|
|
8
|
+
import type { AutoSelectPredicate } from './auto-select/auto-select-defaults';
|
|
9
9
|
type Selection = ReadonlySet<NodeId>;
|
|
10
10
|
export declare const reconstructLogger: import("tslog").Logger<import("tslog").ILogObj>;
|
|
11
|
-
/** The structure of the predicate that should be used to determine if a given normalized node should be included in the reconstructed code independent of if it is selected by the slice or not */
|
|
12
|
-
export type AutoSelectPredicate = (node: RNode<ParentInformation>) => boolean;
|
|
13
|
-
export declare function doNotAutoSelect(_node: RNode<ParentInformation>): boolean;
|
|
14
|
-
export declare function autoSelectLibrary(node: RNode<ParentInformation>): boolean;
|
|
15
11
|
export interface ReconstructionResult {
|
|
16
12
|
code: string;
|
|
17
13
|
/** number of lines that contain nodes that triggered the `autoSelectIf` predicate {@link reconstructToCode} */
|
|
@@ -26,5 +22,5 @@ export interface ReconstructionResult {
|
|
|
26
22
|
*
|
|
27
23
|
* @returns The number of lines for which `autoSelectIf` triggered, as well as the reconstructed code itself.
|
|
28
24
|
*/
|
|
29
|
-
export declare function reconstructToCode
|
|
25
|
+
export declare function reconstructToCode(ast: NormalizedAst, selection: Selection, autoSelectIf?: AutoSelectPredicate): ReconstructionResult;
|
|
30
26
|
export {};
|
|
@@ -5,21 +5,26 @@
|
|
|
5
5
|
* @module
|
|
6
6
|
*/
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
exports.reconstructToCode = exports.
|
|
8
|
+
exports.reconstructToCode = exports.reconstructLogger = void 0;
|
|
9
9
|
const log_1 = require("../util/log");
|
|
10
10
|
const assert_1 = require("../util/assert");
|
|
11
11
|
const r_function_call_1 = require("../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
|
|
12
12
|
const stateful_fold_1 = require("../r-bridge/lang-4.x/ast/model/processing/stateful-fold");
|
|
13
|
+
const auto_select_defaults_1 = require("./auto-select/auto-select-defaults");
|
|
13
14
|
function plain(text) {
|
|
14
15
|
return [{ line: text, indent: 0 }];
|
|
15
16
|
}
|
|
16
17
|
exports.reconstructLogger = log_1.log.getSubLogger({ name: 'reconstruct' });
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
function getLexeme(n) {
|
|
19
|
+
return n.info.fullLexeme ?? n.lexeme ?? '';
|
|
20
|
+
}
|
|
21
|
+
function reconstructAsLeaf(leaf, configuration) {
|
|
22
|
+
const selectionHasLeaf = configuration.selection.has(leaf.info.id) || configuration.autoSelectIf(leaf, configuration.fullAst);
|
|
20
23
|
return selectionHasLeaf ? foldToConst(leaf) : [];
|
|
21
|
-
}
|
|
22
|
-
|
|
24
|
+
}
|
|
25
|
+
function foldToConst(n) {
|
|
26
|
+
return plain(getLexeme(n));
|
|
27
|
+
}
|
|
23
28
|
function indentBy(lines, indent) {
|
|
24
29
|
return lines.map(({ line, indent: i }) => ({ line, indent: i + indent }));
|
|
25
30
|
}
|
|
@@ -57,7 +62,7 @@ function reconstructExpressionList(exprList, _grouping, expressions, config) {
|
|
|
57
62
|
}
|
|
58
63
|
}
|
|
59
64
|
function isSelected(configuration, n) {
|
|
60
|
-
return configuration.selection.has(n.info.id) || configuration.autoSelectIf(n);
|
|
65
|
+
return configuration.selection.has(n.info.id) || configuration.autoSelectIf(n, configuration.fullAst);
|
|
61
66
|
}
|
|
62
67
|
function reconstructRawBinaryOperator(lhs, n, rhs) {
|
|
63
68
|
return [
|
|
@@ -360,18 +365,6 @@ function reconstructFunctionCall(call, functionName, args, configuration) {
|
|
|
360
365
|
return plain(getLexeme(call));
|
|
361
366
|
}
|
|
362
367
|
}
|
|
363
|
-
function doNotAutoSelect(_node) {
|
|
364
|
-
return false;
|
|
365
|
-
}
|
|
366
|
-
exports.doNotAutoSelect = doNotAutoSelect;
|
|
367
|
-
const libraryFunctionCall = /^(library|require|((require|load|attach)Namespace))$/;
|
|
368
|
-
function autoSelectLibrary(node) {
|
|
369
|
-
if (node.type !== "RFunctionCall" /* RType.FunctionCall */ || !node.named) {
|
|
370
|
-
return false;
|
|
371
|
-
}
|
|
372
|
-
return libraryFunctionCall.test(node.functionName.content);
|
|
373
|
-
}
|
|
374
|
-
exports.autoSelectLibrary = autoSelectLibrary;
|
|
375
368
|
/**
|
|
376
369
|
* The fold functions used to reconstruct the ast in {@link reconstructToCode}.
|
|
377
370
|
*/
|
|
@@ -431,14 +424,14 @@ function removeOuterExpressionListIfApplicable(result, linesWithAutoSelected) {
|
|
|
431
424
|
*
|
|
432
425
|
* @returns The number of lines for which `autoSelectIf` triggered, as well as the reconstructed code itself.
|
|
433
426
|
*/
|
|
434
|
-
function reconstructToCode(ast, selection, autoSelectIf = autoSelectLibrary) {
|
|
427
|
+
function reconstructToCode(ast, selection, autoSelectIf = auto_select_defaults_1.autoSelectLibrary) {
|
|
435
428
|
if (exports.reconstructLogger.settings.minLevel <= 1 /* LogLevel.Trace */) {
|
|
436
429
|
exports.reconstructLogger.trace(`reconstruct ast with ids: ${JSON.stringify([...selection])}`);
|
|
437
430
|
}
|
|
438
431
|
// we use a wrapper to count the number of lines for which the autoSelectIf predicate triggered
|
|
439
432
|
const linesWithAutoSelected = new Set();
|
|
440
433
|
const autoSelectIfWrapper = (node) => {
|
|
441
|
-
const result = autoSelectIf(node);
|
|
434
|
+
const result = autoSelectIf(node, ast);
|
|
442
435
|
if (result && node.location) {
|
|
443
436
|
for (let i = node.location[0]; i <= node.location[2]; i++) {
|
|
444
437
|
linesWithAutoSelected.add(i);
|
|
@@ -447,7 +440,7 @@ function reconstructToCode(ast, selection, autoSelectIf = autoSelectLibrary) {
|
|
|
447
440
|
return result;
|
|
448
441
|
};
|
|
449
442
|
// fold of the normalized ast
|
|
450
|
-
const result = (0, stateful_fold_1.foldAstStateful)(ast.ast, { selection, autoSelectIf: autoSelectIfWrapper }, reconstructAstFolds);
|
|
443
|
+
const result = (0, stateful_fold_1.foldAstStateful)(ast.ast, { selection, autoSelectIf: autoSelectIfWrapper, fullAst: ast }, reconstructAstFolds);
|
|
451
444
|
(0, log_1.expensiveTrace)(exports.reconstructLogger, () => `reconstructed ast before string conversion: ${JSON.stringify(result)}`);
|
|
452
445
|
return removeOuterExpressionListIfApplicable(result, linesWithAutoSelected.size);
|
|
453
446
|
}
|
|
@@ -4,8 +4,13 @@ import type { NormalizedAst } from '../../r-bridge/lang-4.x/ast/model/processing
|
|
|
4
4
|
import type { SlicingCriteria } from '../criterion/parse';
|
|
5
5
|
export declare const slicerLogger: import("tslog").Logger<import("tslog").ILogObj>;
|
|
6
6
|
/**
|
|
7
|
-
* This returns the ids to include in the slice, when slicing with the given seed id's (must be at least one).
|
|
7
|
+
* This returns the ids to include in the static backward slice, when slicing with the given seed id's (must be at least one).
|
|
8
8
|
* <p>
|
|
9
9
|
* The returned ids can be used to {@link reconstructToCode|reconstruct the slice to R code}.
|
|
10
|
+
*
|
|
11
|
+
* @param graph - The dataflow graph to conduct the slicing on.
|
|
12
|
+
* @param ast - The normalized AST of the code (used to get static depth information of the lexemes in case of control flow dependencies that may have no effect on the slicing scope).
|
|
13
|
+
* @param criteria - The criteras to slice on.
|
|
14
|
+
* @param threshold - The maximum number of nodes to visit in the graph. If the threshold is reached, the slice will side with inclusion and drop its minimal guarantee. The limit ensures that the algorithm halts.
|
|
10
15
|
*/
|
|
11
16
|
export declare function staticSlicing(graph: DataflowGraph, ast: NormalizedAst, criteria: SlicingCriteria, threshold?: number): Readonly<SliceResult>;
|
|
@@ -11,9 +11,14 @@ const environment_1 = require("../../dataflow/environments/environment");
|
|
|
11
11
|
const edge_1 = require("../../dataflow/graph/edge");
|
|
12
12
|
exports.slicerLogger = log_1.log.getSubLogger({ name: 'slicer' });
|
|
13
13
|
/**
|
|
14
|
-
* This returns the ids to include in the slice, when slicing with the given seed id's (must be at least one).
|
|
14
|
+
* This returns the ids to include in the static backward slice, when slicing with the given seed id's (must be at least one).
|
|
15
15
|
* <p>
|
|
16
16
|
* The returned ids can be used to {@link reconstructToCode|reconstruct the slice to R code}.
|
|
17
|
+
*
|
|
18
|
+
* @param graph - The dataflow graph to conduct the slicing on.
|
|
19
|
+
* @param ast - The normalized AST of the code (used to get static depth information of the lexemes in case of control flow dependencies that may have no effect on the slicing scope).
|
|
20
|
+
* @param criteria - The criteras to slice on.
|
|
21
|
+
* @param threshold - The maximum number of nodes to visit in the graph. If the threshold is reached, the slice will side with inclusion and drop its minimal guarantee. The limit ensures that the algorithm halts.
|
|
17
22
|
*/
|
|
18
23
|
function staticSlicing(graph, ast, criteria, threshold = 75) {
|
|
19
24
|
(0, assert_1.guard)(criteria.length > 0, 'must have at least one seed id to calculate slice');
|
|
@@ -26,11 +31,11 @@ function staticSlicing(graph, ast, criteria, threshold = 75) {
|
|
|
26
31
|
{
|
|
27
32
|
const emptyEnv = (0, environment_1.initializeCleanEnvironments)();
|
|
28
33
|
const basePrint = (0, fingerprint_1.envFingerprint)(emptyEnv);
|
|
29
|
-
for (const startId of decodedCriteria) {
|
|
30
|
-
queue.add(startId
|
|
34
|
+
for (const { id: startId } of decodedCriteria) {
|
|
35
|
+
queue.add(startId, emptyEnv, basePrint, false);
|
|
31
36
|
// retrieve the minimum depth of all nodes to only add control dependencies if they are "part" of the current execution
|
|
32
|
-
minDepth = Math.min(minDepth, ast.idMap.get(startId
|
|
33
|
-
sliceSeedIds.add(startId
|
|
37
|
+
minDepth = Math.min(minDepth, ast.idMap.get(startId)?.info.depth ?? minDepth);
|
|
38
|
+
sliceSeedIds.add(startId);
|
|
34
39
|
}
|
|
35
40
|
}
|
|
36
41
|
while (queue.nonEmpty()) {
|
package/util/diff.d.ts
CHANGED
|
@@ -39,5 +39,17 @@ export interface GenericDifferenceInformation<Report extends WriteableDifference
|
|
|
39
39
|
readonly report: Report;
|
|
40
40
|
/** A human-readable indication of where we are (the prefix of the information if the structures differ) */
|
|
41
41
|
readonly position: string;
|
|
42
|
+
readonly config: GenericDiffConfiguration;
|
|
43
|
+
}
|
|
44
|
+
export interface GenericDiffConfiguration {
|
|
45
|
+
/**
|
|
46
|
+
* The left graph may contain more vertices and or edges than the right graph.
|
|
47
|
+
* However, those which are the same (based on their ids) have to be equal
|
|
48
|
+
*/
|
|
49
|
+
readonly rightIsSubgraph?: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Similar to {@link rightIsSubgraph}, but for the left graph.
|
|
52
|
+
*/
|
|
53
|
+
readonly leftIsSubgraph?: boolean;
|
|
42
54
|
}
|
|
43
55
|
export declare function setDifference<T, Report extends WriteableDifferenceReport = WriteableDifferenceReport>(left: ReadonlySet<T>, right: ReadonlySet<T>, info: GenericDifferenceInformation<Report>): void;
|
package/util/diff.js
CHANGED
|
@@ -15,14 +15,17 @@ function setDifference(left, right, info) {
|
|
|
15
15
|
return;
|
|
16
16
|
}
|
|
17
17
|
let message = info.position;
|
|
18
|
-
if (lWithoutR.size > 0) {
|
|
18
|
+
if (lWithoutR.size > 0 && !info.config.rightIsSubgraph) {
|
|
19
19
|
message += ` More elements in ${info.leftname}: ${JSON.stringify([...lWithoutR])}`;
|
|
20
20
|
}
|
|
21
|
-
if (rWithoutL.size > 0) {
|
|
21
|
+
if (rWithoutL.size > 0 && !info.config.leftIsSubgraph) {
|
|
22
22
|
message += lWithoutR.size > 0 ? ' and m' : 'M';
|
|
23
23
|
message += `ore in ${info.rightname}: ${JSON.stringify([...rWithoutL])}`;
|
|
24
24
|
}
|
|
25
|
-
info.
|
|
25
|
+
if ((rWithoutL.size > 0 && !info.config.leftIsSubgraph)
|
|
26
|
+
|| (lWithoutR.size > 0 && !info.config.rightIsSubgraph)) {
|
|
27
|
+
info.report.addComment(message);
|
|
28
|
+
}
|
|
26
29
|
}
|
|
27
30
|
exports.setDifference = setDifference;
|
|
28
31
|
//# sourceMappingURL=diff.js.map
|
package/util/mermaid/dfg.d.ts
CHANGED
|
@@ -13,6 +13,9 @@ interface MermaidGraph {
|
|
|
13
13
|
presentEdges: Set<string>;
|
|
14
14
|
rootGraph: DataflowGraph;
|
|
15
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Prints a {@link SourceRange|range} as a human readable string.
|
|
18
|
+
*/
|
|
16
19
|
export declare function formatRange(range: SourceRange | undefined): string;
|
|
17
20
|
interface MermaidGraphConfiguration {
|
|
18
21
|
graph: DataflowGraph;
|
package/util/mermaid/dfg.js
CHANGED
|
@@ -7,6 +7,9 @@ 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
|
+
/**
|
|
11
|
+
* Prints a {@link SourceRange|range} as a human readable string.
|
|
12
|
+
*/
|
|
10
13
|
function formatRange(range) {
|
|
11
14
|
if (range === undefined) {
|
|
12
15
|
return '??-??';
|
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.15';
|
|
7
7
|
function flowrVersion() {
|
|
8
8
|
return new semver_1.SemVer(version);
|
|
9
9
|
}
|