@eagleoutice/flowr 2.8.3 → 2.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/repl/core.js +22 -0
- package/config.d.ts +14 -0
- package/config.js +10 -2
- package/control-flow/extract-cfg.js +35 -14
- package/core/print/slice-diff-ansi.js +1 -1
- package/dataflow/extractor.js +2 -2
- package/dataflow/graph/graph.js +0 -4
- package/dataflow/instrument/instrument-dataflow-count.d.ts +9 -0
- package/dataflow/instrument/instrument-dataflow-count.js +22 -0
- package/dataflow/internal/process/functions/call/built-in/built-in-assignment.js +14 -7
- package/dataflow/internal/process/functions/call/built-in/built-in-expression-list.js +4 -2
- package/documentation/wiki-interface.js +3 -1
- package/documentation/wiki-query.js +1 -1
- package/linter/rules/unused-definition.js +6 -5
- package/package.json +1 -1
- package/project/context/flowr-analyzer-files-context.d.ts +1 -0
- package/project/context/flowr-file.d.ts +2 -0
- package/project/context/flowr-file.js +2 -0
- package/project/plugins/file-plugins/flowr-analyzer-license-file-plugin.d.ts +24 -0
- package/project/plugins/file-plugins/flowr-analyzer-license-file-plugin.js +37 -0
- package/project/plugins/flowr-analyzer-plugin-defaults.js +2 -0
- package/project/plugins/plugin-registry.d.ts +2 -1
- package/project/plugins/plugin-registry.js +3 -1
- package/project/plugins/project-discovery/flowr-analyzer-project-discovery-plugin.js +0 -1
- package/queries/catalog/config-query/config-query-format.d.ts +2 -2
- package/queries/catalog/config-query/config-query-format.js +40 -2
- package/queries/catalog/dependencies-query/function-info/read-functions.js +8 -0
- package/queries/catalog/dependencies-query/function-info/write-functions.js +9 -0
- package/queries/query.d.ts +1 -1
- package/r-bridge/lang-4.x/tree-sitter/tree-sitter-normalize.js +481 -447
- package/r-bridge/roxygen2/documentation-provider.js +3 -1
- package/r-bridge/roxygen2/roxygen-parse.d.ts +1 -1
- package/r-bridge/roxygen2/roxygen-parse.js +9 -5
- package/util/r-version.js +17 -1
- package/util/range.d.ts +1 -1
- package/util/range.js +1 -1
- package/util/version.js +1 -1
package/cli/repl/core.js
CHANGED
|
@@ -61,6 +61,7 @@ const repl_main_1 = require("./commands/repl-main");
|
|
|
61
61
|
const log_1 = require("../../util/log");
|
|
62
62
|
const query_1 = require("../../queries/query");
|
|
63
63
|
const strings_1 = require("../../util/text/strings");
|
|
64
|
+
const instrument_dataflow_count_1 = require("../../dataflow/instrument/instrument-dataflow-count");
|
|
64
65
|
let _replCompleterKeywords = undefined;
|
|
65
66
|
function replCompleterKeywords() {
|
|
66
67
|
if (_replCompleterKeywords === undefined) {
|
|
@@ -146,6 +147,10 @@ function handleString(code) {
|
|
|
146
147
|
}
|
|
147
148
|
async function replProcessStatement(output, statement, analyzer, allowRSessionAccess) {
|
|
148
149
|
const time = Date.now();
|
|
150
|
+
const heatMap = new Map();
|
|
151
|
+
if (analyzer.inspectContext().config.repl.dfProcessorHeat) {
|
|
152
|
+
analyzer.context().config.solver.instrument.dataflowExtractors = (0, instrument_dataflow_count_1.instrumentDataflowCount)(heatMap, map => map.clear());
|
|
153
|
+
}
|
|
149
154
|
if (statement.startsWith(':')) {
|
|
150
155
|
const command = statement.slice(1).split(' ')[0].toLowerCase();
|
|
151
156
|
const processor = (0, repl_commands_1.getCommand)(command);
|
|
@@ -201,6 +206,23 @@ async function replProcessStatement(output, statement, analyzer, allowRSessionAc
|
|
|
201
206
|
// do nothing, this is just a nice-to-have
|
|
202
207
|
}
|
|
203
208
|
}
|
|
209
|
+
if (heatMap.size > 0 && analyzer.inspectContext().config.repl.dfProcessorHeat) {
|
|
210
|
+
const sorted = Array.from(heatMap.entries()).sort((a, b) => b[1] - a[1]);
|
|
211
|
+
console.log(output.formatter.format('[REPL Stats] Dataflow Processor Heatmap:', {
|
|
212
|
+
style: 3 /* FontStyles.Italic */,
|
|
213
|
+
effect: ansi_1.ColorEffect.Foreground,
|
|
214
|
+
color: 7 /* Colors.White */
|
|
215
|
+
}));
|
|
216
|
+
const longestKey = Math.max(...Array.from(heatMap.keys(), k => k.length));
|
|
217
|
+
const longestValue = Math.max(...Array.from(heatMap.values(), v => v.toString().length));
|
|
218
|
+
for (const [rType, count] of sorted) {
|
|
219
|
+
console.log(output.formatter.format(` - ${(rType + ':').padEnd(longestKey + 1, ' ')} ${count.toString().padStart(longestValue, ' ')}`, {
|
|
220
|
+
style: 3 /* FontStyles.Italic */,
|
|
221
|
+
effect: ansi_1.ColorEffect.Foreground,
|
|
222
|
+
color: 7 /* Colors.White */
|
|
223
|
+
}));
|
|
224
|
+
}
|
|
225
|
+
}
|
|
204
226
|
}
|
|
205
227
|
/**
|
|
206
228
|
* This function interprets the given `expr` as a REPL command (see {@link repl} for more on the semantics).
|
package/config.d.ts
CHANGED
|
@@ -3,6 +3,9 @@ import Joi from 'joi';
|
|
|
3
3
|
import type { BuiltInDefinitions } from './dataflow/environments/built-in-config';
|
|
4
4
|
import type { KnownParser } from './r-bridge/parser';
|
|
5
5
|
import type { DeepWritable } from 'ts-essentials';
|
|
6
|
+
import type { DataflowProcessors } from './dataflow/processor';
|
|
7
|
+
import type { ParentInformation } from './r-bridge/lang-4.x/ast/model/processing/decorate';
|
|
8
|
+
import type { FlowrAnalyzerContext } from './project/context/flowr-analyzer-context';
|
|
6
9
|
export declare enum VariableResolve {
|
|
7
10
|
/** Don't resolve constants at all */
|
|
8
11
|
Disabled = "disabled",
|
|
@@ -102,6 +105,8 @@ export interface FlowrConfigOptions extends MergeableRecord {
|
|
|
102
105
|
readonly repl: {
|
|
103
106
|
/** Whether to show quick stats in the REPL after each evaluation */
|
|
104
107
|
quickStats: boolean;
|
|
108
|
+
/** This instruments the dataflow processors to count how often each processor is called */
|
|
109
|
+
dfProcessorHeat: boolean;
|
|
105
110
|
};
|
|
106
111
|
readonly project: {
|
|
107
112
|
/** Whether to resolve unknown paths loaded by the r project disk when trying to source/analyze files */
|
|
@@ -137,6 +142,15 @@ export interface FlowrConfigOptions extends MergeableRecord {
|
|
|
137
142
|
*/
|
|
138
143
|
readonly maxIndexCount: number;
|
|
139
144
|
};
|
|
145
|
+
/** These keys are only intended for use within code, allowing to instrument the dataflow analyzer! */
|
|
146
|
+
readonly instrument: {
|
|
147
|
+
/**
|
|
148
|
+
* Modify the dataflow processors used during dataflow analysis.
|
|
149
|
+
* Make sure that all processors required for correct analysis are still present!
|
|
150
|
+
* This may have arbitrary consequences on the analysis precision and performance, consider focusing on decorating existing processors instead of replacing them.
|
|
151
|
+
*/
|
|
152
|
+
dataflowExtractors?: (extractor: DataflowProcessors<ParentInformation>, ctx: FlowrAnalyzerContext) => DataflowProcessors<ParentInformation>;
|
|
153
|
+
};
|
|
140
154
|
/**
|
|
141
155
|
* If lax source calls are active, flowR searches for sourced files much more freely,
|
|
142
156
|
* based on the configurations you give it.
|
package/config.js
CHANGED
|
@@ -66,7 +66,8 @@ exports.defaultConfigOptions = {
|
|
|
66
66
|
}
|
|
67
67
|
},
|
|
68
68
|
repl: {
|
|
69
|
-
quickStats: false
|
|
69
|
+
quickStats: false,
|
|
70
|
+
dfProcessorHeat: false
|
|
70
71
|
},
|
|
71
72
|
project: {
|
|
72
73
|
resolveUnknownPathsOnDisk: true
|
|
@@ -84,6 +85,9 @@ exports.defaultConfigOptions = {
|
|
|
84
85
|
searchPath: [],
|
|
85
86
|
repeatedSourceLimit: 2
|
|
86
87
|
},
|
|
88
|
+
instrument: {
|
|
89
|
+
dataflowExtractors: undefined
|
|
90
|
+
},
|
|
87
91
|
slicer: {
|
|
88
92
|
threshold: 50
|
|
89
93
|
}
|
|
@@ -110,7 +114,8 @@ exports.flowrConfigFileSchema = joi_1.default.object({
|
|
|
110
114
|
}).optional().description('Semantics regarding how to handle the R environment.')
|
|
111
115
|
}).description('Configure language semantics and how flowR handles them.'),
|
|
112
116
|
repl: joi_1.default.object({
|
|
113
|
-
quickStats: joi_1.default.boolean().optional().description('Whether to show quick stats in the REPL after each evaluation.')
|
|
117
|
+
quickStats: joi_1.default.boolean().optional().description('Whether to show quick stats in the REPL after each evaluation.'),
|
|
118
|
+
dfProcessorHeat: joi_1.default.boolean().optional().description('This instruments the dataflow processors to count how often each processor is called.')
|
|
114
119
|
}).description('Configuration options for the REPL.'),
|
|
115
120
|
project: joi_1.default.object({
|
|
116
121
|
resolveUnknownPathsOnDisk: joi_1.default.boolean().optional().description('Whether to resolve unknown paths loaded by the r project disk when trying to source/analyze files.')
|
|
@@ -131,6 +136,9 @@ exports.flowrConfigFileSchema = joi_1.default.object({
|
|
|
131
136
|
pointerTracking: joi_1.default.alternatives(joi_1.default.boolean(), joi_1.default.object({
|
|
132
137
|
maxIndexCount: joi_1.default.number().required().description('The maximum number of indices tracked per object with the pointer analysis.')
|
|
133
138
|
})).description('Whether to track pointers in the dataflow graph, if not, the graph will be over-approximated wrt. containers and accesses.'),
|
|
139
|
+
instrument: joi_1.default.object({
|
|
140
|
+
dataflowExtractors: joi_1.default.any().optional().description('These keys are only intended for use within code, allowing to instrument the dataflow analyzer!')
|
|
141
|
+
}),
|
|
134
142
|
resolveSource: joi_1.default.object({
|
|
135
143
|
dropPaths: joi_1.default.string().valid(...Object.values(DropPathsOption)).description('Allow to drop the first or all parts of the sourced path, if it is relative.'),
|
|
136
144
|
ignoreCapitalization: joi_1.default.boolean().description('Search for filenames matching in the lowercase.'),
|
|
@@ -148,8 +148,10 @@ function cfgIfThenElse(ifNode, condition, then, otherwise) {
|
|
|
148
148
|
for (const entryPoint of condition.entryPoints) {
|
|
149
149
|
graph.addEdge(entryPoint, ifId, { label: 0 /* CfgEdgeType.Fd */ });
|
|
150
150
|
}
|
|
151
|
-
for (const
|
|
152
|
-
|
|
151
|
+
for (const exits of [then.exitPoints, otherwise?.exitPoints ?? []]) {
|
|
152
|
+
for (const exit of exits) {
|
|
153
|
+
graph.addEdge(ifId + '-exit', exit, { label: 0 /* CfgEdgeType.Fd */ });
|
|
154
|
+
}
|
|
153
155
|
}
|
|
154
156
|
if (!otherwise) {
|
|
155
157
|
for (const e of condition.exitPoints) {
|
|
@@ -173,8 +175,10 @@ function cfgRepeat(repeat, body) {
|
|
|
173
175
|
graph.addEdge(entryPoint, repeat.info.id, { label: 0 /* CfgEdgeType.Fd */ });
|
|
174
176
|
}
|
|
175
177
|
// loops automatically
|
|
176
|
-
for (const
|
|
177
|
-
|
|
178
|
+
for (const nexts of [body.nexts, body.exitPoints]) {
|
|
179
|
+
for (const next of nexts) {
|
|
180
|
+
graph.addEdge(repeat.info.id, next, { label: 0 /* CfgEdgeType.Fd */ });
|
|
181
|
+
}
|
|
178
182
|
}
|
|
179
183
|
for (const breakPoint of body.breaks) {
|
|
180
184
|
graph.addEdge(repeat.info.id + '-exit', breakPoint, { label: 0 /* CfgEdgeType.Fd */ });
|
|
@@ -195,8 +199,10 @@ function cfgWhile(whileLoop, condition, body) {
|
|
|
195
199
|
graph.addEdge(entry, e, { label: 1 /* CfgEdgeType.Cd */, when: convert_values_1.RTrue, caused: whileId });
|
|
196
200
|
}
|
|
197
201
|
}
|
|
198
|
-
for (const
|
|
199
|
-
|
|
202
|
+
for (const nexts of [body.nexts, body.exitPoints]) {
|
|
203
|
+
for (const next of nexts) {
|
|
204
|
+
graph.addEdge(whileId, next, { label: 0 /* CfgEdgeType.Fd */ });
|
|
205
|
+
}
|
|
200
206
|
}
|
|
201
207
|
for (const breakPoint of body.breaks) {
|
|
202
208
|
graph.addEdge(whileId + '-exit', breakPoint, { label: 0 /* CfgEdgeType.Fd */ });
|
|
@@ -230,8 +236,10 @@ function cfgFor(forLoop, variable, vector, body) {
|
|
|
230
236
|
graph.addEdge(entry, e, { label: 1 /* CfgEdgeType.Cd */, when: convert_values_1.RTrue, caused: forLoopId });
|
|
231
237
|
}
|
|
232
238
|
}
|
|
233
|
-
for (const
|
|
234
|
-
|
|
239
|
+
for (const points of [body.nexts, body.exitPoints]) {
|
|
240
|
+
for (const next of points) {
|
|
241
|
+
graph.addEdge(forLoopId, next, { label: 0 /* CfgEdgeType.Fd */ });
|
|
242
|
+
}
|
|
235
243
|
}
|
|
236
244
|
for (const breakPoint of body.breaks) {
|
|
237
245
|
graph.addEdge(forLoopId + '-exit', breakPoint, { label: 0 /* CfgEdgeType.Fd */ });
|
|
@@ -257,10 +265,14 @@ function cfgFunctionDefinition(fn, params, body) {
|
|
|
257
265
|
graph.addVertex({ id: fnId + '-exit', type: control_flow_graph_1.CfgVertexType.EndMarker, root: fnId }, false);
|
|
258
266
|
graph.addVertex({ id: fnId, children, type: identifyMayStatementType(fn), mid: paramExits, end: [fnId + '-exit'] });
|
|
259
267
|
graph.mergeWith(body.graph, true);
|
|
260
|
-
|
|
268
|
+
for (const r of body.graph.rootIds()) {
|
|
269
|
+
children.push(r);
|
|
270
|
+
}
|
|
261
271
|
for (const param of params) {
|
|
262
272
|
graph.mergeWith(param.graph, true);
|
|
263
|
-
|
|
273
|
+
for (const r of param.graph.rootIds()) {
|
|
274
|
+
children.push(r);
|
|
275
|
+
}
|
|
264
276
|
for (const entry of param.entryPoints) {
|
|
265
277
|
graph.addEdge(entry, fnId, { label: 0 /* CfgEdgeType.Fd */ });
|
|
266
278
|
}
|
|
@@ -282,7 +294,14 @@ function cfgFunctionDefinition(fn, params, body) {
|
|
|
282
294
|
function cfgFunctionCall(call, name, args, exit = 'exit') {
|
|
283
295
|
const callId = call.info.id;
|
|
284
296
|
const graph = name.graph;
|
|
285
|
-
const info = {
|
|
297
|
+
const info = {
|
|
298
|
+
graph,
|
|
299
|
+
breaks: Array.from(name.breaks),
|
|
300
|
+
nexts: Array.from(name.nexts),
|
|
301
|
+
returns: Array.from(name.returns),
|
|
302
|
+
exitPoints: [callId + '-' + exit],
|
|
303
|
+
entryPoints: [callId]
|
|
304
|
+
};
|
|
286
305
|
graph.addVertex({ id: callId, type: identifyMayStatementType(call), mid: name.exitPoints, end: [callId + '-' + exit] });
|
|
287
306
|
for (const entryPoint of name.entryPoints) {
|
|
288
307
|
graph.addEdge(entryPoint, callId, { label: 0 /* CfgEdgeType.Fd */ });
|
|
@@ -333,8 +352,10 @@ function cfgFunctionCallWithDataflow(graph) {
|
|
|
333
352
|
type: control_flow_graph_1.CfgVertexType.EndMarker,
|
|
334
353
|
root: call.info.id
|
|
335
354
|
});
|
|
336
|
-
for (const
|
|
337
|
-
|
|
355
|
+
for (const col of [baseCfg.exitPoints, exits]) {
|
|
356
|
+
for (const exit of col) {
|
|
357
|
+
baseCfg.graph.addEdge(call.info.id + exports.ResolvedCallSuffix, exit, { label: 0 /* CfgEdgeType.Fd */ });
|
|
358
|
+
}
|
|
338
359
|
}
|
|
339
360
|
return {
|
|
340
361
|
...baseCfg,
|
|
@@ -380,7 +401,7 @@ function cfgArgumentOrParameter(node, name, value) {
|
|
|
380
401
|
}
|
|
381
402
|
function cfgBinaryOp(binOp, lhs, rhs) {
|
|
382
403
|
const graph = new control_flow_graph_1.ControlFlowGraph().mergeWith(lhs.graph).mergeWith(rhs.graph);
|
|
383
|
-
const result = { graph, breaks:
|
|
404
|
+
const result = { graph, breaks: lhs.breaks.concat(rhs.breaks), nexts: lhs.nexts.concat(rhs.nexts), returns: lhs.returns.concat(rhs.returns), entryPoints: [binOp.info.id], exitPoints: [binOp.info.id + '-exit'] };
|
|
384
405
|
graph.addVertex({ id: binOp.info.id, type: binOp.flavor === 'assignment' ? control_flow_graph_1.CfgVertexType.Statement : control_flow_graph_1.CfgVertexType.Expression, end: [binOp.info.id + '-exit'] });
|
|
385
406
|
graph.addVertex({ id: binOp.info.id + '-exit', type: control_flow_graph_1.CfgVertexType.EndMarker, root: binOp.info.id });
|
|
386
407
|
for (const exitPoint of lhs.exitPoints) {
|
|
@@ -13,7 +13,7 @@ function mergeJointRangesInSorted(loc) {
|
|
|
13
13
|
return [
|
|
14
14
|
...acc.slice(0, -1), {
|
|
15
15
|
selected: curr.selected || acc[acc.length - 1].selected,
|
|
16
|
-
location: (0, range_1.mergeRanges)(acc[acc.length - 1].location, curr.location)
|
|
16
|
+
location: (0, range_1.mergeRanges)([acc[acc.length - 1].location, curr.location])
|
|
17
17
|
}
|
|
18
18
|
];
|
|
19
19
|
}
|
package/dataflow/extractor.js
CHANGED
|
@@ -89,7 +89,7 @@ function resolveLinkToSideEffects(ast, graph) {
|
|
|
89
89
|
continue;
|
|
90
90
|
}
|
|
91
91
|
/* this has to change whenever we add a new link to relations because we currently offer no abstraction for the type */
|
|
92
|
-
const potentials = (0, identify_link_to_last_call_relation_1.identifyLinkToLastCallRelation)(s.id, cf
|
|
92
|
+
const potentials = (0, identify_link_to_last_call_relation_1.identifyLinkToLastCallRelation)(s.id, cf?.graph, graph, s.linkTo, knownCalls);
|
|
93
93
|
for (const pot of potentials) {
|
|
94
94
|
graph.addEdge(s.id, pot, edge_1.EdgeType.Reads);
|
|
95
95
|
}
|
|
@@ -113,7 +113,7 @@ function produceDataFlowGraph(parser, completeAst, ctx) {
|
|
|
113
113
|
parser,
|
|
114
114
|
completeAst,
|
|
115
115
|
environment: ctx.env.makeCleanEnv(),
|
|
116
|
-
processors: exports.processors,
|
|
116
|
+
processors: ctx.config.solver.instrument.dataflowExtractors?.(exports.processors, ctx) ?? exports.processors,
|
|
117
117
|
controlDependencies: undefined,
|
|
118
118
|
referenceChain: [files[0].filePath],
|
|
119
119
|
ctx
|
package/dataflow/graph/graph.js
CHANGED
|
@@ -5,7 +5,6 @@ exports.isPositionalArgument = isPositionalArgument;
|
|
|
5
5
|
exports.isNamedArgument = isNamedArgument;
|
|
6
6
|
exports.getReferenceOfArgument = getReferenceOfArgument;
|
|
7
7
|
const assert_1 = require("../../util/assert");
|
|
8
|
-
const diff_dataflow_graph_1 = require("./diff-dataflow-graph");
|
|
9
8
|
const vertex_1 = require("./vertex");
|
|
10
9
|
const arrays_1 = require("../../util/collections/arrays");
|
|
11
10
|
const r_function_call_1 = require("../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
|
|
@@ -385,9 +384,6 @@ function mergeNodeInfos(current, next) {
|
|
|
385
384
|
if (current.tag === vertex_1.VertexType.VariableDefinition) {
|
|
386
385
|
(0, assert_1.guard)(current.scope === next.scope, 'nodes to be joined for the same id must have the same scope');
|
|
387
386
|
}
|
|
388
|
-
else if (current.tag === vertex_1.VertexType.FunctionCall) {
|
|
389
|
-
(0, assert_1.guard)((0, diff_dataflow_graph_1.equalFunctionArguments)(current.id, current.args, next.args), 'nodes to be joined for the same id must have the same function call information');
|
|
390
|
-
}
|
|
391
387
|
else if (current.tag === vertex_1.VertexType.FunctionDefinition) {
|
|
392
388
|
(0, assert_1.guard)(current.scope === next.scope, 'nodes to be joined for the same id must have the same scope');
|
|
393
389
|
current.exitPoints = (0, arrays_1.uniqueArrayMerge)(current.exitPoints, next.exitPoints);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { DataflowProcessors } from '../processor';
|
|
2
|
+
import type { ParentInformation } from '../../r-bridge/lang-4.x/ast/model/processing/decorate';
|
|
3
|
+
import type { FlowrAnalyzerContext } from '../../project/context/flowr-analyzer-context';
|
|
4
|
+
import type { RType } from '../../r-bridge/lang-4.x/ast/model/type';
|
|
5
|
+
/**
|
|
6
|
+
* This takes the out parameter `countMap` and fills it with the count of how many times each RType was processed.
|
|
7
|
+
* The accompanying `reset` function can be used to reset the map to an empty state.
|
|
8
|
+
*/
|
|
9
|
+
export declare function instrumentDataflowCount(countMap: Map<RType, number>, reset: (map: Map<RType, number>) => void): (extractor: DataflowProcessors<ParentInformation>, ctx: FlowrAnalyzerContext) => DataflowProcessors<ParentInformation>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.instrumentDataflowCount = instrumentDataflowCount;
|
|
4
|
+
/**
|
|
5
|
+
* This takes the out parameter `countMap` and fills it with the count of how many times each RType was processed.
|
|
6
|
+
* The accompanying `reset` function can be used to reset the map to an empty state.
|
|
7
|
+
*/
|
|
8
|
+
function instrumentDataflowCount(countMap, reset) {
|
|
9
|
+
return (extractor, _ctx) => {
|
|
10
|
+
reset(countMap);
|
|
11
|
+
const instrumented = {};
|
|
12
|
+
for (const [key, processor] of Object.entries(extractor)) {
|
|
13
|
+
instrumented[key] = ((...args) => {
|
|
14
|
+
const prev = countMap.get(key) ?? 0;
|
|
15
|
+
countMap.set(key, prev + 1);
|
|
16
|
+
return processor(...args);
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
return instrumented;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=instrument-dataflow-count.js.map
|
|
@@ -221,13 +221,20 @@ function extractSourceAndTarget(args) {
|
|
|
221
221
|
* Promotes the ingoing/unknown references of target (an assignment) to definitions
|
|
222
222
|
*/
|
|
223
223
|
function produceWrittenNodes(rootId, target, referenceType, data, makeMaybe, value) {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
224
|
+
const written = [];
|
|
225
|
+
for (const refs of [target.in, target.unknownReferences]) {
|
|
226
|
+
for (const ref of refs) {
|
|
227
|
+
written.push({
|
|
228
|
+
nodeId: ref.nodeId,
|
|
229
|
+
name: ref.name,
|
|
230
|
+
type: referenceType,
|
|
231
|
+
definedAt: rootId,
|
|
232
|
+
controlDependencies: data.controlDependencies ?? (makeMaybe ? [] : undefined),
|
|
233
|
+
value
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return written;
|
|
231
238
|
}
|
|
232
239
|
function processAssignmentToString(target, args, name, rootId, data, config, source) {
|
|
233
240
|
const symbol = {
|
|
@@ -126,8 +126,10 @@ function processExpressionList(name, args, rootId, data) {
|
|
|
126
126
|
}
|
|
127
127
|
out = out.concat(processed.out);
|
|
128
128
|
// all inputs that have not been written until now are read!
|
|
129
|
-
for (const
|
|
130
|
-
|
|
129
|
+
for (const ls of [processed.in, processed.unknownReferences]) {
|
|
130
|
+
for (const read of ls) {
|
|
131
|
+
linkReadNameToWriteIfPossible(read, environment, listEnvironments, remainingRead, nextGraph);
|
|
132
|
+
}
|
|
131
133
|
}
|
|
132
134
|
const calledEnvs = (0, linker_1.linkFunctionCalls)(nextGraph, data.completeAst.idMap, processed.graph);
|
|
133
135
|
for (const c of calledEnvs) {
|
|
@@ -241,7 +241,8 @@ ${(0, doc_code_1.codeBlock)('json', JSON.stringify({
|
|
|
241
241
|
}
|
|
242
242
|
},
|
|
243
243
|
repl: {
|
|
244
|
-
quickStats: false
|
|
244
|
+
quickStats: false,
|
|
245
|
+
dfProcessorHeat: false
|
|
245
246
|
},
|
|
246
247
|
project: {
|
|
247
248
|
resolveUnknownPathsOnDisk: true
|
|
@@ -257,6 +258,7 @@ ${(0, doc_code_1.codeBlock)('json', JSON.stringify({
|
|
|
257
258
|
inferWorkingDirectory: config_1.InferWorkingDirectory.ActiveScript,
|
|
258
259
|
searchPath: []
|
|
259
260
|
},
|
|
261
|
+
instrument: {},
|
|
260
262
|
slicer: {
|
|
261
263
|
threshold: 50
|
|
262
264
|
}
|
|
@@ -457,7 +457,7 @@ ${await (0, doc_repl_1.documentReplSession)(shell, [
|
|
|
457
457
|
])}
|
|
458
458
|
|
|
459
459
|
One of the most useful options to change on-the-fly are probably those under \`repl\`. For example, setting \`repl.quickStats=true\`
|
|
460
|
-
enables quick statistics after each REPL command.
|
|
460
|
+
enables quick statistics after each REPL command. Likewise, setting \`repl.dfProcessorHeat=true\` enables the dataflow processor heatmap after each REPL command.
|
|
461
461
|
`;
|
|
462
462
|
}
|
|
463
463
|
});
|
|
@@ -33,10 +33,11 @@ function buildQuickFix(variable, dfg, ast) {
|
|
|
33
33
|
if (hasImportantArgs) {
|
|
34
34
|
return undefined; // we can not remove this definition, it has important arguments
|
|
35
35
|
}
|
|
36
|
-
const totalRangeToRemove = (0, range_1.mergeRanges)(...definedBys.map(d => {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
const totalRangeToRemove = (0, range_1.mergeRanges)([...definedBys.map(d => {
|
|
37
|
+
const vertex = ast.idMap.get(d);
|
|
38
|
+
return vertex?.info.fullRange ?? vertex?.location;
|
|
39
|
+
}),
|
|
40
|
+
variable.info.fullRange ?? variable.location]);
|
|
40
41
|
return [{
|
|
41
42
|
type: 'remove',
|
|
42
43
|
range: totalRangeToRemove,
|
|
@@ -52,7 +53,7 @@ function onlyKeepSupersetOfUnused(elements) {
|
|
|
52
53
|
return elements; // nothing to filter, only one element
|
|
53
54
|
}
|
|
54
55
|
return elements.filter(e => {
|
|
55
|
-
const otherRange = (0, range_1.mergeRanges)(
|
|
56
|
+
const otherRange = (0, range_1.mergeRanges)((e.quickFix?.map(q => q.range) ?? [e.range]));
|
|
56
57
|
return !ranges.some(r => (0, range_1.rangeCompare)(r, otherRange) !== 0 && (0, range_1.rangeIsSubsetOf)(otherRange, r)); // there is no smaller remove
|
|
57
58
|
});
|
|
58
59
|
}
|
package/package.json
CHANGED
|
@@ -24,6 +24,7 @@ export type RoleBasedFiles = {
|
|
|
24
24
|
[FileRole.Namespace]: FlowrNamespaceFile[];
|
|
25
25
|
[FileRole.Vignette]: FlowrFileProvider[];
|
|
26
26
|
[FileRole.Test]: FlowrFileProvider[];
|
|
27
|
+
[FileRole.License]: FlowrFileProvider[];
|
|
27
28
|
[FileRole.Source]: FlowrFileProvider[];
|
|
28
29
|
[FileRole.Data]: FlowrFileProvider[];
|
|
29
30
|
[FileRole.Other]: FlowrFileProvider[];
|
|
@@ -23,6 +23,8 @@ export declare enum FileRole {
|
|
|
23
23
|
Test = "test",
|
|
24
24
|
/** Data files, e.g., `R/sysdata.rda`, currently not specially supported. */
|
|
25
25
|
Data = "data",
|
|
26
|
+
/** Signals separate license files, but please note, that DESCRIPTION files may contain license info too */
|
|
27
|
+
License = "license",
|
|
26
28
|
/**
|
|
27
29
|
* Catch-all for any file that provides usable R source code to incorporate into the analysis.
|
|
28
30
|
* Please note, that the loading order/inclusion and even potential relevance of these source files
|
|
@@ -24,6 +24,8 @@ var FileRole;
|
|
|
24
24
|
FileRole["Test"] = "test";
|
|
25
25
|
/** Data files, e.g., `R/sysdata.rda`, currently not specially supported. */
|
|
26
26
|
FileRole["Data"] = "data";
|
|
27
|
+
/** Signals separate license files, but please note, that DESCRIPTION files may contain license info too */
|
|
28
|
+
FileRole["License"] = "license";
|
|
27
29
|
/**
|
|
28
30
|
* Catch-all for any file that provides usable R source code to incorporate into the analysis.
|
|
29
31
|
* Please note, that the loading order/inclusion and even potential relevance of these source files
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { FlowrAnalyzerFilePlugin } from './flowr-analyzer-file-plugin';
|
|
2
|
+
import { SemVer } from 'semver';
|
|
3
|
+
import type { PathLike } from 'fs';
|
|
4
|
+
import type { FlowrAnalyzerContext } from '../../context/flowr-analyzer-context';
|
|
5
|
+
import type { FlowrFileProvider } from '../../context/flowr-file';
|
|
6
|
+
/**
|
|
7
|
+
* This plugin provides supports for the identification of license files.
|
|
8
|
+
*/
|
|
9
|
+
export declare class FlowrAnalyzerLicenseFilePlugin extends FlowrAnalyzerFilePlugin {
|
|
10
|
+
readonly name = "flowr-analyzer-license-files-plugin";
|
|
11
|
+
readonly description = "This plugin provides support for loading license files.";
|
|
12
|
+
readonly version: SemVer;
|
|
13
|
+
private readonly pathPattern;
|
|
14
|
+
/**
|
|
15
|
+
* Creates a new instance of the TEST file plugin.
|
|
16
|
+
* @param pathPattern - The pathPattern to identify TEST files, see {@link FileNamePattern} for the default pathPattern.
|
|
17
|
+
*/
|
|
18
|
+
constructor(pathPattern?: RegExp);
|
|
19
|
+
applies(file: PathLike): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Processes the given file, assigning it the {@link FileRole.License} role.
|
|
22
|
+
*/
|
|
23
|
+
process(_ctx: FlowrAnalyzerContext, file: FlowrFileProvider): FlowrFileProvider;
|
|
24
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FlowrAnalyzerLicenseFilePlugin = void 0;
|
|
4
|
+
const flowr_analyzer_file_plugin_1 = require("./flowr-analyzer-file-plugin");
|
|
5
|
+
const semver_1 = require("semver");
|
|
6
|
+
const flowr_file_1 = require("../../context/flowr-file");
|
|
7
|
+
const built_in_source_1 = require("../../../dataflow/internal/process/functions/call/built-in/built-in-source");
|
|
8
|
+
const FileNamePattern = /license(\.md|\.txt)?$/i;
|
|
9
|
+
/**
|
|
10
|
+
* This plugin provides supports for the identification of license files.
|
|
11
|
+
*/
|
|
12
|
+
class FlowrAnalyzerLicenseFilePlugin extends flowr_analyzer_file_plugin_1.FlowrAnalyzerFilePlugin {
|
|
13
|
+
name = 'flowr-analyzer-license-files-plugin';
|
|
14
|
+
description = 'This plugin provides support for loading license files.';
|
|
15
|
+
version = new semver_1.SemVer('0.1.0');
|
|
16
|
+
pathPattern;
|
|
17
|
+
/**
|
|
18
|
+
* Creates a new instance of the TEST file plugin.
|
|
19
|
+
* @param pathPattern - The pathPattern to identify TEST files, see {@link FileNamePattern} for the default pathPattern.
|
|
20
|
+
*/
|
|
21
|
+
constructor(pathPattern = FileNamePattern) {
|
|
22
|
+
super();
|
|
23
|
+
this.pathPattern = pathPattern;
|
|
24
|
+
}
|
|
25
|
+
applies(file) {
|
|
26
|
+
return this.pathPattern.test((0, built_in_source_1.platformBasename)(file.toString()));
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Processes the given file, assigning it the {@link FileRole.License} role.
|
|
30
|
+
*/
|
|
31
|
+
process(_ctx, file) {
|
|
32
|
+
file.assignRole(flowr_file_1.FileRole.License);
|
|
33
|
+
return file;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.FlowrAnalyzerLicenseFilePlugin = FlowrAnalyzerLicenseFilePlugin;
|
|
37
|
+
//# sourceMappingURL=flowr-analyzer-license-file-plugin.js.map
|
|
@@ -11,6 +11,7 @@ const flowr_analyzer_namespace_files_plugin_1 = require("./file-plugins/flowr-an
|
|
|
11
11
|
const flowr_analyzer_news_file_plugin_1 = require("./file-plugins/flowr-analyzer-news-file-plugin");
|
|
12
12
|
const flowr_analyzer_vignette_file_plugin_1 = require("./file-plugins/flowr-analyzer-vignette-file-plugin");
|
|
13
13
|
const flowr_analyzer_test_file_plugin_1 = require("./file-plugins/flowr-analyzer-test-file-plugin");
|
|
14
|
+
const flowr_analyzer_license_file_plugin_1 = require("./file-plugins/flowr-analyzer-license-file-plugin");
|
|
14
15
|
/**
|
|
15
16
|
* Provides the default set of Flowr Analyzer plugins.
|
|
16
17
|
*/
|
|
@@ -23,6 +24,7 @@ function FlowrAnalyzerPluginDefaults() {
|
|
|
23
24
|
new flowr_analyzer_loading_order_description_file_plugin_1.FlowrAnalyzerLoadingOrderDescriptionFilePlugin(),
|
|
24
25
|
new flowr_analyzer_rmd_file_plugin_1.FlowrAnalyzerRmdFilePlugin(),
|
|
25
26
|
new flowr_analyzer_qmd_file_plugin_1.FlowrAnalyzerQmdFilePlugin(),
|
|
27
|
+
new flowr_analyzer_license_file_plugin_1.FlowrAnalyzerLicenseFilePlugin(),
|
|
26
28
|
new flowr_analyzer_jupyter_file_plugin_1.FlowrAnalyzerJupyterFilePlugin(),
|
|
27
29
|
new flowr_analyzer_namespace_files_plugin_1.FlowrAnalyzerNamespaceFilesPlugin(),
|
|
28
30
|
new flowr_analyzer_news_file_plugin_1.FlowrAnalyzerNewsFilePlugin()
|
|
@@ -9,10 +9,11 @@ import { FlowrAnalyzerNamespaceFilesPlugin } from './file-plugins/flowr-analyzer
|
|
|
9
9
|
import { FlowrAnalyzerNewsFilePlugin } from './file-plugins/flowr-analyzer-news-file-plugin';
|
|
10
10
|
import { FlowrAnalyzerMetaVignetteFilesPlugin } from './file-plugins/flowr-analyzer-vignette-file-plugin';
|
|
11
11
|
import { FlowrAnalyzerMetaTestFilesPlugin } from './file-plugins/flowr-analyzer-test-file-plugin';
|
|
12
|
+
import { FlowrAnalyzerLicenseFilePlugin } from './file-plugins/flowr-analyzer-license-file-plugin';
|
|
12
13
|
/**
|
|
13
14
|
* The built-in Flowr Analyzer plugins that are always available.
|
|
14
15
|
*/
|
|
15
|
-
export declare const BuiltInPlugins: [["file:description", typeof FlowrAnalyzerDescriptionFilePlugin], ["versions:description", typeof FlowrAnalyzerPackageVersionsDescriptionFilePlugin], ["loading-order:description", typeof FlowrAnalyzerLoadingOrderDescriptionFilePlugin], ["files:vignette", typeof FlowrAnalyzerMetaVignetteFilesPlugin], ["files:test", typeof FlowrAnalyzerMetaTestFilesPlugin], ["file:rmd", typeof FlowrAnalyzerRmdFilePlugin], ["file:qmd", typeof FlowrAnalyzerQmdFilePlugin], ["file:ipynb", typeof FlowrAnalyzerJupyterFilePlugin], ["file:namespace", typeof FlowrAnalyzerNamespaceFilesPlugin], ["file:news", typeof FlowrAnalyzerNewsFilePlugin]];
|
|
16
|
+
export declare const BuiltInPlugins: [["file:description", typeof FlowrAnalyzerDescriptionFilePlugin], ["versions:description", typeof FlowrAnalyzerPackageVersionsDescriptionFilePlugin], ["loading-order:description", typeof FlowrAnalyzerLoadingOrderDescriptionFilePlugin], ["files:vignette", typeof FlowrAnalyzerMetaVignetteFilesPlugin], ["files:test", typeof FlowrAnalyzerMetaTestFilesPlugin], ["file:rmd", typeof FlowrAnalyzerRmdFilePlugin], ["file:qmd", typeof FlowrAnalyzerQmdFilePlugin], ["file:ipynb", typeof FlowrAnalyzerJupyterFilePlugin], ["file:namespace", typeof FlowrAnalyzerNamespaceFilesPlugin], ["file:news", typeof FlowrAnalyzerNewsFilePlugin], ["file:license", typeof FlowrAnalyzerLicenseFilePlugin]];
|
|
16
17
|
export type BuiltInFlowrPluginName = typeof BuiltInPlugins[number][0];
|
|
17
18
|
export type BuiltInFlowrPluginArgs<N extends BuiltInFlowrPluginName> = N extends typeof BuiltInPlugins[number][0] ? ConstructorParameters<Extract<typeof BuiltInPlugins[number], [N, PluginProducer]>[1]> : never;
|
|
18
19
|
type PluginProducer = new (...args: never[]) => FlowrAnalyzerPlugin;
|
|
@@ -15,6 +15,7 @@ const flowr_analyzer_namespace_files_plugin_1 = require("./file-plugins/flowr-an
|
|
|
15
15
|
const flowr_analyzer_news_file_plugin_1 = require("./file-plugins/flowr-analyzer-news-file-plugin");
|
|
16
16
|
const flowr_analyzer_vignette_file_plugin_1 = require("./file-plugins/flowr-analyzer-vignette-file-plugin");
|
|
17
17
|
const flowr_analyzer_test_file_plugin_1 = require("./file-plugins/flowr-analyzer-test-file-plugin");
|
|
18
|
+
const flowr_analyzer_license_file_plugin_1 = require("./file-plugins/flowr-analyzer-license-file-plugin");
|
|
18
19
|
/**
|
|
19
20
|
* The built-in Flowr Analyzer plugins that are always available.
|
|
20
21
|
*/
|
|
@@ -28,7 +29,8 @@ exports.BuiltInPlugins = [
|
|
|
28
29
|
['file:qmd', flowr_analyzer_qmd_file_plugin_1.FlowrAnalyzerQmdFilePlugin],
|
|
29
30
|
['file:ipynb', flowr_analyzer_jupyter_file_plugin_1.FlowrAnalyzerJupyterFilePlugin],
|
|
30
31
|
['file:namespace', flowr_analyzer_namespace_files_plugin_1.FlowrAnalyzerNamespaceFilesPlugin],
|
|
31
|
-
['file:news', flowr_analyzer_news_file_plugin_1.FlowrAnalyzerNewsFilePlugin]
|
|
32
|
+
['file:news', flowr_analyzer_news_file_plugin_1.FlowrAnalyzerNewsFilePlugin],
|
|
33
|
+
['file:license', flowr_analyzer_license_file_plugin_1.FlowrAnalyzerLicenseFilePlugin]
|
|
32
34
|
];
|
|
33
35
|
/**
|
|
34
36
|
* The registry of built-in and user-registered Flowr Analyzer plugins.
|
|
@@ -57,7 +57,6 @@ class DefaultFlowrAnalyzerProjectDiscoveryPlugin extends FlowrAnalyzerProjectDis
|
|
|
57
57
|
const requests = [];
|
|
58
58
|
/* the dummy approach of collecting all files, group R and Rmd files, and be done with it */
|
|
59
59
|
for (const file of (0, files_1.getAllFilesSync)(args.content, /.*/, this.ignorePathsRegex)) {
|
|
60
|
-
console.log(`Discovered file: ${file}`);
|
|
61
60
|
const relativePath = path_1.default.relative(args.content, file);
|
|
62
61
|
if (this.supportedExtensions.test(relativePath) && (!this.onlyTraversePaths || this.onlyTraversePaths.test(relativePath)) && !this.excludePathsRegex.test((0, built_in_source_1.platformDirname)(relativePath))) {
|
|
63
62
|
requests.push({ content: file, request: 'file' });
|
|
@@ -4,7 +4,7 @@ import { type OutputFormatter } from '../../../util/text/ansi';
|
|
|
4
4
|
import Joi from 'joi';
|
|
5
5
|
import type { FlowrConfigOptions } from '../../../config';
|
|
6
6
|
import type { DeepPartial } from 'ts-essentials';
|
|
7
|
-
import type { ParsedQueryLine } from '../../query';
|
|
7
|
+
import type { ParsedQueryLine, Query } from '../../query';
|
|
8
8
|
import type { ReplOutput } from '../../../cli/repl/commands/repl-main';
|
|
9
9
|
import type { CommandCompletions } from '../../../cli/repl/core';
|
|
10
10
|
export interface ConfigQuery extends BaseQueryFormat {
|
|
@@ -18,7 +18,7 @@ declare function configReplCompleter(partialLine: readonly string[], _startingNe
|
|
|
18
18
|
declare function configQueryLineParser(output: ReplOutput, line: readonly string[], _config: FlowrConfigOptions): ParsedQueryLine<'config'>;
|
|
19
19
|
export declare const ConfigQueryDefinition: {
|
|
20
20
|
readonly executor: typeof executeConfigQuery;
|
|
21
|
-
readonly asciiSummarizer: (formatter: OutputFormatter, _analyzer: unknown, queryResults: BaseQueryResult, result: string[]) => true;
|
|
21
|
+
readonly asciiSummarizer: (formatter: OutputFormatter, _analyzer: unknown, queryResults: BaseQueryResult, result: string[], queries: readonly Query[]) => true;
|
|
22
22
|
readonly completer: typeof configReplCompleter;
|
|
23
23
|
readonly fromLine: typeof configQueryLineParser;
|
|
24
24
|
readonly schema: Joi.ObjectSchema<any>;
|