@eagleoutice/flowr 2.0.1 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/benchmark/slicer.d.ts +1 -0
- package/benchmark/slicer.js +69 -8
- package/benchmark/stats/print.d.ts +1 -0
- package/benchmark/stats/print.js +94 -31
- package/benchmark/stats/size-of.d.ts +3 -0
- package/benchmark/stats/size-of.js +68 -0
- package/benchmark/stats/stats.d.ts +23 -0
- package/benchmark/summarizer/data.d.ts +24 -1
- 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 +4 -2
- package/benchmark/summarizer/first-phase/process.js +120 -33
- package/benchmark/summarizer/second-phase/graph.js +7 -0
- package/benchmark/summarizer/second-phase/process.js +65 -27
- package/benchmark/summarizer/summarizer.d.ts +1 -0
- package/benchmark/summarizer/summarizer.js +23 -10
- package/cli/repl/commands/commands.js +19 -1
- package/cli/slicer-app.js +1 -1
- package/dataflow/environments/append.js +1 -2
- package/dataflow/environments/built-in.js +2 -1
- package/dataflow/environments/clone.js +1 -1
- package/dataflow/environments/diff.d.ts +1 -1
- package/dataflow/environments/diff.js +16 -18
- package/dataflow/environments/environment.d.ts +6 -8
- package/dataflow/environments/environment.js +5 -8
- package/dataflow/environments/identifier.d.ts +2 -1
- package/dataflow/environments/overwrite.js +1 -2
- package/dataflow/environments/scoping.js +1 -1
- package/dataflow/graph/diff.js +11 -6
- package/dataflow/graph/edge.d.ts +2 -3
- package/dataflow/graph/edge.js +2 -2
- package/dataflow/graph/graph.d.ts +6 -2
- package/dataflow/graph/graph.js +16 -9
- package/dataflow/graph/vertex.d.ts +2 -1
- package/dataflow/info.d.ts +10 -1
- package/dataflow/info.js +54 -2
- package/dataflow/internal/linker.d.ts +1 -1
- package/dataflow/internal/linker.js +1 -2
- package/dataflow/internal/process/functions/call/built-in/built-in-assignment.js +5 -5
- package/dataflow/internal/process/functions/call/built-in/built-in-for-loop.js +1 -1
- package/dataflow/internal/process/functions/call/built-in/built-in-function-definition.js +21 -25
- package/dataflow/internal/process/functions/call/built-in/built-in-get.js +6 -1
- package/dataflow/internal/process/functions/call/built-in/built-in-if-then-else.js +10 -8
- package/dataflow/internal/process/functions/call/built-in/built-in-logical-bin-op.d.ts +1 -0
- package/dataflow/internal/process/functions/call/built-in/built-in-logical-bin-op.js +1 -2
- package/dataflow/internal/process/functions/call/built-in/built-in-while-loop.js +1 -1
- package/dataflow/internal/process/functions/call/default-call-handling.js +1 -1
- package/dataflow/internal/process/functions/call/unnamed-call-handling.js +1 -1
- package/dataflow/internal/process/process-value.js +0 -1
- package/dataflow/processor.d.ts +2 -3
- package/package.json +5 -2
- package/r-bridge/data/data.d.ts +1 -1
- package/r-bridge/data/data.js +1 -1
- package/r-bridge/lang-4.x/ast/model/nodes/r-function-call.d.ts +2 -2
- package/r-bridge/lang-4.x/ast/model/operators.js +1 -1
- package/r-bridge/lang-4.x/ast/model/processing/decorate.js +1 -1
- package/r-bridge/lang-4.x/ast/model/processing/stateful-fold.js +1 -1
- package/r-bridge/lang-4.x/ast/model/processing/visitor.js +2 -2
- package/r-bridge/lang-4.x/ast/parser/xml/internal/functions/normalize-call.js +2 -2
- package/r-bridge/lang-4.x/ast/parser/xml/internal/operators/normalize-binary.js +1 -1
- package/r-bridge/retriever.d.ts +1 -1
- package/r-bridge/retriever.js +3 -2
- package/r-bridge/shell.js +2 -1
- package/reconstruct/reconstruct.d.ts +3 -3
- package/reconstruct/reconstruct.js +40 -41
- package/slicing/criterion/filters/all-variables.js +1 -1
- package/slicing/static/static-slicer.js +2 -2
- package/statistics/features/common-syntax-probability.js +1 -1
- package/statistics/features/supported/control-flow/control-flow.js +1 -1
- package/statistics/features/supported/defined-functions/defined-functions.js +1 -1
- package/statistics/features/supported/loops/loops.js +1 -1
- package/statistics/features/supported/used-functions/used-functions.js +1 -1
- package/util/assert.d.ts +1 -1
- package/util/mermaid/ast.js +4 -0
- package/util/mermaid/dfg.d.ts +0 -1
- package/util/mermaid/dfg.js +16 -13
- package/util/mermaid/mermaid.js +21 -1
- package/util/version.js +1 -1
|
@@ -39,7 +39,7 @@ args, rootId, data, config) {
|
|
|
39
39
|
}
|
|
40
40
|
const effectiveArgs = getEffectiveOrder(config, args);
|
|
41
41
|
const { target, source } = extractSourceAndTarget(effectiveArgs, name);
|
|
42
|
-
const { type,
|
|
42
|
+
const { type, named } = target;
|
|
43
43
|
if (type === "RSymbol" /* RType.Symbol */) {
|
|
44
44
|
const res = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, reverseOrder: !config.swapSourceAndTarget });
|
|
45
45
|
return processAssignmentToSymbol({
|
|
@@ -53,7 +53,7 @@ args, rootId, data, config) {
|
|
|
53
53
|
information: res.information,
|
|
54
54
|
});
|
|
55
55
|
}
|
|
56
|
-
else if (config.canBeReplacement && type === "RFunctionCall" /* RType.FunctionCall */ &&
|
|
56
|
+
else if (config.canBeReplacement && type === "RFunctionCall" /* RType.FunctionCall */ && named) {
|
|
57
57
|
/* as replacement functions take precedence over the lhs fn-call (i.e., `names(x) <- ...` is independent from the definition of `names`), we do not have to process the call */
|
|
58
58
|
logger_1.dataflowLogger.debug(`Assignment ${name.content} has a function call as target => replacement function ${target.lexeme}`);
|
|
59
59
|
const replacement = toReplacementSymbol(target, target.functionName.content, config.superAssignment ?? false);
|
|
@@ -132,7 +132,7 @@ function processAssignmentToSymbol({ name, source, args: [targetArg, sourceArg],
|
|
|
132
132
|
}
|
|
133
133
|
// we drop the first arg which we use to pass along arguments :D
|
|
134
134
|
const readFromSourceWritten = sourceArg.out.slice(1);
|
|
135
|
-
const readTargets = [{ nodeId:
|
|
135
|
+
const readTargets = [{ nodeId: rootId, name: name.content, controlDependencies: data.controlDependencies }, ...sourceArg.unknownReferences, ...sourceArg.in, ...targetArg.in.filter(i => i.nodeId !== target.info.id), ...readFromSourceWritten];
|
|
136
136
|
const writeTargets = [...writeNodes, ...writeNodes, ...readFromSourceWritten];
|
|
137
137
|
information.environment = (0, overwrite_1.overwriteEnvironment)(targetArg.environment, sourceArg.environment);
|
|
138
138
|
// install assigned variables in environment
|
|
@@ -152,14 +152,14 @@ function processAssignmentToSymbol({ name, source, args: [targetArg, sourceArg],
|
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
|
-
information.graph.addEdge(
|
|
155
|
+
information.graph.addEdge(rootId, targetArg.entryPoint, { type: 8 /* EdgeType.Returns */ });
|
|
156
156
|
if (quoteSource) {
|
|
157
157
|
information.graph.addEdge(rootId, source.info.id, { type: 256 /* EdgeType.NonStandardEvaluation */ });
|
|
158
158
|
}
|
|
159
159
|
return {
|
|
160
160
|
...information,
|
|
161
161
|
unknownReferences: [],
|
|
162
|
-
entryPoint:
|
|
162
|
+
entryPoint: rootId,
|
|
163
163
|
in: readTargets,
|
|
164
164
|
out: writeTargets
|
|
165
165
|
};
|
|
@@ -29,7 +29,7 @@ function processForLoop(name, args, rootId, data) {
|
|
|
29
29
|
const variable = (0, processor_1.processDataflowFor)(variableArg, data);
|
|
30
30
|
// this should not be able to exit always!
|
|
31
31
|
const originalDependency = data.controlDependencies;
|
|
32
|
-
data = { ...data, controlDependencies: [...data.controlDependencies ?? [], name.info.id] };
|
|
32
|
+
data = { ...data, controlDependencies: [...data.controlDependencies ?? [], { id: name.info.id, when: true }] };
|
|
33
33
|
let headEnvironments = (0, overwrite_1.overwriteEnvironment)(vector.environment, variable.environment);
|
|
34
34
|
const headGraph = variable.graph.mergeWith(vector.graph);
|
|
35
35
|
const writtenVariable = [...variable.unknownReferences, ...variable.in];
|
|
@@ -13,6 +13,7 @@ const overwrite_1 = require("../../../../../environments/overwrite");
|
|
|
13
13
|
const scoping_1 = require("../../../../../environments/scoping");
|
|
14
14
|
const environment_1 = require("../../../../../environments/environment");
|
|
15
15
|
const resolve_by_name_1 = require("../../../../../environments/resolve-by-name");
|
|
16
|
+
const log_1 = require("../../../../../../util/log");
|
|
16
17
|
function processFunctionDefinition(name, args, rootId, data) {
|
|
17
18
|
if (args.length < 1) {
|
|
18
19
|
logger_1.dataflowLogger.warn(`Function Definition ${name.content} does not have an argument, skipping`);
|
|
@@ -53,7 +54,7 @@ function processFunctionDefinition(name, args, rootId, data) {
|
|
|
53
54
|
tag: "use" /* VertexType.Use */,
|
|
54
55
|
id: read.nodeId,
|
|
55
56
|
environment: undefined,
|
|
56
|
-
controlDependencies:
|
|
57
|
+
controlDependencies: undefined
|
|
57
58
|
});
|
|
58
59
|
}
|
|
59
60
|
}
|
|
@@ -65,8 +66,8 @@ function processFunctionDefinition(name, args, rootId, data) {
|
|
|
65
66
|
graph: new Set(subgraph.rootIds()),
|
|
66
67
|
environment: outEnvironment
|
|
67
68
|
};
|
|
69
|
+
updateNestedFunctionClosures(subgraph, outEnvironment, name);
|
|
68
70
|
const exitPoints = body.exitPoints;
|
|
69
|
-
updateNestedFunctionClosures(exitPoints, subgraph, outEnvironment, name);
|
|
70
71
|
const graph = new graph_1.DataflowGraph(data.completeAst.idMap).mergeWith(subgraph, false);
|
|
71
72
|
graph.addVertex({
|
|
72
73
|
tag: "function-definition" /* VertexType.FunctionDefinition */,
|
|
@@ -88,33 +89,28 @@ function processFunctionDefinition(name, args, rootId, data) {
|
|
|
88
89
|
};
|
|
89
90
|
}
|
|
90
91
|
exports.processFunctionDefinition = processFunctionDefinition;
|
|
91
|
-
function updateNestedFunctionClosures(
|
|
92
|
-
// track *all* function definitions - including those nested within the current graph
|
|
92
|
+
function updateNestedFunctionClosures(subgraph, outEnvironment, name) {
|
|
93
|
+
// track *all* function definitions - including those nested within the current graph,
|
|
93
94
|
// try to resolve their 'in' by only using the lowest scope which will be popped after this definition
|
|
94
|
-
for (const [id,
|
|
95
|
-
if (
|
|
95
|
+
for (const [id, { subflow, tag }] of subgraph.vertices(true)) {
|
|
96
|
+
if (tag !== "function-definition" /* VertexType.FunctionDefinition */) {
|
|
96
97
|
continue;
|
|
97
98
|
}
|
|
98
|
-
const ingoingRefs =
|
|
99
|
-
const remainingIn =
|
|
99
|
+
const ingoingRefs = subflow.in;
|
|
100
|
+
const remainingIn = [];
|
|
100
101
|
for (const ingoing of ingoingRefs) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
110
|
-
logger_1.dataflowLogger.trace(`Found ${resolved.length} references to open ref ${id} in closure of function definition ${name.info.id}`);
|
|
111
|
-
for (const ref of resolved) {
|
|
112
|
-
subgraph.addEdge(ingoing, ref, { type: 1 /* EdgeType.Reads */ });
|
|
113
|
-
}
|
|
102
|
+
const resolved = ingoing.name ? (0, resolve_by_name_1.resolveByName)(ingoing.name, outEnvironment) : undefined;
|
|
103
|
+
if (resolved === undefined) {
|
|
104
|
+
remainingIn.push(ingoing);
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
(0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Found ${resolved.length} references to open ref ${id} in closure of function definition ${name.info.id}`);
|
|
108
|
+
for (const ref of resolved) {
|
|
109
|
+
subgraph.addEdge(ingoing, ref, { type: 1 /* EdgeType.Reads */ });
|
|
114
110
|
}
|
|
115
111
|
}
|
|
116
|
-
logger_1.dataflowLogger
|
|
117
|
-
|
|
112
|
+
(0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Keeping ${remainingIn.length} references to open ref ${id} in closure of function definition ${name.info.id}`);
|
|
113
|
+
subflow.in = remainingIn;
|
|
118
114
|
}
|
|
119
115
|
}
|
|
120
116
|
function prepareFunctionEnvironment(data) {
|
|
@@ -127,11 +123,11 @@ function prepareFunctionEnvironment(data) {
|
|
|
127
123
|
/**
|
|
128
124
|
* Within something like `f <- function(a=b, m=3) { b <- 1; a; b <- 5; a + 1 }`
|
|
129
125
|
* `a` will be defined by `b` and `b`will be a promise object bound by the first definition of b it can find.
|
|
130
|
-
* This means
|
|
126
|
+
* This means that this function returns `2` due to the first `b <- 1` definition.
|
|
131
127
|
* If the code is `f <- function(a=b, m=3) { if(m > 3) { b <- 1; }; a; b <- 5; a + 1 }`, we need a link to `b <- 1` and `b <- 6`
|
|
132
128
|
* as `b` can be defined by either one of them.
|
|
133
129
|
* <p>
|
|
134
|
-
* <b>Currently we may be unable to narrow down every definition within the body as we have not implemented ways to track what covers
|
|
130
|
+
* <b>Currently we may be unable to narrow down every definition within the body as we have not implemented ways to track what covers the first definitions precisely</b>
|
|
135
131
|
*/
|
|
136
132
|
function findPromiseLinkagesForParameters(parameters, readInParameters, parameterEnvs, body) {
|
|
137
133
|
// first, we try to bind again within parameters - if we have it, fine
|
|
@@ -24,12 +24,17 @@ function processGet(name, args, rootId, data) {
|
|
|
24
24
|
location: retrieve.location,
|
|
25
25
|
namespace: undefined
|
|
26
26
|
};
|
|
27
|
-
const { information } = (0, known_call_handling_1.processKnownFunctionCall)({
|
|
27
|
+
const { information, processedArguments } = (0, known_call_handling_1.processKnownFunctionCall)({
|
|
28
28
|
name,
|
|
29
29
|
args: (0, make_argument_1.wrapArgumentsUnnamed)([treatTargetAsSymbol], data.completeAst.idMap),
|
|
30
30
|
rootId,
|
|
31
31
|
data
|
|
32
32
|
});
|
|
33
|
+
const firstArg = processedArguments[0];
|
|
34
|
+
if (firstArg) {
|
|
35
|
+
// get 'reads' its first argument
|
|
36
|
+
information.graph.addEdge(rootId, firstArg.entryPoint, { type: 1 /* EdgeType.Reads */ });
|
|
37
|
+
}
|
|
33
38
|
return information;
|
|
34
39
|
}
|
|
35
40
|
exports.processGet = processGet;
|
|
@@ -57,21 +57,23 @@ function processIfThenElse(name, args, rootId, data) {
|
|
|
57
57
|
const thenEnvironment = then?.environment ?? cond.environment;
|
|
58
58
|
// if there is no "else" case, we have to recover whatever we had before as it may be not executed
|
|
59
59
|
const finalEnvironment = (0, append_1.appendEnvironment)(thenEnvironment, otherwise ? otherwise.environment : cond.environment);
|
|
60
|
+
const cdTrue = { id: rootId, when: true };
|
|
61
|
+
const cdFalse = { id: rootId, when: false };
|
|
60
62
|
// again within an if-then-else we consider all actives to be read
|
|
61
63
|
const ingoing = [
|
|
62
64
|
...cond.in,
|
|
63
|
-
...(makeThenMaybe ? (0, environment_1.makeAllMaybe)(then?.in, nextGraph, finalEnvironment, false,
|
|
64
|
-
...(makeOtherwiseMaybe ? (0, environment_1.makeAllMaybe)(otherwise?.in, nextGraph, finalEnvironment, false,
|
|
65
|
+
...(makeThenMaybe ? (0, environment_1.makeAllMaybe)(then?.in, nextGraph, finalEnvironment, false, cdTrue) : then?.in ?? []),
|
|
66
|
+
...(makeOtherwiseMaybe ? (0, environment_1.makeAllMaybe)(otherwise?.in, nextGraph, finalEnvironment, false, cdFalse) : otherwise?.in ?? []),
|
|
65
67
|
...cond.unknownReferences,
|
|
66
|
-
...(makeThenMaybe ? (0, environment_1.makeAllMaybe)(then?.unknownReferences, nextGraph, finalEnvironment, false,
|
|
67
|
-
...(makeOtherwiseMaybe ? (0, environment_1.makeAllMaybe)(otherwise?.unknownReferences, nextGraph, finalEnvironment, false,
|
|
68
|
+
...(makeThenMaybe ? (0, environment_1.makeAllMaybe)(then?.unknownReferences, nextGraph, finalEnvironment, false, cdTrue) : then?.unknownReferences ?? []),
|
|
69
|
+
...(makeOtherwiseMaybe ? (0, environment_1.makeAllMaybe)(otherwise?.unknownReferences, nextGraph, finalEnvironment, false, cdFalse) : otherwise?.unknownReferences ?? []),
|
|
68
70
|
];
|
|
69
71
|
// we assign all with a maybe marker
|
|
70
72
|
// we do not merge even if they appear in both branches because the maybe links will refer to different ids
|
|
71
73
|
const outgoing = [
|
|
72
74
|
...cond.out,
|
|
73
|
-
...(makeThenMaybe ? (0, environment_1.makeAllMaybe)(then?.out, nextGraph, finalEnvironment, true,
|
|
74
|
-
...(makeOtherwiseMaybe ? (0, environment_1.makeAllMaybe)(otherwise?.out, nextGraph, finalEnvironment, true,
|
|
75
|
+
...(makeThenMaybe ? (0, environment_1.makeAllMaybe)(then?.out, nextGraph, finalEnvironment, true, cdTrue) : then?.out ?? []),
|
|
76
|
+
...(makeOtherwiseMaybe ? (0, environment_1.makeAllMaybe)(otherwise?.out, nextGraph, finalEnvironment, true, cdFalse) : otherwise?.out ?? []),
|
|
75
77
|
];
|
|
76
78
|
(0, common_1.patchFunctionCall)({
|
|
77
79
|
nextGraph,
|
|
@@ -83,8 +85,8 @@ function processIfThenElse(name, args, rootId, data) {
|
|
|
83
85
|
// as an if always evaluates its condition, we add a 'reads'-edge
|
|
84
86
|
nextGraph.addEdge(name.info.id, cond.entryPoint, { type: 1 /* EdgeType.Reads */ });
|
|
85
87
|
const exitPoints = [
|
|
86
|
-
...(then?.exitPoints ?? []).map(e => ({ ...e, controlDependencies: makeThenMaybe ? [...data.controlDependencies ?? []] : e.controlDependencies })),
|
|
87
|
-
...(otherwise?.exitPoints ?? []).map(e => ({ ...e, controlDependencies: makeOtherwiseMaybe ? [...data.controlDependencies ?? []] : e.controlDependencies }))
|
|
88
|
+
...(then?.exitPoints ?? []).map(e => ({ ...e, controlDependencies: makeThenMaybe ? [...data.controlDependencies ?? [], { id: rootId, when: true }] : e.controlDependencies })),
|
|
89
|
+
...(otherwise?.exitPoints ?? []).map(e => ({ ...e, controlDependencies: makeOtherwiseMaybe ? [...data.controlDependencies ?? [], { id: rootId, when: false }] : e.controlDependencies }))
|
|
88
90
|
];
|
|
89
91
|
return {
|
|
90
92
|
unknownReferences: [],
|
|
@@ -6,4 +6,5 @@ import type { RSymbol } from '../../../../../../r-bridge/lang-4.x/ast/model/node
|
|
|
6
6
|
import type { NodeId } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/node-id';
|
|
7
7
|
export declare function processSpecialBinOp<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly RFunctionArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>, config: {
|
|
8
8
|
lazy: boolean;
|
|
9
|
+
evalRhsWhen: boolean;
|
|
9
10
|
}): DataflowInformation;
|
|
@@ -14,8 +14,7 @@ function processSpecialBinOp(name, args, rootId, data, config) {
|
|
|
14
14
|
const { information, processedArguments } = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data,
|
|
15
15
|
patchData: (d, i) => {
|
|
16
16
|
if (i === 1) {
|
|
17
|
-
|
|
18
|
-
return { ...d, controlDependencies: [...d.controlDependencies ?? [], name.info.id] };
|
|
17
|
+
return { ...d, controlDependencies: [...d.controlDependencies ?? [], { id: name.info.id, when: config.evalRhsWhen }] };
|
|
19
18
|
}
|
|
20
19
|
return d;
|
|
21
20
|
}
|
|
@@ -28,7 +28,7 @@ function processWhileLoop(name, args, rootId, data) {
|
|
|
28
28
|
markAsNSE: [1],
|
|
29
29
|
patchData: (d, i) => {
|
|
30
30
|
if (i === 1) {
|
|
31
|
-
return { ...d, controlDependencies: [...d.controlDependencies ?? [], name.info.id] };
|
|
31
|
+
return { ...d, controlDependencies: [...d.controlDependencies ?? [], { id: name.info.id, when: true }] };
|
|
32
32
|
}
|
|
33
33
|
return d;
|
|
34
34
|
}
|
|
@@ -4,7 +4,7 @@ exports.processFunctionCall = void 0;
|
|
|
4
4
|
const named_call_handling_1 = require("./named-call-handling");
|
|
5
5
|
const unnamed_call_handling_1 = require("./unnamed-call-handling");
|
|
6
6
|
function processFunctionCall(functionCall, data) {
|
|
7
|
-
if (functionCall.
|
|
7
|
+
if (functionCall.named) {
|
|
8
8
|
return (0, named_call_handling_1.processNamedCall)(functionCall.functionName, functionCall.arguments, functionCall.info.id, data);
|
|
9
9
|
}
|
|
10
10
|
else {
|
|
@@ -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/dataflow/processor.d.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Based on a two-way fold, this processor will automatically supply scope information
|
|
3
3
|
*/
|
|
4
|
-
import type { DataflowInformation } from './info';
|
|
4
|
+
import type { ControlDependency, DataflowInformation } from './info';
|
|
5
5
|
import type { NormalizedAst, ParentInformation, RNodeWithParent } from '../r-bridge/lang-4.x/ast/model/processing/decorate';
|
|
6
6
|
import type { REnvironmentInformation } from './environments/environment';
|
|
7
7
|
import type { RParseRequest } from '../r-bridge/retriever';
|
|
8
|
-
import type { NodeId } from '../r-bridge/lang-4.x/ast/model/processing/node-id';
|
|
9
8
|
import type { RNode } from '../r-bridge/lang-4.x/ast/model/model';
|
|
10
9
|
export interface DataflowProcessorInformation<OtherInfo> {
|
|
11
10
|
/**
|
|
@@ -33,7 +32,7 @@ export interface DataflowProcessorInformation<OtherInfo> {
|
|
|
33
32
|
/**
|
|
34
33
|
* The chain of control-flow {@link NodeId}s that lead to the current node (e.g. of known ifs).
|
|
35
34
|
*/
|
|
36
|
-
readonly controlDependencies:
|
|
35
|
+
readonly controlDependencies: ControlDependency[] | undefined;
|
|
37
36
|
}
|
|
38
37
|
export type DataflowProcessor<OtherInfo, NodeType extends RNodeWithParent<OtherInfo>> = (node: NodeType, data: DataflowProcessorInformation<OtherInfo>) => DataflowInformation;
|
|
39
38
|
type NodeWithKey<OtherInfo, Key> = RNode<OtherInfo & ParentInformation> & {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eagleoutice/flowr",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.3",
|
|
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": {
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"url": "https://github.com/Code-Inspect/flowr/issues"
|
|
13
13
|
},
|
|
14
14
|
"scripts": {
|
|
15
|
-
"main": "npm run build && node dist/src/cli/flowr.js",
|
|
15
|
+
"main": "npm run build:bundle-flowr && node dist/src/cli/flowr.min.js",
|
|
16
16
|
"publish-library": "cp .npmignore package.json README.md LICENSE dist/src/ && cd dist/src && npm publish --access public",
|
|
17
17
|
"release": "npx release-it --ci",
|
|
18
18
|
"stats": "ts-node src/cli/statistics-app.ts",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"export-quads": "ts-node src/cli/export-quads-app.ts",
|
|
25
25
|
"capabilities-markdown": "ts-node src/r-bridge/data/print.ts",
|
|
26
26
|
"build": "tsc --project .",
|
|
27
|
+
"build:bundle-flowr": "npm run build && esbuild --bundle dist/src/cli/flowr.js --platform=node --bundle --minify --target=node18 --outfile=dist/src/cli/flowr.min.js",
|
|
27
28
|
"lint-local": "npx eslint --version && npx eslint src/ test/ --rule \"no-warning-comments: off\"",
|
|
28
29
|
"lint": "npm run license-compat -- --summary && npx eslint --version && npx eslint src/ test/",
|
|
29
30
|
"license-compat": "license-checker --onlyAllow 'MIT;MIT OR X11;GPLv2;LGPL;GNUGPL;ISC;Apache-2.0;FreeBSD;BSD-2-Clause;clearbsd;ModifiedBSD;BSD-3-Clause;Python-2.0;Unlicense;WTFPL;BlueOak-1.0.0;CC-BY-4.0;CC-BY-3.0;CC0-1.0;0BSD'",
|
|
@@ -200,6 +201,7 @@
|
|
|
200
201
|
"@typescript-eslint/eslint-plugin": "^7.8.0",
|
|
201
202
|
"chai": "^4.3.16",
|
|
202
203
|
"chai-as-promised": "^7.1.1",
|
|
204
|
+
"esbuild": "0.21.3",
|
|
203
205
|
"eslint": "^8.57.0",
|
|
204
206
|
"eslint-plugin-check-file": "^2.8.0",
|
|
205
207
|
"eslint-plugin-import": "^2.29.1",
|
|
@@ -226,6 +228,7 @@
|
|
|
226
228
|
"n-readlines": "^1.0.1",
|
|
227
229
|
"n3": "^1.17.2",
|
|
228
230
|
"object-hash": "^3.0.0",
|
|
231
|
+
"object-sizeof": "^2.6.4",
|
|
229
232
|
"rotating-file-stream": "^3.1.1",
|
|
230
233
|
"semver": "^7.5.4",
|
|
231
234
|
"tar": "^7.1.0",
|
package/r-bridge/data/data.d.ts
CHANGED
|
@@ -373,7 +373,7 @@ export declare const flowrCapabilities: {
|
|
|
373
373
|
readonly name: "Sequencing";
|
|
374
374
|
readonly id: "built-in-sequencing";
|
|
375
375
|
readonly supported: "not";
|
|
376
|
-
readonly description: "_Handle `:`, `seq`, ...
|
|
376
|
+
readonly description: "_Handle `:`, `seq`, ... by gathering value information using abstract interpretation._";
|
|
377
377
|
}, {
|
|
378
378
|
readonly name: "Internal and Primitive Functions";
|
|
379
379
|
readonly id: "built-in-internal-and-primitive-functions";
|
package/r-bridge/data/data.js
CHANGED
|
@@ -464,7 +464,7 @@ exports.flowrCapabilities = {
|
|
|
464
464
|
name: 'Sequencing',
|
|
465
465
|
id: 'built-in-sequencing',
|
|
466
466
|
supported: 'not',
|
|
467
|
-
description: '_Handle `:`, `seq`, ...
|
|
467
|
+
description: '_Handle `:`, `seq`, ... by gathering value information using abstract interpretation._'
|
|
468
468
|
},
|
|
469
469
|
{
|
|
470
470
|
name: 'Internal and Primitive Functions',
|
|
@@ -11,7 +11,7 @@ export type RFunctionArgument<Info = NoInfo> = RArgument<Info> | typeof EmptyArg
|
|
|
11
11
|
*/
|
|
12
12
|
export interface RNamedFunctionCall<Info = NoInfo> extends Base<Info>, Location {
|
|
13
13
|
readonly type: RType.FunctionCall;
|
|
14
|
-
readonly
|
|
14
|
+
readonly named: true;
|
|
15
15
|
functionName: RSymbol<Info>;
|
|
16
16
|
/** arguments can be empty, for example when calling as `a(1, ,3)` */
|
|
17
17
|
readonly arguments: readonly RFunctionArgument<Info>[];
|
|
@@ -23,7 +23,7 @@ export interface RNamedFunctionCall<Info = NoInfo> extends Base<Info>, Location
|
|
|
23
23
|
*/
|
|
24
24
|
export interface RUnnamedFunctionCall<Info = NoInfo> extends Base<Info>, Location {
|
|
25
25
|
readonly type: RType.FunctionCall;
|
|
26
|
-
readonly
|
|
26
|
+
readonly named: false | undefined;
|
|
27
27
|
calledFunction: RNode<Info>;
|
|
28
28
|
/** marks function calls like `3 %xx% 4` which have been written in special infix notation; deprecated in v2 */
|
|
29
29
|
infixSpecial?: boolean;
|
|
@@ -41,7 +41,7 @@ exports.OperatorDatabase = {
|
|
|
41
41
|
'=': { name: 'equal assignment', stringUsedInRAst: "EQ_ASSIGN" /* RawRType.EqualAssign */, stringUsedInternally: '=', writtenAs: 'infix', arity: 2 /* OperatorArity.Binary */, usedAs: 'assignment', capabilities: ['binary-operator', 'infix-calls', 'assignment-functions', 'local-equal-assignment', 'function-calls'] },
|
|
42
42
|
/* others */
|
|
43
43
|
/* maybe introduce custom in-r-ast flavor for these? we consider it arithmetic, as it works on numbers => if we change this we have to create custom tests! (with arithmetic, there is the automatic test set) */
|
|
44
|
-
':': { name: 'sequence operator', stringUsedInRAst: ":" /* RawRType.Colon */, stringUsedInternally: ':', writtenAs: 'infix', arity: 2 /* OperatorArity.Binary */, usedAs: 'operation', capabilities: ['binary-operator', 'infix-calls', 'function-calls'
|
|
44
|
+
':': { name: 'sequence operator', stringUsedInRAst: ":" /* RawRType.Colon */, stringUsedInternally: ':', writtenAs: 'infix', arity: 2 /* OperatorArity.Binary */, usedAs: 'operation', capabilities: ['binary-operator', 'infix-calls', 'function-calls'] },
|
|
45
45
|
'?': { name: 'question', stringUsedInRAst: "?" /* RawRType.Question */, stringUsedInternally: '?', writtenAs: 'prefix', arity: 1 /* OperatorArity.Unary */, usedAs: 'operation', capabilities: ['unary-operator', 'built-in-help'] }
|
|
46
46
|
};
|
|
47
47
|
/* eslint-enable */
|
|
@@ -269,7 +269,7 @@ function createFoldForFunctionCall(info) {
|
|
|
269
269
|
return (data, functionName, args, depth) => {
|
|
270
270
|
const id = info.getId(data);
|
|
271
271
|
let decorated;
|
|
272
|
-
if (data.
|
|
272
|
+
if (data.named) {
|
|
273
273
|
decorated = { ...data, info: { ...data.info, id, parent: undefined, depth }, functionName, arguments: args };
|
|
274
274
|
}
|
|
275
275
|
else {
|
|
@@ -37,7 +37,7 @@ function foldAstStateful(ast, down, folds) {
|
|
|
37
37
|
case "RRepeatLoop" /* RType.RepeatLoop */:
|
|
38
38
|
return folds.loop.foldRepeat(ast, foldAstStateful(ast.body, down, folds), down);
|
|
39
39
|
case "RFunctionCall" /* RType.FunctionCall */:
|
|
40
|
-
return folds.functions.foldFunctionCall(ast, foldAstStateful(ast.
|
|
40
|
+
return folds.functions.foldFunctionCall(ast, foldAstStateful(ast.named ? ast.functionName : ast.calledFunction, down, folds), ast.arguments.map(param => param === r_function_call_1.EmptyArgument ? param : foldAstStateful(param, down, folds)), down);
|
|
41
41
|
case "RFunctionDefinition" /* RType.FunctionDefinition */:
|
|
42
42
|
return folds.functions.foldFunctionDefinition(ast, ast.parameters.map(param => foldAstStateful(param, down, folds)), foldAstStateful(ast.body, down, folds), down);
|
|
43
43
|
case "RParameter" /* RType.Parameter */:
|
|
@@ -15,11 +15,11 @@ class NodeVisitor {
|
|
|
15
15
|
if (this.onEnter?.(node)) {
|
|
16
16
|
return;
|
|
17
17
|
}
|
|
18
|
-
/* let the type system know
|
|
18
|
+
/* let the type system know that the type does not change */
|
|
19
19
|
const type = node.type;
|
|
20
20
|
switch (type) {
|
|
21
21
|
case "RFunctionCall" /* RType.FunctionCall */:
|
|
22
|
-
this.visitSingle(node.
|
|
22
|
+
this.visitSingle(node.named ? node.functionName : node.calledFunction);
|
|
23
23
|
this.visit(node.arguments);
|
|
24
24
|
break;
|
|
25
25
|
case "RFunctionDefinition" /* RType.FunctionDefinition */:
|
|
@@ -96,7 +96,7 @@ function tryParseUnnamedFunctionCall(data, mappedWithName, location, content) {
|
|
|
96
96
|
}
|
|
97
97
|
return {
|
|
98
98
|
type: "RFunctionCall" /* RType.FunctionCall */,
|
|
99
|
-
|
|
99
|
+
named: undefined,
|
|
100
100
|
location,
|
|
101
101
|
lexeme: content,
|
|
102
102
|
calledFunction: calledFunction,
|
|
@@ -129,7 +129,7 @@ function parseNamedFunctionCall(data, symbolContent, mappedWithName, location, c
|
|
|
129
129
|
const parsedArguments = parseArguments(mappedWithName, data);
|
|
130
130
|
return {
|
|
131
131
|
type: "RFunctionCall" /* RType.FunctionCall */,
|
|
132
|
-
|
|
132
|
+
named: true,
|
|
133
133
|
location,
|
|
134
134
|
lexeme: content,
|
|
135
135
|
functionName,
|
|
@@ -37,7 +37,7 @@ function parseBinaryOp(data, lhs, operator, rhs) {
|
|
|
37
37
|
// parse as infix function call!
|
|
38
38
|
return {
|
|
39
39
|
type: "RFunctionCall" /* RType.FunctionCall */,
|
|
40
|
-
|
|
40
|
+
named: true,
|
|
41
41
|
infixSpecial: true,
|
|
42
42
|
lexeme: data.currentLexeme ?? content,
|
|
43
43
|
location,
|
package/r-bridge/retriever.d.ts
CHANGED
|
@@ -49,4 +49,4 @@ export declare function removeRQuotes(str: string): string;
|
|
|
49
49
|
/**
|
|
50
50
|
* Needs to be called *after* {@link retrieveParseDataFromRCode} (or {@link retrieveNormalizedAstFromRCode})
|
|
51
51
|
*/
|
|
52
|
-
export declare function retrieveNumberOfRTokensOfLastParse(shell: RShell): Promise<number>;
|
|
52
|
+
export declare function retrieveNumberOfRTokensOfLastParse(shell: RShell, ignoreComments?: boolean): Promise<number>;
|
package/r-bridge/retriever.js
CHANGED
|
@@ -95,8 +95,9 @@ exports.removeRQuotes = removeRQuotes;
|
|
|
95
95
|
/**
|
|
96
96
|
* Needs to be called *after* {@link retrieveParseDataFromRCode} (or {@link retrieveNormalizedAstFromRCode})
|
|
97
97
|
*/
|
|
98
|
-
async function retrieveNumberOfRTokensOfLastParse(shell) {
|
|
99
|
-
const
|
|
98
|
+
async function retrieveNumberOfRTokensOfLastParse(shell, ignoreComments = false) {
|
|
99
|
+
const rows = ignoreComments ? `flowr_output[flowr_output$token != "${"COMMENT" /* RawRType.Comment */}", ]` : 'flowr_output';
|
|
100
|
+
const result = await shell.sendCommandWithOutput(`cat(nrow(${rows}),${(0, convert_values_1.ts2r)(shell.options.eol)})`);
|
|
100
101
|
(0, assert_1.guard)(result.length === 1, () => `expected exactly one line to obtain the number of R tokens, but got: ${JSON.stringify(result)}`);
|
|
101
102
|
return Number(result[0]);
|
|
102
103
|
}
|
package/r-bridge/shell.js
CHANGED
|
@@ -196,7 +196,8 @@ class RShell {
|
|
|
196
196
|
*/
|
|
197
197
|
clearEnvironment() {
|
|
198
198
|
this.log.debug('clearing environment');
|
|
199
|
-
|
|
199
|
+
// run rm(list=ls()) but ignore 'flowr_get_ast', which is the compile command installed
|
|
200
|
+
this._sendCommand('rm(list=setdiff(ls(), "flowr_get_ast"))');
|
|
200
201
|
}
|
|
201
202
|
/**
|
|
202
203
|
* Obtain the temporary directory used by R.
|
|
@@ -14,8 +14,8 @@ export declare function doNotAutoSelect(_node: RNode<ParentInformation>): boolea
|
|
|
14
14
|
export declare function autoSelectLibrary(node: RNode<ParentInformation>): boolean;
|
|
15
15
|
export interface ReconstructionResult {
|
|
16
16
|
code: string;
|
|
17
|
-
/** number of nodes that triggered the `autoSelectIf` predicate {@link reconstructToCode} */
|
|
18
|
-
|
|
17
|
+
/** number of lines that contain nodes that triggered the `autoSelectIf` predicate {@link reconstructToCode} */
|
|
18
|
+
linesWithAutoSelected: number;
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
21
|
* Reconstructs parts of a normalized R ast into R code on an expression basis.
|
|
@@ -24,7 +24,7 @@ export interface ReconstructionResult {
|
|
|
24
24
|
* @param selection - The selection of nodes to be reconstructed (probably the {@link NodeId|NodeIds} identified by the slicer)
|
|
25
25
|
* @param autoSelectIf - A predicate that can be used to force the reconstruction of a node (for example to reconstruct library call statements, see {@link autoSelectLibrary}, {@link doNotAutoSelect})
|
|
26
26
|
*
|
|
27
|
-
* @returns The number of
|
|
27
|
+
* @returns The number of lines for which `autoSelectIf` triggered, as well as the reconstructed code itself.
|
|
28
28
|
*/
|
|
29
29
|
export declare function reconstructToCode<Info>(ast: NormalizedAst<Info>, selection: Selection, autoSelectIf?: AutoSelectPredicate): ReconstructionResult;
|
|
30
30
|
export {};
|