@eagleoutice/flowr 2.2.9 → 2.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/cli/repl/server/compact.d.ts +2 -2
- package/cli/repl/server/compact.js +3 -3
- package/cli/repl/server/messages/message-analysis.d.ts +2 -2
- package/cli/repl/server/messages/message-analysis.js +2 -2
- package/config.d.ts +9 -0
- package/config.js +8 -2
- package/dataflow/environments/default-builtin-config.js +15 -2
- package/dataflow/environments/environment.js +4 -2
- package/dataflow/extractor.d.ts +1 -1
- package/dataflow/extractor.js +8 -6
- package/dataflow/graph/edge.js +4 -1
- package/dataflow/graph/graph.d.ts +6 -0
- package/dataflow/graph/graph.js +13 -0
- package/dataflow/graph/resolve-graph.js +8 -0
- package/dataflow/internal/process/functions/call/argument/unpack-argument.d.ts +3 -0
- package/dataflow/internal/process/functions/call/argument/unpack-argument.js +4 -10
- package/dataflow/internal/process/functions/call/built-in/built-in-assignment.d.ts +1 -0
- package/dataflow/internal/process/functions/call/built-in/built-in-assignment.js +7 -9
- package/dataflow/internal/process/functions/call/built-in/built-in-expression-list.js +4 -3
- package/dataflow/internal/process/functions/call/built-in/built-in-pipe.js +21 -3
- package/dataflow/internal/process/functions/call/built-in/built-in-source.js +4 -2
- package/dataflow/processor.d.ts +7 -7
- package/documentation/data/server/doc-data-server-messages.js +2 -2
- package/documentation/print-interface-wiki.js +3 -0
- package/documentation/print-query-wiki.js +20 -1
- package/package.json +3 -3
- package/queries/catalog/call-context-query/call-context-query-executor.js +13 -0
- package/queries/catalog/call-context-query/call-context-query-format.d.ts +4 -0
- package/queries/catalog/call-context-query/call-context-query-format.js +1 -0
- package/queries/catalog/location-map-query/location-map-query-executor.d.ts +1 -1
- package/queries/catalog/location-map-query/location-map-query-executor.js +38 -3
- package/queries/catalog/location-map-query/location-map-query-format.d.ts +10 -1
- package/queries/catalog/location-map-query/location-map-query-format.js +5 -1
- package/queries/catalog/project-query/project-query-executor.d.ts +3 -0
- package/queries/catalog/project-query/project-query-executor.js +17 -0
- package/queries/catalog/project-query/project-query-format.d.ts +67 -0
- package/queries/catalog/project-query/project-query-format.js +26 -0
- package/queries/query.d.ts +60 -1
- package/queries/query.js +3 -1
- package/slicing/static/fingerprint.js +8 -1
- package/slicing/static/slice-call.d.ts +1 -1
- package/slicing/static/slice-call.js +5 -16
- package/slicing/static/slicer-types.d.ts +2 -0
- package/slicing/static/static-slicer.d.ts +4 -2
- package/slicing/static/static-slicer.js +24 -18
- package/slicing/static/visiting-queue.d.ts +7 -1
- package/slicing/static/visiting-queue.js +20 -6
- package/util/version.js +1 -1
package/README.md
CHANGED
|
@@ -48,7 +48,7 @@ It offers a wide variety of features, for example:
|
|
|
48
48
|
|
|
49
49
|
```shell
|
|
50
50
|
$ docker run -it --rm eagleoutice/flowr # or npm run flowr
|
|
51
|
-
flowR repl using flowR v2.2.
|
|
51
|
+
flowR repl using flowR v2.2.10, R v4.4.0 (r-shell engine)
|
|
52
52
|
R> :slicer test/testfiles/example.R --criterion "11@sum"
|
|
53
53
|
```
|
|
54
54
|
|
|
@@ -95,7 +95,7 @@ It offers a wide variety of features, for example:
|
|
|
95
95
|
|
|
96
96
|
|
|
97
97
|
* 🚀 **fast data- and control-flow graphs**\
|
|
98
|
-
Within just <i><span title="This measurement is automatically fetched from the latest benchmark!">
|
|
98
|
+
Within just <i><span title="This measurement is automatically fetched from the latest benchmark!">119.1 ms</span></i> (as of Feb 23, 2025),
|
|
99
99
|
_flowR_ can analyze the data- and control-flow of the average real-world R script. See the [benchmarks](https://flowr-analysis.github.io/flowr/wiki/stats/benchmark) for more information,
|
|
100
100
|
and consult the [wiki pages](https://github.com/flowr-analysis/flowr/wiki/Dataflow-Graph) for more details on the dataflow graph.
|
|
101
101
|
|
|
@@ -131,7 +131,7 @@ It offers a wide variety of features, for example:
|
|
|
131
131
|
|
|
132
132
|
```shell
|
|
133
133
|
$ docker run -it --rm eagleoutice/flowr # or npm run flowr
|
|
134
|
-
flowR repl using flowR v2.2.
|
|
134
|
+
flowR repl using flowR v2.2.10, R v4.4.0 (r-shell engine)
|
|
135
135
|
R> :dataflow* test/testfiles/example.R
|
|
136
136
|
```
|
|
137
137
|
|
|
@@ -377,7 +377,7 @@ It offers a wide variety of features, for example:
|
|
|
377
377
|
```
|
|
378
378
|
|
|
379
379
|
|
|
380
|
-
(The analysis required
|
|
380
|
+
(The analysis required _21.37 ms_ (including parse and normalize, using the [r-shell](https://github.com/flowr-analysis/flowr/wiki/Engines) engine) within the generation environment.)
|
|
381
381
|
|
|
382
382
|
|
|
383
383
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare function compact(obj: unknown):
|
|
2
|
-
export declare function uncompact(buf:
|
|
1
|
+
export declare function compact(obj: unknown): string;
|
|
2
|
+
export declare function uncompact(buf: string): unknown;
|
|
@@ -3,11 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.compact = compact;
|
|
4
4
|
exports.uncompact = uncompact;
|
|
5
5
|
const json_1 = require("../../../util/json");
|
|
6
|
-
const
|
|
6
|
+
const lz_string_1 = require("lz-string");
|
|
7
7
|
function compact(obj) {
|
|
8
|
-
return
|
|
8
|
+
return (0, lz_string_1.compressToUTF16)(JSON.stringify(obj, json_1.jsonReplacer));
|
|
9
9
|
}
|
|
10
10
|
function uncompact(buf) {
|
|
11
|
-
return (0,
|
|
11
|
+
return JSON.parse((0, lz_string_1.decompressFromUTF16)(buf));
|
|
12
12
|
}
|
|
13
13
|
//# sourceMappingURL=compact.js.map
|
|
@@ -69,11 +69,11 @@ export interface FileAnalysisResponseMessageCompact extends IdMessageBase {
|
|
|
69
69
|
/**
|
|
70
70
|
* See the {@link PipelineExecutor} and {@link DEFAULT_DATAFLOW_PIPELINE} for details on the results.
|
|
71
71
|
*/
|
|
72
|
-
results:
|
|
72
|
+
results: string;
|
|
73
73
|
/**
|
|
74
74
|
* Only if the {@link FileAnalysisRequestMessage} contained a `cfg: true` this will contain the {@link ControlFlowInformation} of the file.
|
|
75
75
|
*/
|
|
76
|
-
cfg?:
|
|
76
|
+
cfg?: string;
|
|
77
77
|
}
|
|
78
78
|
/**
|
|
79
79
|
* Similar to {@link FileAnalysisResponseMessageJson} but using n-quads as serialization format.
|
|
@@ -29,8 +29,8 @@ const compactSchema = joi_1.default.object({
|
|
|
29
29
|
type: joi_1.default.string().valid('response-file-analysis').required().description('The type of the message.'),
|
|
30
30
|
id: joi_1.default.string().optional().description('The id of the message, if you passed one in the request.'),
|
|
31
31
|
format: joi_1.default.string().valid('bson').required().description('The format of the results in bson format.'),
|
|
32
|
-
results: joi_1.default.
|
|
33
|
-
cfg: joi_1.default.
|
|
32
|
+
results: joi_1.default.string().required().description('The results of the analysis (one field per step).'),
|
|
33
|
+
cfg: joi_1.default.string().optional().description('The control flow information of the file, only present if requested.')
|
|
34
34
|
});
|
|
35
35
|
const nquadsSchema = joi_1.default.object({
|
|
36
36
|
type: joi_1.default.string().valid('response-file-analysis').required().description('The type of the message.'),
|
package/config.d.ts
CHANGED
|
@@ -97,6 +97,15 @@ export interface FlowrConfigOptions extends MergeableRecord {
|
|
|
97
97
|
*/
|
|
98
98
|
readonly dropPaths: DropPathsOption;
|
|
99
99
|
};
|
|
100
|
+
/**
|
|
101
|
+
* The configuration for flowR's slicer
|
|
102
|
+
*/
|
|
103
|
+
slicer?: {
|
|
104
|
+
/**
|
|
105
|
+
* The maximum number of iterations to perform on a single function call during slicing
|
|
106
|
+
*/
|
|
107
|
+
readonly threshold?: number;
|
|
108
|
+
};
|
|
100
109
|
};
|
|
101
110
|
}
|
|
102
111
|
export interface TreeSitterEngineConfig extends MergeableRecord {
|
package/config.js
CHANGED
|
@@ -70,12 +70,15 @@ exports.defaultConfigOptions = {
|
|
|
70
70
|
defaultEngine: 'r-shell',
|
|
71
71
|
solver: {
|
|
72
72
|
variables: VariableResolve.Alias,
|
|
73
|
-
pointerTracking:
|
|
73
|
+
pointerTracking: false,
|
|
74
74
|
resolveSource: {
|
|
75
75
|
dropPaths: DropPathsOption.No,
|
|
76
76
|
ignoreCapitalization: true,
|
|
77
77
|
inferWorkingDirectory: InferWorkingDirectory.ActiveScript,
|
|
78
78
|
searchPath: []
|
|
79
|
+
},
|
|
80
|
+
slicer: {
|
|
81
|
+
threshold: 50
|
|
79
82
|
}
|
|
80
83
|
}
|
|
81
84
|
};
|
|
@@ -107,7 +110,10 @@ exports.flowrConfigFileSchema = joi_1.default.object({
|
|
|
107
110
|
ignoreCapitalization: joi_1.default.boolean().description('Search for filenames matching in the lowercase.'),
|
|
108
111
|
inferWorkingDirectory: joi_1.default.string().valid(...Object.values(InferWorkingDirectory)).description('Try to infer the working directory from the main or any script to analyze.'),
|
|
109
112
|
searchPath: joi_1.default.array().items(joi_1.default.string()).description('Additionally search in these paths.')
|
|
110
|
-
}).optional().description('If lax source calls are active, flowR searches for sourced files much more freely, based on the configurations you give it. This option is only in effect if `ignoreSourceCalls` is set to false.')
|
|
113
|
+
}).optional().description('If lax source calls are active, flowR searches for sourced files much more freely, based on the configurations you give it. This option is only in effect if `ignoreSourceCalls` is set to false.'),
|
|
114
|
+
slicer: joi_1.default.object({
|
|
115
|
+
threshold: joi_1.default.number().optional().description('The maximum number of iterations to perform on a single function call during slicing.')
|
|
116
|
+
}).optional().description('The configuration for the slicer.')
|
|
111
117
|
}).description('How to resolve constants, constraints, cells, ...')
|
|
112
118
|
}).description('The configuration file format for flowR.');
|
|
113
119
|
// we don't load from a config file at all by default unless setConfigFile is called
|
|
@@ -114,16 +114,29 @@ exports.DefaultBuiltinConfig = [
|
|
|
114
114
|
{ type: 'function', names: ['while'], processor: 'builtin:while-loop', config: {}, assumePrimitive: true },
|
|
115
115
|
{ type: 'function', names: ['do.call'], processor: 'builtin:apply', config: { indexOfFunction: 0, unquoteFunction: true }, assumePrimitive: true },
|
|
116
116
|
{ type: 'function', names: ['list'], processor: 'builtin:list', config: {}, assumePrimitive: true },
|
|
117
|
+
{
|
|
118
|
+
type: 'function',
|
|
119
|
+
names: ['setnames', 'setNames', 'setkey', 'setkeyv', 'setindex', 'setindexv', 'setattr'],
|
|
120
|
+
processor: 'builtin:assignment',
|
|
121
|
+
config: {
|
|
122
|
+
canBeReplacement: false,
|
|
123
|
+
targetVariable: false,
|
|
124
|
+
makeMaybe: true,
|
|
125
|
+
mayHaveMoreArgs: true
|
|
126
|
+
}
|
|
127
|
+
},
|
|
117
128
|
{
|
|
118
129
|
type: 'function',
|
|
119
130
|
names: [
|
|
120
|
-
'on.exit', 'sys.on.exit', 'par', '
|
|
131
|
+
'on.exit', 'sys.on.exit', 'par', 'sink',
|
|
121
132
|
/* library and require is handled above */
|
|
122
133
|
'requireNamespace', 'loadNamespace', 'attachNamespace', 'asNamespace',
|
|
123
134
|
/* downloader and installer functions (R, devtools, BiocManager) */
|
|
124
135
|
'library.dynam', 'install.packages', 'install', 'install_github', 'install_gitlab', 'install_bitbucket', 'install_url', 'install_git', 'install_svn', 'install_local', 'install_version', 'update_packages',
|
|
125
136
|
/* weird env attachments */
|
|
126
|
-
'attach', 'unname', 'data'
|
|
137
|
+
'attach', 'unname', 'data',
|
|
138
|
+
/* file creation/removal */
|
|
139
|
+
'dir.create', 'dir_create', 'Sys.chmod', 'unlink', 'file.remove', 'file.rename', 'file.copy', 'file.link', 'file.append', 'Sys.junction'
|
|
127
140
|
],
|
|
128
141
|
processor: 'builtin:default',
|
|
129
142
|
config: { hasUnknownSideEffects: true },
|
|
@@ -81,8 +81,10 @@ exports.EmptyBuiltInEnvironment = {
|
|
|
81
81
|
* See {@link EmptyBuiltInEnvironment} for the case `fullBuiltIns = false`.
|
|
82
82
|
*/
|
|
83
83
|
function initializeCleanEnvironments(fullBuiltIns = true) {
|
|
84
|
-
exports.BuiltInEnvironment.memory
|
|
85
|
-
|
|
84
|
+
if (exports.BuiltInEnvironment.memory === undefined) {
|
|
85
|
+
exports.BuiltInEnvironment.memory = built_in_1.BuiltInMemory;
|
|
86
|
+
exports.EmptyBuiltInEnvironment.memory = built_in_1.EmptyBuiltInMemory;
|
|
87
|
+
}
|
|
86
88
|
return {
|
|
87
89
|
current: new Environment(fullBuiltIns ? exports.BuiltInEnvironment : exports.EmptyBuiltInEnvironment),
|
|
88
90
|
level: 0
|
package/dataflow/extractor.d.ts
CHANGED
|
@@ -14,4 +14,4 @@ export declare const processors: DataflowProcessors<ParentInformation>;
|
|
|
14
14
|
* (e.g., in the event of a `source` call).
|
|
15
15
|
* For the actual, canonical fold entry point, see {@link processDataflowFor}.
|
|
16
16
|
*/
|
|
17
|
-
export declare function produceDataFlowGraph<OtherInfo>(parser: Parser<KnownParserType>, request: RParseRequests,
|
|
17
|
+
export declare function produceDataFlowGraph<OtherInfo>(parser: Parser<KnownParserType>, request: RParseRequests, completeAst: NormalizedAst<OtherInfo & ParentInformation>): DataflowInformation;
|
package/dataflow/extractor.js
CHANGED
|
@@ -80,9 +80,9 @@ function resolveLinkToSideEffects(ast, graph) {
|
|
|
80
80
|
* (e.g., in the event of a `source` call).
|
|
81
81
|
* For the actual, canonical fold entry point, see {@link processDataflowFor}.
|
|
82
82
|
*/
|
|
83
|
-
function produceDataFlowGraph(parser, request,
|
|
84
|
-
const multifile = Array.isArray(request);
|
|
83
|
+
function produceDataFlowGraph(parser, request, completeAst) {
|
|
85
84
|
let firstRequest;
|
|
85
|
+
const multifile = Array.isArray(request);
|
|
86
86
|
if (multifile) {
|
|
87
87
|
firstRequest = request[0];
|
|
88
88
|
}
|
|
@@ -90,23 +90,25 @@ function produceDataFlowGraph(parser, request, ast) {
|
|
|
90
90
|
firstRequest = request;
|
|
91
91
|
}
|
|
92
92
|
const dfData = {
|
|
93
|
-
parser
|
|
94
|
-
completeAst
|
|
93
|
+
parser,
|
|
94
|
+
completeAst,
|
|
95
95
|
environment: (0, environment_1.initializeCleanEnvironments)(),
|
|
96
96
|
processors: exports.processors,
|
|
97
97
|
currentRequest: firstRequest,
|
|
98
98
|
controlDependencies: undefined,
|
|
99
99
|
referenceChain: [firstRequest],
|
|
100
100
|
};
|
|
101
|
-
let df = (0, processor_1.processDataflowFor)(
|
|
101
|
+
let df = (0, processor_1.processDataflowFor)(completeAst.ast, dfData);
|
|
102
|
+
df.graph.sourced.unshift(firstRequest.request === 'file' ? firstRequest.content : '<inline>');
|
|
102
103
|
if (multifile) {
|
|
103
104
|
for (let i = 1; i < request.length; i++) {
|
|
105
|
+
/* source requests register automatically */
|
|
104
106
|
df = (0, built_in_source_1.standaloneSourceFile)(request[i], dfData, `root-${i}`, df);
|
|
105
107
|
}
|
|
106
108
|
}
|
|
107
109
|
// finally, resolve linkages
|
|
108
110
|
(0, built_in_function_definition_1.updateNestedFunctionCalls)(df.graph, df.environment);
|
|
109
|
-
resolveLinkToSideEffects(
|
|
111
|
+
resolveLinkToSideEffects(completeAst, df.graph);
|
|
110
112
|
return df;
|
|
111
113
|
}
|
|
112
114
|
//# sourceMappingURL=extractor.js.map
|
package/dataflow/graph/edge.js
CHANGED
|
@@ -98,7 +98,10 @@ function edgeDoesNotIncludeType(type, types) {
|
|
|
98
98
|
const alwaysTraverseEdgeTypes = EdgeType.Reads | EdgeType.DefinedBy | EdgeType.Argument | EdgeType.Calls;
|
|
99
99
|
const definedByOnCallTypes = EdgeType.DefinesOnCall | EdgeType.DefinedByOnCall;
|
|
100
100
|
function shouldTraverseEdge(types) {
|
|
101
|
-
if (edgeIncludesType(types,
|
|
101
|
+
if (edgeIncludesType(types, EdgeType.NonStandardEvaluation)) {
|
|
102
|
+
return 0 /* TraverseEdge.Never */;
|
|
103
|
+
}
|
|
104
|
+
else if (edgeIncludesType(types, alwaysTraverseEdgeTypes)) {
|
|
102
105
|
return 3 /* TraverseEdge.Always */;
|
|
103
106
|
}
|
|
104
107
|
else if (edgeIncludesType(types, definedByOnCallTypes)) {
|
|
@@ -73,6 +73,7 @@ export interface DataflowGraphJson {
|
|
|
73
73
|
readonly rootVertices: NodeId[];
|
|
74
74
|
readonly vertexInformation: [NodeId, DataflowGraphVertexInfo][];
|
|
75
75
|
readonly edgeInformation: [NodeId, [NodeId, DataflowGraphEdge][]][];
|
|
76
|
+
readonly sourced?: (string | '<inline>')[];
|
|
76
77
|
}
|
|
77
78
|
/**
|
|
78
79
|
* An unknown side effect describes something that we cannot handle correctly (in all cases).
|
|
@@ -103,6 +104,8 @@ export type UnknownSidEffect = NodeId | {
|
|
|
103
104
|
export declare class DataflowGraph<Vertex extends DataflowGraphVertexInfo = DataflowGraphVertexInfo, Edge extends DataflowGraphEdge = DataflowGraphEdge> {
|
|
104
105
|
private static DEFAULT_ENVIRONMENT;
|
|
105
106
|
private _idMap;
|
|
107
|
+
/** all file paths included in this dfg */
|
|
108
|
+
private _sourced;
|
|
106
109
|
private readonly _unknownSideEffects;
|
|
107
110
|
constructor(idMap: AstIdMap | undefined);
|
|
108
111
|
/** Contains the vertices of the root level graph (i.e., included those vertices from the complete graph, that are nested within function definitions) */
|
|
@@ -135,6 +138,9 @@ export declare class DataflowGraph<Vertex extends DataflowGraphVertexInfo = Data
|
|
|
135
138
|
ingoingEdges(id: NodeId): IngoingEdges | undefined;
|
|
136
139
|
/** Retrieves the id-map to the normalized AST attached to the dataflow graph */
|
|
137
140
|
get idMap(): AstIdMap | undefined;
|
|
141
|
+
get sourced(): (string | '<inline>')[];
|
|
142
|
+
/** Mark this file as being part of the dfg */
|
|
143
|
+
addFile(source: string | '<inline>'): void;
|
|
138
144
|
/**
|
|
139
145
|
* Retrieves the set of vertices which have side effects that we do not know anything about.
|
|
140
146
|
*/
|
package/dataflow/graph/graph.js
CHANGED
|
@@ -55,6 +55,8 @@ function getReferenceOfArgument(arg) {
|
|
|
55
55
|
class DataflowGraph {
|
|
56
56
|
static DEFAULT_ENVIRONMENT = undefined;
|
|
57
57
|
_idMap;
|
|
58
|
+
/** all file paths included in this dfg */
|
|
59
|
+
_sourced = [];
|
|
58
60
|
/*
|
|
59
61
|
* Set of vertices which have sideEffects that we do not know anything about.
|
|
60
62
|
* As a (temporary) solution until we have FD edges, a side effect may also store known target links
|
|
@@ -113,6 +115,13 @@ class DataflowGraph {
|
|
|
113
115
|
get idMap() {
|
|
114
116
|
return this._idMap;
|
|
115
117
|
}
|
|
118
|
+
get sourced() {
|
|
119
|
+
return this._sourced;
|
|
120
|
+
}
|
|
121
|
+
/** Mark this file as being part of the dfg */
|
|
122
|
+
addFile(source) {
|
|
123
|
+
this._sourced.push(source);
|
|
124
|
+
}
|
|
116
125
|
/**
|
|
117
126
|
* Retrieves the set of vertices which have side effects that we do not know anything about.
|
|
118
127
|
*/
|
|
@@ -235,6 +244,7 @@ class DataflowGraph {
|
|
|
235
244
|
this.rootVertices.add(root);
|
|
236
245
|
}
|
|
237
246
|
}
|
|
247
|
+
this.sourced.push(...otherGraph.sourced);
|
|
238
248
|
for (const unknown of otherGraph.unknownSideEffects) {
|
|
239
249
|
this._unknownSideEffects.add(unknown);
|
|
240
250
|
}
|
|
@@ -326,6 +336,9 @@ class DataflowGraph {
|
|
|
326
336
|
graph.rootVertices = new Set(data.rootVertices);
|
|
327
337
|
graph.vertexInformation = new Map(data.vertexInformation);
|
|
328
338
|
graph.edgeInformation = new Map(data.edgeInformation.map(([id, edges]) => [id, new Map(edges)]));
|
|
339
|
+
if (data.sourced) {
|
|
340
|
+
graph._sourced = data.sourced;
|
|
341
|
+
}
|
|
329
342
|
return graph;
|
|
330
343
|
}
|
|
331
344
|
}
|
|
@@ -46,6 +46,14 @@ function resolveDataflowGraph(graph, idMap) {
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
|
+
for (const unknown of graph.unknownSideEffects) {
|
|
50
|
+
if (typeof unknown === 'object') {
|
|
51
|
+
resultGraph.markIdForUnknownSideEffects(resolve(unknown.id), unknown.linkTo);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
resultGraph.markIdForUnknownSideEffects(resolve(unknown));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
49
57
|
return resultGraph;
|
|
50
58
|
}
|
|
51
59
|
//# sourceMappingURL=resolve-graph.js.map
|
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
import type { RFunctionArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
|
|
2
2
|
import type { RNode } from '../../../../../../r-bridge/lang-4.x/ast/model/model';
|
|
3
|
+
/**
|
|
4
|
+
* Retrieve the value from an argument, if it is not empty.
|
|
5
|
+
*/
|
|
3
6
|
export declare function unpackArgument<OtherInfo>(arg: RFunctionArgument<OtherInfo>, noNameOnly?: boolean): RNode<OtherInfo> | undefined;
|
|
@@ -1,17 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.unpackArgument = unpackArgument;
|
|
4
|
-
const log_1 = require("../../../../../../util/log");
|
|
5
4
|
const r_function_call_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
|
|
5
|
+
/**
|
|
6
|
+
* Retrieve the value from an argument, if it is not empty.
|
|
7
|
+
*/
|
|
6
8
|
function unpackArgument(arg, noNameOnly = true) {
|
|
7
|
-
|
|
8
|
-
log_1.log.trace('Argument is empty, skipping');
|
|
9
|
-
return undefined;
|
|
10
|
-
}
|
|
11
|
-
else if (noNameOnly && arg.name !== undefined) {
|
|
12
|
-
log_1.log.trace(`Argument ${JSON.stringify(arg)} is not unnamed, skipping`);
|
|
13
|
-
return undefined;
|
|
14
|
-
}
|
|
15
|
-
return arg.value;
|
|
9
|
+
return arg === r_function_call_1.EmptyArgument || (noNameOnly && arg.name) ? undefined : arg.value;
|
|
16
10
|
}
|
|
17
11
|
//# sourceMappingURL=unpack-argument.js.map
|
|
@@ -20,6 +20,7 @@ export interface AssignmentConfiguration extends ForceArguments {
|
|
|
20
20
|
/** is the target a variable pointing at the actual name? */
|
|
21
21
|
readonly targetVariable?: boolean;
|
|
22
22
|
readonly indicesCollection?: ContainerIndicesCollection;
|
|
23
|
+
readonly mayHaveMoreArgs?: boolean;
|
|
23
24
|
}
|
|
24
25
|
/**
|
|
25
26
|
* Processes an assignment, i.e., `<target> <- <source>`.
|
|
@@ -53,7 +53,7 @@ function findRootAccess(node) {
|
|
|
53
53
|
function processAssignment(name,
|
|
54
54
|
/* we expect them to be ordered in the sense that we have (source, target): `<source> <- <target>` */
|
|
55
55
|
args, rootId, data, config) {
|
|
56
|
-
if (args.length
|
|
56
|
+
if (!config.mayHaveMoreArgs && args.length !== 2) {
|
|
57
57
|
logger_1.dataflowLogger.warn(`Assignment ${name.content} has something else than 2 arguments, skipping`);
|
|
58
58
|
return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, forceArgs: config.forceArgs }).information;
|
|
59
59
|
}
|
|
@@ -217,14 +217,12 @@ function markAsAssignment(information, nodeToDefine, sourceIds, rootIdOfAssignme
|
|
|
217
217
|
}
|
|
218
218
|
}
|
|
219
219
|
information.graph.addEdge(nodeToDefine, rootIdOfAssignment, edge_1.EdgeType.DefinedBy);
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
out?.delete(id);
|
|
227
|
-
}
|
|
220
|
+
// kinda dirty, but we have to remove existing read edges for the symbol, added by the child
|
|
221
|
+
const out = information.graph.outgoingEdges(nodeToDefine.nodeId);
|
|
222
|
+
for (const [id, edge] of (out ?? [])) {
|
|
223
|
+
edge.types &= ~edge_1.EdgeType.Reads;
|
|
224
|
+
if (edge.types === 0) {
|
|
225
|
+
out?.delete(id);
|
|
228
226
|
}
|
|
229
227
|
}
|
|
230
228
|
}
|
|
@@ -17,6 +17,7 @@ const scoping_1 = require("../../../../../environments/scoping");
|
|
|
17
17
|
const built_in_1 = require("../../../../../environments/built-in");
|
|
18
18
|
const overwrite_1 = require("../../../../../environments/overwrite");
|
|
19
19
|
const logger_1 = require("../../../../../logger");
|
|
20
|
+
const log_1 = require("../../../../../../util/log");
|
|
20
21
|
const dotDotDotAccess = /^\.\.\d+$/;
|
|
21
22
|
function linkReadNameToWriteIfPossible(read, environments, listEnvironments, remainingRead, nextGraph) {
|
|
22
23
|
const readName = read.name && dotDotDotAccess.test(read.name) ? '...' : read.name;
|
|
@@ -83,7 +84,7 @@ function updateSideEffectsForCalledFunctions(calledEnvs, inputEnvironment, nextG
|
|
|
83
84
|
}
|
|
84
85
|
function processExpressionList(name, args, rootId, data) {
|
|
85
86
|
const expressions = args.map(e => (0, unpack_argument_1.unpackArgument)(e));
|
|
86
|
-
logger_1.dataflowLogger
|
|
87
|
+
(0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `[expr list] with ${expressions.length} expressions`);
|
|
87
88
|
let { environment } = data;
|
|
88
89
|
// used to detect if a "write" happens within the same expression list
|
|
89
90
|
const listEnvironments = new Set();
|
|
@@ -95,7 +96,7 @@ function processExpressionList(name, args, rootId, data) {
|
|
|
95
96
|
const processedExpressions = [];
|
|
96
97
|
let defaultReturnExpr = undefined;
|
|
97
98
|
for (const expression of expressions) {
|
|
98
|
-
logger_1.dataflowLogger
|
|
99
|
+
(0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `processing expression ${++expressionCounter} of ${expressions.length}`);
|
|
99
100
|
if (expression === undefined) {
|
|
100
101
|
processedExpressions.push(undefined);
|
|
101
102
|
continue;
|
|
@@ -115,7 +116,7 @@ function processExpressionList(name, args, rootId, data) {
|
|
|
115
116
|
}
|
|
116
117
|
(0, info_1.addNonDefaultExitPoints)(exitPoints, processed.exitPoints);
|
|
117
118
|
out.push(...processed.out);
|
|
118
|
-
logger_1.dataflowLogger
|
|
119
|
+
(0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `expression ${expressionCounter} of ${expressions.length} has ${processed.unknownReferences.length} unknown nodes`);
|
|
119
120
|
processNextExpression(processed, environment, listEnvironments, remainingRead, nextGraph);
|
|
120
121
|
environment = exitPoints.length > 0 ? (0, overwrite_1.overwriteEnvironment)(environment, processed.environment) : processed.environment;
|
|
121
122
|
const calledEnvs = (0, linker_1.linkFunctionCalls)(nextGraph, data.completeAst.idMap, processed.graph);
|
|
@@ -35,11 +35,29 @@ function processPipe(name, args, rootId, data) {
|
|
|
35
35
|
information.graph.addEdge(functionCallNode.id, argId, edge_1.EdgeType.Argument | edge_1.EdgeType.Reads);
|
|
36
36
|
}
|
|
37
37
|
const firstArgument = processedArguments[0];
|
|
38
|
+
const uniqueIn = [...information.in];
|
|
39
|
+
for (const ing of (firstArgument?.in ?? [])) {
|
|
40
|
+
if (!uniqueIn.find(e => e.nodeId === ing.nodeId)) {
|
|
41
|
+
uniqueIn.push(ing);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const uniqueOut = [...information.out];
|
|
45
|
+
for (const outg of (firstArgument?.out ?? [])) {
|
|
46
|
+
if (!uniqueOut.find(e => e.nodeId === outg.nodeId)) {
|
|
47
|
+
uniqueOut.push(outg);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const uniqueUnknownReferences = [...information.unknownReferences];
|
|
51
|
+
for (const unknown of (firstArgument?.unknownReferences ?? [])) {
|
|
52
|
+
if (!uniqueUnknownReferences.find(e => e.nodeId === unknown.nodeId)) {
|
|
53
|
+
uniqueUnknownReferences.push(unknown);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
38
56
|
return {
|
|
39
57
|
...information,
|
|
40
|
-
in:
|
|
41
|
-
out:
|
|
42
|
-
unknownReferences:
|
|
58
|
+
in: uniqueIn,
|
|
59
|
+
out: uniqueOut,
|
|
60
|
+
unknownReferences: uniqueUnknownReferences,
|
|
43
61
|
entryPoint: rootId
|
|
44
62
|
};
|
|
45
63
|
}
|
|
@@ -136,7 +136,7 @@ function processSourceCall(name, args, rootId, data, config) {
|
|
|
136
136
|
const request = sourceProvider.createRequest(filepath);
|
|
137
137
|
// check if the sourced file has already been dataflow analyzed, and if so, skip it
|
|
138
138
|
if (data.referenceChain.find(e => e.request === request.request && e.content === request.content)) {
|
|
139
|
-
|
|
139
|
+
logger_1.dataflowLogger.warn(`Found loop in dataflow analysis for ${JSON.stringify(request)}: ${JSON.stringify(data.referenceChain)}, skipping further dataflow analysis`);
|
|
140
140
|
information.graph.markIdForUnknownSideEffects(rootId);
|
|
141
141
|
return information;
|
|
142
142
|
}
|
|
@@ -172,7 +172,8 @@ function sourceRequest(rootId, request, data, information, getId) {
|
|
|
172
172
|
});
|
|
173
173
|
}
|
|
174
174
|
catch (e) {
|
|
175
|
-
logger_1.dataflowLogger.
|
|
175
|
+
logger_1.dataflowLogger.error(`Failed to analyze sourced file ${JSON.stringify(request)}, skipping: ${e.message}`);
|
|
176
|
+
logger_1.dataflowLogger.error(e.stack);
|
|
176
177
|
information.graph.markIdForUnknownSideEffects(rootId);
|
|
177
178
|
return information;
|
|
178
179
|
}
|
|
@@ -183,6 +184,7 @@ function sourceRequest(rootId, request, data, information, getId) {
|
|
|
183
184
|
for (const out of dataflow.out) {
|
|
184
185
|
dataflow.graph.addControlDependency(out.nodeId, rootId, true);
|
|
185
186
|
}
|
|
187
|
+
dataflow.graph.addFile(request.request === 'file' ? request.content : '<inline>');
|
|
186
188
|
// update our graph with the sourced file's information
|
|
187
189
|
const newInformation = { ...information };
|
|
188
190
|
newInformation.environment = (0, overwrite_1.overwriteEnvironment)(information.environment, dataflow.environment);
|
package/dataflow/processor.d.ts
CHANGED
|
@@ -10,17 +10,17 @@ import type { KnownParserType, Parser } from '../r-bridge/parser';
|
|
|
10
10
|
export interface DataflowProcessorInformation<OtherInfo> {
|
|
11
11
|
readonly parser: Parser<KnownParserType>;
|
|
12
12
|
/**
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
* Initial and frozen ast-information
|
|
14
|
+
*/
|
|
15
15
|
readonly completeAst: NormalizedAst<OtherInfo>;
|
|
16
16
|
/**
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
* Correctly contains pushed local scopes introduced by `function` scopes.
|
|
18
|
+
* Will by default *not* contain any symbol-bindings introduced along the way; they have to be decorated when moving up the tree.
|
|
19
|
+
*/
|
|
20
20
|
readonly environment: REnvironmentInformation;
|
|
21
21
|
/**
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
* Other processors to be called by the given functions
|
|
23
|
+
*/
|
|
24
24
|
readonly processors: DataflowProcessors<OtherInfo>;
|
|
25
25
|
/**
|
|
26
26
|
* The {@link RParseRequests} that is currently being parsed
|
|
@@ -201,10 +201,10 @@ While the context is derived from the \`filename\`, we currently offer no way to
|
|
|
201
201
|
})}
|
|
202
202
|
|
|
203
203
|
<a id="analysis-format-compact"></a>
|
|
204
|
-
**Retrieve the Output in a
|
|
204
|
+
**Retrieve the Output in a Compacted Form**
|
|
205
205
|
|
|
206
206
|
The default response is formatted as JSON. But this can get very big quickly.
|
|
207
|
-
By specifying \`format: "compact"\`, you can retrieve the results heavily compacted (using [
|
|
207
|
+
By specifying \`format: "compact"\`, you can retrieve the results heavily compacted (using [lz-string](https://www.npmjs.com/package/lz-string)).
|
|
208
208
|
This works with and without the control flow graph as described [above](#analysis-include-cfg).
|
|
209
209
|
|
|
210
210
|
${await (0, doc_server_message_1.documentServerMessageResponse)({
|
|
@@ -62,6 +62,7 @@ It's also possible to filter the results based on the following properties:
|
|
|
62
62
|
The \`fileFilter\` property is an object made up of two properties:
|
|
63
63
|
- **Filter** (\`filter\`): A regular expression that a node's file attribute must match to be considered.
|
|
64
64
|
- **Include Undefined Files** (\`includeUndefinedFiles\`): If \`fileFilter\` is set, but a node's file attribute is not present, should we include it in the results? Defaults to \`true\`.
|
|
65
|
+
2. **Ignore Parameter Values** (\`ignoreParameterValues\`): Should we ignore default values for parameters in the results?
|
|
65
66
|
|
|
66
67
|
Re-using the example code from above, the following query attaches all calls to \`mean\` to the kind \`visualize\` and the subkind \`text\`,
|
|
67
68
|
all calls that start with \`read_\` to the kind \`input\` but only if they are not locally overwritten, and the subkind \`csv-file\`, and links all calls to \`points\` to the last call to \`plot\`:
|
|
@@ -122,6 +123,24 @@ ${await (0, doc_query_1.showQuery)(shell, exampleCode, [{
|
|
|
122
123
|
`;
|
|
123
124
|
}
|
|
124
125
|
});
|
|
126
|
+
(0, doc_query_1.registerQueryDocumentation)('project', {
|
|
127
|
+
name: 'Project Query',
|
|
128
|
+
type: 'active',
|
|
129
|
+
shortDescription: 'Returns information about the analyzed project',
|
|
130
|
+
functionName: dataflow_query_executor_1.executeDataflowQuery.name,
|
|
131
|
+
functionFile: '../queries/catalog/project-query/project-query-executor.ts',
|
|
132
|
+
buildExplanation: async (shell) => {
|
|
133
|
+
const exampleCode = 'x + 1';
|
|
134
|
+
return `
|
|
135
|
+
This query returns the information about the analyzed project.
|
|
136
|
+
Currently, this is only the list of file paths included.
|
|
137
|
+
|
|
138
|
+
${await (0, doc_query_1.showQuery)(shell, exampleCode, [{
|
|
139
|
+
type: 'project'
|
|
140
|
+
}], { showCode: true, collapseQuery: true })}
|
|
141
|
+
`;
|
|
142
|
+
}
|
|
143
|
+
});
|
|
125
144
|
(0, doc_query_1.registerQueryDocumentation)('normalized-ast', {
|
|
126
145
|
name: 'Normalized AST Query',
|
|
127
146
|
type: 'active',
|
|
@@ -464,7 +483,7 @@ ${await (0, doc_query_1.showQuery)(shell, exampleCode, [{
|
|
|
464
483
|
type: 'location-map'
|
|
465
484
|
}], { showCode: false, collapseQuery: true })}
|
|
466
485
|
|
|
467
|
-
All locations are given as a ${(0, doc_types_1.shortLink)('SourceRange', types.info)} in the format \`[start-line, start-column, end-line, end-column]\`.
|
|
486
|
+
All locations are given as a ${(0, doc_types_1.shortLink)('SourceRange', types.info)} paired with the file id in the format \`[file-id, [start-line, start-column, end-line, end-column]]\`.
|
|
468
487
|
|
|
469
488
|
`;
|
|
470
489
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eagleoutice/flowr",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.11",
|
|
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": {
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"gen:readme": "ts-node src/documentation/print-readme.ts",
|
|
37
37
|
"build": "tsc --project .",
|
|
38
38
|
"build-dev": "npm run build && npm run build:copy-wasm",
|
|
39
|
-
"build:bundle-flowr": "npm run build && esbuild --bundle dist/src/cli/flowr.js --platform=node --bundle --minify --external:clipboardy --target=node22 --outfile=dist/src/cli/flowr.min.js && npm run build:copy-wasm-min",
|
|
39
|
+
"build:bundle-flowr": "npm run build && esbuild --bundle dist/src/cli/flowr.js --platform=node --tree-shaking=true --bundle --minify --external:clipboardy --target=node22 --outfile=dist/src/cli/flowr.min.js && npm run build:copy-wasm-min",
|
|
40
40
|
"build:copy-wasm": "mkdir -p dist/src/r-bridge/lang-4.x/tree-sitter/ && cp src/r-bridge/lang-4.x/tree-sitter/tree-sitter-r.wasm src/r-bridge/lang-4.x/tree-sitter/tree-sitter.wasm dist/src/r-bridge/lang-4.x/tree-sitter/",
|
|
41
41
|
"build:copy-wasm-min": "mkdir -p dist/src/cli && cp src/r-bridge/lang-4.x/tree-sitter/tree-sitter-r.wasm src/r-bridge/lang-4.x/tree-sitter/tree-sitter.wasm dist/src/cli",
|
|
42
42
|
"lint-local": "npx eslint --version && npx eslint src/ test/ --rule \"no-warning-comments: off\"",
|
|
@@ -199,12 +199,12 @@
|
|
|
199
199
|
"vitest": "^3.0.6"
|
|
200
200
|
},
|
|
201
201
|
"dependencies": {
|
|
202
|
-
"@msgpack/msgpack": "^3.0.1",
|
|
203
202
|
"@xmldom/xmldom": "^0.9.7",
|
|
204
203
|
"clipboardy": "^4.0.0",
|
|
205
204
|
"command-line-args": "^6.0.1",
|
|
206
205
|
"command-line-usage": "^7.0.3",
|
|
207
206
|
"joi": "^17.13.3",
|
|
207
|
+
"lz-string": "^1.5.0",
|
|
208
208
|
"n-readlines": "^1.0.1",
|
|
209
209
|
"n3": "^1.23.1",
|
|
210
210
|
"object-hash": "^3.0.0",
|