@eagleoutice/flowr 2.9.9 → 2.9.10
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 +34 -29
- package/abstract-interpretation/absint-visitor.d.ts +16 -14
- package/abstract-interpretation/absint-visitor.js +91 -46
- package/abstract-interpretation/data-frame/shape-inference.d.ts +2 -5
- package/abstract-interpretation/data-frame/shape-inference.js +4 -5
- package/abstract-interpretation/domains/abstract-domain.d.ts +4 -4
- package/abstract-interpretation/domains/abstract-domain.js +8 -8
- package/abstract-interpretation/domains/mapped-abstract-domain.d.ts +12 -5
- package/abstract-interpretation/domains/mapped-abstract-domain.js +47 -23
- package/abstract-interpretation/domains/state-abstract-domain.d.ts +30 -1
- package/abstract-interpretation/domains/state-abstract-domain.js +130 -4
- package/control-flow/cfg-simplification.d.ts +1 -0
- package/control-flow/cfg-simplification.js +1 -0
- package/control-flow/extract-cfg.js +33 -15
- package/control-flow/semantic-cfg-guided-visitor.js +1 -0
- package/dataflow/environments/built-in.d.ts +4 -0
- package/dataflow/environments/built-in.js +17 -5
- package/dataflow/environments/default-builtin-config.d.ts +4 -8
- package/dataflow/environments/default-builtin-config.js +8 -5
- package/dataflow/environments/reference-to-maybe.d.ts +8 -0
- package/dataflow/environments/reference-to-maybe.js +46 -3
- package/dataflow/eval/resolve/alias-tracking.d.ts +1 -1
- package/dataflow/eval/resolve/alias-tracking.js +4 -5
- package/dataflow/eval/resolve/resolve.js +8 -7
- package/dataflow/graph/graph.d.ts +1 -1
- package/dataflow/graph/graph.js +9 -10
- package/dataflow/graph/unknown-side-effect.js +3 -1
- package/dataflow/info.d.ts +4 -0
- package/dataflow/info.js +2 -2
- package/dataflow/internal/linker.d.ts +2 -2
- package/dataflow/internal/linker.js +52 -27
- package/dataflow/internal/process/functions/call/argument/make-argument.js +2 -1
- package/dataflow/internal/process/functions/call/built-in/built-in-for-loop.js +15 -6
- package/dataflow/internal/process/functions/call/built-in/built-in-repeat-loop.js +2 -2
- package/dataflow/internal/process/functions/call/built-in/built-in-s-seven-new-generic.js +4 -4
- package/dataflow/internal/process/functions/call/built-in/built-in-source.js +22 -11
- package/dataflow/internal/process/functions/call/built-in/built-in-while-loop.js +22 -19
- package/dataflow/internal/process/functions/call/known-call-handling.js +0 -2
- package/documentation/wiki-absint.js +7 -8
- package/linter/rules/dead-code.js +1 -1
- package/linter/rules/seeded-randomness.js +1 -1
- package/linter/rules/unused-definition.js +1 -1
- package/package.json +7 -7
- package/project/plugins/file-plugins/files/flowr-description-file.d.ts +4 -0
- package/project/plugins/file-plugins/files/flowr-description-file.js +4 -0
- package/queries/catalog/call-context-query/call-context-query-executor.js +1 -1
- package/queries/catalog/df-shape-query/df-shape-query-format.d.ts +1 -1
- package/queries/catalog/df-shape-query/df-shape-query-format.js +3 -3
- package/r-bridge/lang-4.x/ast/model/processing/decorate.js +23 -17
- package/r-bridge/lang-4.x/ast/model/processing/role.d.ts +18 -17
- package/r-bridge/lang-4.x/tree-sitter/tree-sitter-normalize.js +31 -13
- package/statistics/features/supported/data-access/data-access.js +2 -2
- package/util/mermaid/ast.js +1 -1
- package/util/mermaid/dfg.js +6 -5
- package/util/version.js +1 -1
|
@@ -14,6 +14,7 @@ const general_1 = require("../../../../../eval/values/general");
|
|
|
14
14
|
const alias_tracking_1 = require("../../../../../eval/resolve/alias-tracking");
|
|
15
15
|
const reference_to_maybe_1 = require("../../../../../environments/reference-to-maybe");
|
|
16
16
|
const built_in_1 = require("../../../../../environments/built-in");
|
|
17
|
+
const append_1 = require("../../../../../environments/append");
|
|
17
18
|
/**
|
|
18
19
|
* Process a while loop like `while(cond) { ... }`.
|
|
19
20
|
*/
|
|
@@ -27,6 +28,8 @@ function processWhileLoop(name, args, rootId, data) {
|
|
|
27
28
|
logger_1.dataflowLogger.warn(`While-Loop ${identifier_1.Identifier.toString(name.content)} has empty arguments in ${JSON.stringify(args)}, skipping`);
|
|
28
29
|
return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, origin: 'default' }).information;
|
|
29
30
|
}
|
|
31
|
+
const nameId = name.info.id;
|
|
32
|
+
const origEnv = data.environment;
|
|
30
33
|
// we should defer this to the abstract interpretation
|
|
31
34
|
const values = (0, alias_tracking_1.resolveIdToValue)(unpackedArgs[0]?.info.id, { environment: data.environment, idMap: data.completeAst.idMap, resolve: data.ctx.config.solver.variables, ctx: data.ctx });
|
|
32
35
|
const conditionIsAlwaysFalse = (0, general_1.valueSetGuard)(values)?.elements.every(d => d.type === 'logical' && d.value === false) ?? false;
|
|
@@ -41,49 +44,49 @@ function processWhileLoop(name, args, rootId, data) {
|
|
|
41
44
|
rootId,
|
|
42
45
|
data,
|
|
43
46
|
markAsNSE: [1],
|
|
44
|
-
|
|
45
|
-
if (i === 1) {
|
|
46
|
-
return { ...d, cds: [...d.cds ?? [], { id: name.info.id, when: true }] };
|
|
47
|
-
}
|
|
48
|
-
return d;
|
|
49
|
-
}, origin: built_in_1.BuiltInProcName.WhileLoop
|
|
47
|
+
origin: built_in_1.BuiltInProcName.WhileLoop
|
|
50
48
|
});
|
|
51
49
|
const [condition, body] = processedArguments;
|
|
52
50
|
// If the condition is always false, we don't include the body
|
|
53
51
|
if (condition !== undefined && conditionIsAlwaysFalse) {
|
|
54
|
-
information.graph.addEdge(
|
|
52
|
+
information.graph.addEdge(nameId, condition.entryPoint, edge_1.EdgeType.Reads);
|
|
55
53
|
return {
|
|
56
54
|
unknownReferences: [],
|
|
57
|
-
in: [{ nodeId:
|
|
55
|
+
in: [{ nodeId: nameId, name: name.lexeme, cds: data.cds, type: identifier_1.ReferenceType.Function }],
|
|
58
56
|
out: condition.out,
|
|
59
|
-
entryPoint:
|
|
57
|
+
entryPoint: nameId,
|
|
60
58
|
exitPoints: [],
|
|
61
59
|
graph: information.graph,
|
|
62
60
|
environment: information.environment,
|
|
63
61
|
hooks: condition.hooks
|
|
64
62
|
};
|
|
65
63
|
}
|
|
64
|
+
const conditionIsAlwaysTrue = (0, general_1.valueSetGuard)(values)?.elements.every(d => d.type === 'logical' && d.value === true) ?? false;
|
|
66
65
|
(0, assert_1.guard)(condition !== undefined && body !== undefined, () => `While-Loop ${identifier_1.Identifier.toString(name.content)} has no condition or body, impossible!`);
|
|
67
66
|
const originalDependency = data.cds;
|
|
68
67
|
if ((0, info_1.alwaysExits)(condition)) {
|
|
69
68
|
logger_1.dataflowLogger.warn(`While-Loop ${rootId} forces exit in condition, skipping rest`);
|
|
70
|
-
information.graph.addEdge(
|
|
69
|
+
information.graph.addEdge(nameId, condition.entryPoint, edge_1.EdgeType.Reads);
|
|
71
70
|
return condition;
|
|
72
71
|
}
|
|
73
|
-
const cdTrue = [{ id:
|
|
74
|
-
const
|
|
75
|
-
(0,
|
|
76
|
-
(0, linker_1.
|
|
72
|
+
const cdTrue = [{ id: nameId, when: true }];
|
|
73
|
+
const bodyRead = body.in.concat(body.unknownReferences);
|
|
74
|
+
(0, reference_to_maybe_1.applyCdsToAllInGraphButConstants)(body.graph, bodyRead, cdTrue);
|
|
75
|
+
const remainingInputs = (0, linker_1.linkInputs)(bodyRead, information.environment, condition.in.concat(condition.unknownReferences), information.graph, true);
|
|
76
|
+
(0, reference_to_maybe_1.applyCdToReferences)(body.out, cdTrue);
|
|
77
|
+
(0, linker_1.linkCircularRedefinitionsWithinALoop)(information.graph, (0, linker_1.produceNameSharedIdMap)((0, linker_1.findNonLocalReads)(information.graph, new Set(condition.in.map(i => i.nodeId)))), body.out);
|
|
78
|
+
(0, linker_1.reapplyLoopExitPoints)(body.exitPoints, body.in.concat(body.out, body.unknownReferences), information.graph);
|
|
77
79
|
// as the while-loop always evaluates its condition
|
|
78
|
-
information.graph.addEdge(
|
|
80
|
+
information.graph.addEdge(nameId, condition.entryPoint, edge_1.EdgeType.Reads);
|
|
79
81
|
return {
|
|
80
82
|
unknownReferences: [],
|
|
81
|
-
in: [{ nodeId:
|
|
82
|
-
out: condition.out.concat(
|
|
83
|
-
entryPoint:
|
|
83
|
+
in: [{ nodeId: nameId, name: name.lexeme, cds: originalDependency, type: identifier_1.ReferenceType.Function }, ...remainingInputs],
|
|
84
|
+
out: condition.out.concat(body.out),
|
|
85
|
+
entryPoint: nameId,
|
|
84
86
|
exitPoints: (0, info_1.filterOutLoopExitPoints)(body.exitPoints),
|
|
85
87
|
graph: information.graph,
|
|
86
|
-
|
|
88
|
+
// as we do not know whether the loop executes at all, we have to merge the environments of the condition and the body, as both may be relevant
|
|
89
|
+
environment: conditionIsAlwaysTrue ? information.environment : (0, append_1.appendEnvironment)(origEnv, information.environment),
|
|
87
90
|
hooks: condition.hooks.concat(body.hooks)
|
|
88
91
|
};
|
|
89
92
|
}
|
|
@@ -9,7 +9,6 @@ const graph_1 = require("../../../../graph/graph");
|
|
|
9
9
|
const edge_1 = require("../../../../graph/edge");
|
|
10
10
|
const logger_1 = require("../../../../logger");
|
|
11
11
|
const vertex_1 = require("../../../../graph/vertex");
|
|
12
|
-
const log_1 = require("../../../../../util/log");
|
|
13
12
|
const unknown_side_effect_1 = require("../../../../graph/unknown-side-effect");
|
|
14
13
|
const built_in_1 = require("../../../../environments/built-in");
|
|
15
14
|
/**
|
|
@@ -36,7 +35,6 @@ function processKnownFunctionCall({ name, args, rootId, data, reverseOrder = fal
|
|
|
36
35
|
const functionName = (0, processor_1.processDataflowFor)(name, data);
|
|
37
36
|
const finalGraph = new graph_1.DataflowGraph(data.completeAst.idMap);
|
|
38
37
|
const functionCallName = name.content;
|
|
39
|
-
(0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Processing known function call ${identifier_1.Identifier.toString(functionCallName)} with ${args.length} arguments`);
|
|
40
38
|
const processArgs = reverseOrder ? args.toReversed() : args;
|
|
41
39
|
const { finalEnv, callArgs, remainingReadInArgs, processedArguments } = (0, common_1.processAllArguments)({ functionName, args: processArgs, data, finalGraph, functionRootId: rootId, patchData, forceArgs });
|
|
42
40
|
if (markAsNSE) {
|
|
@@ -17,7 +17,7 @@ class IntervalInferenceVisitor extends absint_visitor_1.AbstractInterpretationVi
|
|
|
17
17
|
onNumberConstant({ vertex, node }) {
|
|
18
18
|
super.onNumberConstant({ vertex, node });
|
|
19
19
|
const interval = new interval_domain_1.IntervalDomain([node.content.num, node.content.num]);
|
|
20
|
-
this.
|
|
20
|
+
this.updateState(node.info.id, interval);
|
|
21
21
|
}
|
|
22
22
|
onFunctionCall({ call }) {
|
|
23
23
|
super.onFunctionCall({ call });
|
|
@@ -31,9 +31,9 @@ class IntervalInferenceVisitor extends absint_visitor_1.AbstractInterpretationVi
|
|
|
31
31
|
// We map the numerical operation to the resulting interval after applying the abstract semantics of the operation
|
|
32
32
|
switch (identifier_1.Identifier.getName(call.name)) {
|
|
33
33
|
case '+':
|
|
34
|
-
return this.
|
|
34
|
+
return this.updateState(call.id, left.add(right));
|
|
35
35
|
case '-':
|
|
36
|
-
return this.
|
|
36
|
+
return this.updateState(call.id, left.subtract(right));
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
}
|
|
@@ -51,8 +51,7 @@ async function inferIntervals() {
|
|
|
51
51
|
const dfg = (await analyzer.dataflow()).graph;
|
|
52
52
|
const cfg = await analyzer.controlflow(undefined, cfg_kind_1.CfgKind.NoFunctionDefs);
|
|
53
53
|
const ctx = analyzer.inspectContext();
|
|
54
|
-
const
|
|
55
|
-
const inference = new IntervalInferenceVisitor({ controlFlow: cfg, dfg: dfg, normalizedAst: ast, ctx: ctx, domain: domain });
|
|
54
|
+
const inference = new IntervalInferenceVisitor({ controlFlow: cfg, dfg: dfg, normalizedAst: ast, ctx: ctx });
|
|
56
55
|
inference.start();
|
|
57
56
|
const result = inference.getEndState();
|
|
58
57
|
return result.value.entries().toArray()
|
|
@@ -128,15 +127,15 @@ For example, if we want to perform a (very basic) interval analysis using abstra
|
|
|
128
127
|
|
|
129
128
|
${ctx.code(IntervalInferenceVisitor)}
|
|
130
129
|
|
|
131
|
-
The interval inference visitor first overrides the ${ctx.link(`${semantic_cfg_guided_visitor_1.SemanticCfgGuidedVisitor.name}:::onNumberConstant`)} function to infer intervals for visited control flow vertices that represent numeric constants. For numeric constants, the resulting interval consists just of the number value of the constant. We then update the current abstract state of the visitor by setting the inferred abstract value of the currently visited control flow vertex to the new interval.
|
|
130
|
+
The interval inference visitor first overrides the ${ctx.link(`${semantic_cfg_guided_visitor_1.SemanticCfgGuidedVisitor.name}:::onNumberConstant`)} function to infer intervals for visited control flow vertices that represent numeric constants. For numeric constants, the resulting interval consists just of the number value of the constant. We then update the current abstract state of the visitor via ${ctx.linkM(absint_visitor_1.AbstractInterpretationVisitor, 'updateState', { hideClass: true })} by setting the inferred abstract value of the currently visited control flow vertex to the new interval.
|
|
132
131
|
|
|
133
|
-
In this simple example, we only want to support the addition and subtraction of numeric values. Therefore, we override the ${ctx.link(`${absint_visitor_1.AbstractInterpretationVisitor.name}:::onFunctionCall`)} function to apply the abstract semantics of additions and subtraction with resprect to the interval domain. For the addition and subtraction, we are only interested in function calls with exactly two non-empty arguments. We first resolve the currently inferred abstract value for the left and right operand of the function call. If we have no inferred value for one of the operands, this function call might not be a numeric function call and we ignore it. Otherwise, we check whether the function call represents an addition or subtraction and apply the abstract semantics of the operation to the left and right operand. We then again update the current abstract state of the visitor by setting the inferred abstract value of the currently visited function call vertex to the abstract value resulting from applying the abstract semantics of the operation to the operands.
|
|
132
|
+
In this simple example, we only want to support the addition and subtraction of numeric values. Therefore, we override the ${ctx.link(`${absint_visitor_1.AbstractInterpretationVisitor.name}:::onFunctionCall`)} function to apply the abstract semantics of additions and subtraction with resprect to the interval domain. For the addition and subtraction, we are only interested in function calls with exactly two non-empty arguments. We first resolve the currently inferred abstract value for the left and right operand of the function call. If we have no inferred value for one of the operands, this function call might not be a numeric function call and we ignore it. Otherwise, we check whether the function call represents an addition or subtraction and apply the abstract semantics of the operation to the left and right operand. We then again update the current abstract state of the visitor via ${ctx.linkM(absint_visitor_1.AbstractInterpretationVisitor, 'updateState', { hideClass: true })} by setting the inferred abstract value of the currently visited function call vertex to the abstract value resulting from applying the abstract semantics of the operation to the operands.
|
|
134
133
|
|
|
135
134
|
If we now want to run the interval inference, we can write the following code:
|
|
136
135
|
|
|
137
136
|
${ctx.code(inferIntervals, { dropLinesStart: 1, dropLinesEnd: 5 })}
|
|
138
137
|
|
|
139
|
-
We first need a ${ctx.linkPage('wiki/Analyzer', 'flowR analyzer')} (in this case, using the ${ctx.linkPage('wiki/Engines', 'tree-sitter engine')}). In this example, we want to analyze a small example code that assigns \`42\` to the variable \`x\`, randomly assigns \`6\` or \`12\` to the variable \`y\`, and assignes the sum of \`x\` and \`y\` to the variable \`z\`. For the abstract interpretation visitor, we need to retrieve the ${ctx.linkPage('wiki/Normalized AST', 'normalized AST')}, ${ctx.linkPage('wiki/Dataflow Graph', 'dataflow graph')}, ${ctx.linkPage('wiki/Control Flow Graph', 'control flow graph')}, and context of the flowR anaylzer.
|
|
138
|
+
We first need a ${ctx.linkPage('wiki/Analyzer', 'flowR analyzer')} (in this case, using the ${ctx.linkPage('wiki/Engines', 'tree-sitter engine')}). In this example, we want to analyze a small example code that assigns \`42\` to the variable \`x\`, randomly assigns \`6\` or \`12\` to the variable \`y\`, and assignes the sum of \`x\` and \`y\` to the variable \`z\`. For the abstract interpretation visitor, we need to retrieve the ${ctx.linkPage('wiki/Normalized AST', 'normalized AST')}, ${ctx.linkPage('wiki/Dataflow Graph', 'dataflow graph')}, ${ctx.linkPage('wiki/Control Flow Graph', 'control flow graph')}, and context of the flowR anaylzer. For performance reasons, we construct the control flow graph without simplification passes, data flow information, and function definitions. We then create a new ${ctx.link(IntervalInferenceVisitor)} using the control flow graph, dataflow graph, normalized AST, and analyzer context, and start the visitor using ${ctx.linkM(absint_visitor_1.AbstractInterpretationVisitor, 'start', { hideClass: true })}. After the visitor is finished, we retrieve the inferred abstract state at the end of the program using ${ctx.linkM(absint_visitor_1.AbstractInterpretationVisitor, 'getEndState', { hideClass: true })}.
|
|
140
139
|
|
|
141
140
|
If we now print the inferred abstract state at the end of the program, we get the following output:
|
|
142
141
|
|
|
@@ -22,7 +22,7 @@ exports.DEAD_CODE = {
|
|
|
22
22
|
.filter(element => {
|
|
23
23
|
meta.consideredNodes++;
|
|
24
24
|
const cfgInformation = (0, search_enrichers_1.enrichmentContent)(element, search_enrichers_1.Enrichment.CfgInformation);
|
|
25
|
-
return
|
|
25
|
+
return element.node.info.role !== "el-g" /* RoleInParent.ExpressionListGrouping */ && !cfgInformation.isReachable;
|
|
26
26
|
})
|
|
27
27
|
.map(element => ({
|
|
28
28
|
certainty: linter_format_1.LintingResultCertainty.Certain,
|
|
@@ -35,7 +35,7 @@ exports.SEEDED_RANDOMNESS = {
|
|
|
35
35
|
{ callName: getDefaultAssignments().flatMap(b => b.names).map(identifier_1.Identifier.getName), cascadeIf: () => cascade_action_1.CascadeAction.Continue }
|
|
36
36
|
]),
|
|
37
37
|
processSearchResult: (elements, config, { dataflow, analyzer }) => {
|
|
38
|
-
const assignmentProducers = new Set(config.randomnessProducers.filter(p => p.type
|
|
38
|
+
const assignmentProducers = new Set(config.randomnessProducers.filter(p => p.type === 'assignment').map(p => p.name));
|
|
39
39
|
const assignmentArgIndexes = new Map(getDefaultAssignments().flatMap(a => a.names.map(n => ([identifier_1.Identifier.getName(n), a.config?.swapSourceAndTarget ? 1 : 0]))));
|
|
40
40
|
const metadata = {
|
|
41
41
|
consumerCalls: 0,
|
|
@@ -19,7 +19,7 @@ function getDefinitionArguments(def, dfg) {
|
|
|
19
19
|
function buildQuickFix(variable, dfg, ast) {
|
|
20
20
|
// first we check whether any of the 'Defined by' targets have any obligations - if so, we can not remove the definition
|
|
21
21
|
// otherwise we can automatically remove the full definition!
|
|
22
|
-
if (variable.info.role === "
|
|
22
|
+
if (variable.info.role === "acc" /* RoleInParent.Accessed */ || variable.info.role === "for-var" /* RoleInParent.ForVariable */) {
|
|
23
23
|
// this is an access or a for variable, we can not remove it currently
|
|
24
24
|
return undefined;
|
|
25
25
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eagleoutice/flowr",
|
|
3
|
-
"version": "2.9.
|
|
3
|
+
"version": "2.9.10",
|
|
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": {
|
|
@@ -199,7 +199,7 @@
|
|
|
199
199
|
},
|
|
200
200
|
"dependencies": {
|
|
201
201
|
"@eagleoutice/tree-sitter-r": "^1.1.2",
|
|
202
|
-
"@jupyterlab/nbformat": "^4.5.
|
|
202
|
+
"@jupyterlab/nbformat": "^4.5.4",
|
|
203
203
|
"@xmldom/xmldom": "^0.9.7",
|
|
204
204
|
"clipboardy": "^4.0.0",
|
|
205
205
|
"command-line-args": "^6.0.1",
|
|
@@ -209,17 +209,17 @@
|
|
|
209
209
|
"gray-matter": "^4.0.3",
|
|
210
210
|
"joi": "^18.0.1",
|
|
211
211
|
"lz-string": "^1.5.0",
|
|
212
|
-
"n-readlines": "^1.0.
|
|
213
|
-
"n3": "^1.
|
|
212
|
+
"n-readlines": "^1.0.3",
|
|
213
|
+
"n3": "^1.26.0",
|
|
214
214
|
"object-hash": "^3.0.0",
|
|
215
215
|
"object-sizeof": "^2.6.5",
|
|
216
|
-
"rotating-file-stream": "^3.2.
|
|
216
|
+
"rotating-file-stream": "^3.2.8",
|
|
217
217
|
"seedrandom": "^3.0.5",
|
|
218
|
-
"semver": "^7.7.
|
|
218
|
+
"semver": "^7.7.4",
|
|
219
219
|
"tar": "^7.4.3",
|
|
220
220
|
"tmp": "^0.2.3",
|
|
221
221
|
"ts-essentials": "^10.1.1",
|
|
222
|
-
"tslog": "^4.
|
|
222
|
+
"tslog": "^4.10.2",
|
|
223
223
|
"web-tree-sitter": "^0.24.7",
|
|
224
224
|
"ws": "^8.18.0",
|
|
225
225
|
"xpath-ts2": "^1.4.2"
|
|
@@ -24,6 +24,10 @@ export declare class FlowrDescriptionFile extends FlowrFile<DeepReadonly<DCF>> {
|
|
|
24
24
|
* @see {@link parseDCF} for details on the parsing logic.
|
|
25
25
|
*/
|
|
26
26
|
protected loadContent(): DCF;
|
|
27
|
+
/**
|
|
28
|
+
* Creates a FlowrDescriptionFile from given DCF content, path and optional roles.
|
|
29
|
+
* This is useful if you already have the DCF content parsed and want to create a description file instance without re-parsing.
|
|
30
|
+
*/
|
|
27
31
|
static fromDCF(dcf: DCF, path: string, roles?: FileRole[]): FlowrDescriptionFile;
|
|
28
32
|
/**
|
|
29
33
|
* Description file lifter, this does not re-create if already a description file
|
|
@@ -34,6 +34,10 @@ class FlowrDescriptionFile extends flowr_file_1.FlowrFile {
|
|
|
34
34
|
loadContent() {
|
|
35
35
|
return parseDCF(this.wrapped);
|
|
36
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Creates a FlowrDescriptionFile from given DCF content, path and optional roles.
|
|
39
|
+
* This is useful if you already have the DCF content parsed and want to create a description file instance without re-parsing.
|
|
40
|
+
*/
|
|
37
41
|
static fromDCF(dcf, path, roles) {
|
|
38
42
|
const file = new FlowrDescriptionFile(new flowr_file_1.FlowrTextFile(path, roles));
|
|
39
43
|
file.setContent(dcf);
|
|
@@ -173,7 +173,7 @@ function doesFilepathMatch(file, filter) {
|
|
|
173
173
|
function isParameterDefaultValue(nodeId, ast) {
|
|
174
174
|
let node = ast.idMap.get(nodeId);
|
|
175
175
|
while (node !== undefined) {
|
|
176
|
-
if (node.info.role === "param-
|
|
176
|
+
if (node.info.role === "param-v" /* RoleInParent.ParameterDefaultValue */) {
|
|
177
177
|
return true;
|
|
178
178
|
}
|
|
179
179
|
const nip = node.info.parent;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Joi from 'joi';
|
|
2
2
|
import type { DataFrameDomain } from '../../../abstract-interpretation/data-frame/dataframe-domain';
|
|
3
|
-
import { StateAbstractDomain } from '../../../abstract-interpretation/domains/state-abstract-domain';
|
|
3
|
+
import type { StateAbstractDomain } from '../../../abstract-interpretation/domains/state-abstract-domain';
|
|
4
4
|
import type { ReplOutput } from '../../../cli/repl/commands/repl-main';
|
|
5
5
|
import type { FlowrConfigOptions } from '../../../config';
|
|
6
6
|
import type { SingleSlicingCriterion } from '../../../slicing/criterion/parse';
|
|
@@ -5,11 +5,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.DfShapeQueryDefinition = void 0;
|
|
7
7
|
const joi_1 = __importDefault(require("joi"));
|
|
8
|
-
const state_abstract_domain_1 = require("../../../abstract-interpretation/domains/state-abstract-domain");
|
|
9
8
|
const slice_query_parser_1 = require("../../../cli/repl/parser/slice-query-parser");
|
|
10
9
|
const ansi_1 = require("../../../util/text/ansi");
|
|
11
10
|
const time_1 = require("../../../util/text/time");
|
|
12
11
|
const df_shape_query_executor_1 = require("./df-shape-query-executor");
|
|
12
|
+
const abstract_domain_1 = require("../../../abstract-interpretation/domains/abstract-domain");
|
|
13
13
|
function dfShapeQueryLineParser(_output, line, _config) {
|
|
14
14
|
const criterion = (0, slice_query_parser_1.sliceCriterionParser)(line[0]);
|
|
15
15
|
return {
|
|
@@ -24,7 +24,7 @@ exports.DfShapeQueryDefinition = {
|
|
|
24
24
|
executor: df_shape_query_executor_1.executeDfShapeQuery,
|
|
25
25
|
asciiSummarizer: (formatter, _analyzer, queryResults, result) => {
|
|
26
26
|
const out = queryResults;
|
|
27
|
-
const domains = out.domains instanceof
|
|
27
|
+
const domains = out.domains instanceof abstract_domain_1.AbstractDomain ? out.domains.value : out.domains;
|
|
28
28
|
result.push(`Query: ${(0, ansi_1.bold)('df-shape', formatter)} (${(0, time_1.printAsMs)(out['.meta'].timing, 0)})`);
|
|
29
29
|
result.push(...domains.entries().take(20).map(([key, domain]) => {
|
|
30
30
|
return ` ╰ ${key}: ${domain?.toString()}`;
|
|
@@ -36,7 +36,7 @@ exports.DfShapeQueryDefinition = {
|
|
|
36
36
|
},
|
|
37
37
|
jsonFormatter: (queryResults) => {
|
|
38
38
|
const { domains, ...out } = queryResults;
|
|
39
|
-
const state = domains instanceof
|
|
39
|
+
const state = domains instanceof abstract_domain_1.AbstractDomain ? domains.value : domains;
|
|
40
40
|
const json = Object.fromEntries(state.entries().map(([key, domain]) => [key, domain?.toJson() ?? null]));
|
|
41
41
|
const result = { domains: json, ...out };
|
|
42
42
|
return result;
|
|
@@ -173,12 +173,12 @@ function createFoldForBinaryOp(info) {
|
|
|
173
173
|
rhsInfo.parent = id;
|
|
174
174
|
rhsInfo.index = 1;
|
|
175
175
|
if (data.type === type_1.RType.Pipe) {
|
|
176
|
-
lhsInfo.role = "pipe-
|
|
177
|
-
rhsInfo.role = "pipe-
|
|
176
|
+
lhsInfo.role = "pipe-l" /* RoleInParent.PipeLhs */;
|
|
177
|
+
rhsInfo.role = "pipe-r" /* RoleInParent.PipeRhs */;
|
|
178
178
|
}
|
|
179
179
|
else {
|
|
180
|
-
lhsInfo.role = "
|
|
181
|
-
rhsInfo.role = "
|
|
180
|
+
lhsInfo.role = "bin-l" /* RoleInParent.BinaryOperationLhs */;
|
|
181
|
+
rhsInfo.role = "bin-r" /* RoleInParent.BinaryOperationRhs */;
|
|
182
182
|
}
|
|
183
183
|
decorated.info.file = info.file;
|
|
184
184
|
return decorated;
|
|
@@ -203,7 +203,7 @@ function createFoldForAccess(info) {
|
|
|
203
203
|
info.idMap.set(id, decorated);
|
|
204
204
|
const accessedInfo = accessed.info;
|
|
205
205
|
accessedInfo.parent = id;
|
|
206
|
-
accessedInfo.role = "
|
|
206
|
+
accessedInfo.role = "acc" /* RoleInParent.Accessed */;
|
|
207
207
|
if (typeof access !== 'string') {
|
|
208
208
|
let idx = 0; // the first oe will be skipped in the first iter
|
|
209
209
|
for (const acc of access) {
|
|
@@ -212,7 +212,7 @@ function createFoldForAccess(info) {
|
|
|
212
212
|
const curInfo = acc.info;
|
|
213
213
|
curInfo.parent = id;
|
|
214
214
|
curInfo.index = idx;
|
|
215
|
-
curInfo.role = "
|
|
215
|
+
curInfo.role = "idx-acc" /* RoleInParent.IndexAccess */;
|
|
216
216
|
}
|
|
217
217
|
}
|
|
218
218
|
}
|
|
@@ -235,7 +235,7 @@ function createFoldForForLoop(info) {
|
|
|
235
235
|
const bodyInfo = body.info;
|
|
236
236
|
bodyInfo.parent = id;
|
|
237
237
|
bodyInfo.index = 2;
|
|
238
|
-
bodyInfo.role = "for-
|
|
238
|
+
bodyInfo.role = "for-b" /* RoleInParent.ForBody */;
|
|
239
239
|
decorated.info.file = info.file;
|
|
240
240
|
return decorated;
|
|
241
241
|
};
|
|
@@ -247,7 +247,7 @@ function createFoldForRepeatLoop(info) {
|
|
|
247
247
|
info.idMap.set(id, decorated);
|
|
248
248
|
const bodyInfo = body.info;
|
|
249
249
|
bodyInfo.parent = id;
|
|
250
|
-
bodyInfo.role = "repeat-
|
|
250
|
+
bodyInfo.role = "repeat-b" /* RoleInParent.RepeatBody */;
|
|
251
251
|
decorated.info.file = info.file;
|
|
252
252
|
return decorated;
|
|
253
253
|
};
|
|
@@ -259,11 +259,11 @@ function createFoldForWhileLoop(info) {
|
|
|
259
259
|
info.idMap.set(id, decorated);
|
|
260
260
|
const condInfo = condition.info;
|
|
261
261
|
condInfo.parent = id;
|
|
262
|
-
condInfo.role = "while-
|
|
262
|
+
condInfo.role = "while-c" /* RoleInParent.WhileCondition */;
|
|
263
263
|
const bodyInfo = body.info;
|
|
264
264
|
bodyInfo.parent = id;
|
|
265
265
|
bodyInfo.index = 1;
|
|
266
|
-
bodyInfo.role = "while-
|
|
266
|
+
bodyInfo.role = "while-b" /* RoleInParent.WhileBody */;
|
|
267
267
|
decorated.info.file = info.file;
|
|
268
268
|
return decorated;
|
|
269
269
|
};
|
|
@@ -275,7 +275,7 @@ function createFoldForIfThenElse(info) {
|
|
|
275
275
|
info.idMap.set(id, decorated);
|
|
276
276
|
const condInfo = condition.info;
|
|
277
277
|
condInfo.parent = id;
|
|
278
|
-
condInfo.role = "if-
|
|
278
|
+
condInfo.role = "if-c" /* RoleInParent.IfCondition */;
|
|
279
279
|
const thenInfo = then.info;
|
|
280
280
|
thenInfo.parent = id;
|
|
281
281
|
thenInfo.index = 1;
|
|
@@ -300,7 +300,13 @@ function createFoldForExprList(info) {
|
|
|
300
300
|
const childInfo = child.info;
|
|
301
301
|
childInfo.parent = id;
|
|
302
302
|
childInfo.index = i++;
|
|
303
|
-
childInfo.role = "
|
|
303
|
+
childInfo.role = "el-c" /* RoleInParent.ExpressionListChild */;
|
|
304
|
+
}
|
|
305
|
+
// assign role for grouping
|
|
306
|
+
if (grouping) {
|
|
307
|
+
const [open, close] = grouping;
|
|
308
|
+
open.info.role = "el-g" /* RoleInParent.ExpressionListGrouping */;
|
|
309
|
+
close.info.role = "el-g" /* RoleInParent.ExpressionListGrouping */;
|
|
304
310
|
}
|
|
305
311
|
decorated.info.file = info.file;
|
|
306
312
|
return decorated;
|
|
@@ -349,7 +355,7 @@ function createFoldForFunctionDefinition(info) {
|
|
|
349
355
|
const bodyInfo = body.info;
|
|
350
356
|
bodyInfo.parent = id;
|
|
351
357
|
bodyInfo.index = idx;
|
|
352
|
-
bodyInfo.role = "fun-
|
|
358
|
+
bodyInfo.role = "fun-b" /* RoleInParent.FunctionDefinitionBody */;
|
|
353
359
|
decorated.info.file = info.file;
|
|
354
360
|
return decorated;
|
|
355
361
|
};
|
|
@@ -361,12 +367,12 @@ function createFoldForFunctionParameter(info) {
|
|
|
361
367
|
info.idMap.set(id, decorated);
|
|
362
368
|
const nameInfo = name.info;
|
|
363
369
|
nameInfo.parent = id;
|
|
364
|
-
nameInfo.role = "param-
|
|
370
|
+
nameInfo.role = "param-n" /* RoleInParent.ParameterName */;
|
|
365
371
|
if (defaultValue) {
|
|
366
372
|
const defaultInfo = defaultValue.info;
|
|
367
373
|
defaultInfo.parent = id;
|
|
368
374
|
defaultInfo.index = 1;
|
|
369
|
-
defaultInfo.role = "param-
|
|
375
|
+
defaultInfo.role = "param-v" /* RoleInParent.ParameterDefaultValue */;
|
|
370
376
|
}
|
|
371
377
|
decorated.info.file = info.file;
|
|
372
378
|
return decorated;
|
|
@@ -381,14 +387,14 @@ function createFoldForFunctionArgument(info) {
|
|
|
381
387
|
if (name) {
|
|
382
388
|
const nameInfo = name.info;
|
|
383
389
|
nameInfo.parent = id;
|
|
384
|
-
nameInfo.role = "arg-
|
|
390
|
+
nameInfo.role = "arg-n" /* RoleInParent.ArgumentName */;
|
|
385
391
|
idx++; // adaptive, 0 for the value if there is no name!
|
|
386
392
|
}
|
|
387
393
|
if (value) {
|
|
388
394
|
const valueInfo = value.info;
|
|
389
395
|
valueInfo.parent = id;
|
|
390
396
|
valueInfo.index = idx;
|
|
391
|
-
valueInfo.role = "arg-
|
|
397
|
+
valueInfo.role = "arg-v" /* RoleInParent.ArgumentValue */;
|
|
392
398
|
}
|
|
393
399
|
decorated.info.file = info.file;
|
|
394
400
|
return decorated;
|
|
@@ -8,31 +8,32 @@ import type { AstIdMap, RNodeWithParent } from './decorate';
|
|
|
8
8
|
export declare const enum RoleInParent {
|
|
9
9
|
/** has no parent */
|
|
10
10
|
Root = "root",
|
|
11
|
-
IfCondition = "if-
|
|
11
|
+
IfCondition = "if-c",
|
|
12
12
|
IfThen = "if-then",
|
|
13
13
|
IfOtherwise = "if-other",
|
|
14
|
-
WhileCondition = "while-
|
|
15
|
-
WhileBody = "while-
|
|
16
|
-
RepeatBody = "repeat-
|
|
14
|
+
WhileCondition = "while-c",
|
|
15
|
+
WhileBody = "while-b",
|
|
16
|
+
RepeatBody = "repeat-b",
|
|
17
17
|
ForVariable = "for-var",
|
|
18
18
|
ForVector = "for-vec",
|
|
19
|
-
ForBody = "for-
|
|
19
|
+
ForBody = "for-b",
|
|
20
20
|
FunctionCallName = "call-name",
|
|
21
21
|
FunctionCallArgument = "call-arg",
|
|
22
|
-
FunctionDefinitionBody = "fun-
|
|
22
|
+
FunctionDefinitionBody = "fun-b",
|
|
23
23
|
FunctionDefinitionParameter = "param",
|
|
24
|
-
ExpressionListChild = "
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
ExpressionListChild = "el-c",
|
|
25
|
+
ExpressionListGrouping = "el-g",
|
|
26
|
+
BinaryOperationLhs = "bin-l",
|
|
27
|
+
BinaryOperationRhs = "bin-r",
|
|
28
|
+
PipeLhs = "pipe-l",
|
|
29
|
+
PipeRhs = "pipe-r",
|
|
29
30
|
UnaryOperand = "unary-op",
|
|
30
|
-
ParameterName = "param-
|
|
31
|
-
ParameterDefaultValue = "param-
|
|
32
|
-
ArgumentName = "arg-
|
|
33
|
-
ArgumentValue = "arg-
|
|
34
|
-
Accessed = "
|
|
35
|
-
IndexAccess = "
|
|
31
|
+
ParameterName = "param-n",
|
|
32
|
+
ParameterDefaultValue = "param-v",
|
|
33
|
+
ArgumentName = "arg-n",
|
|
34
|
+
ArgumentValue = "arg-v",
|
|
35
|
+
Accessed = "acc",
|
|
36
|
+
IndexAccess = "idx-acc"
|
|
36
37
|
}
|
|
37
38
|
/**
|
|
38
39
|
* Returns the roles of the parents of the given node, starting with the parent-role of the node itself.
|
|
@@ -260,7 +260,8 @@ function convertTreeNode(node) {
|
|
|
260
260
|
...defaultInfo
|
|
261
261
|
};
|
|
262
262
|
case tree_sitter_types_1.TreeSitterType.IfStatement: {
|
|
263
|
-
const [
|
|
263
|
+
const [comments, children] = splitComments(nonErrorChildren(node));
|
|
264
|
+
const [ifNode, /* ( */ , condition, /* ) */ , then, /* else */ , ...otherwise] = children;
|
|
264
265
|
const filteredOtherwise = otherwise.filter(n => n.type !== tree_sitter_types_1.TreeSitterType.ElseStatement);
|
|
265
266
|
return {
|
|
266
267
|
type: type_1.RType.IfThenElse,
|
|
@@ -269,11 +270,14 @@ function convertTreeNode(node) {
|
|
|
269
270
|
otherwise: filteredOtherwise.length > 0 ? (0, normalize_meta_1.ensureExpressionList)(convertTreeNode(filteredOtherwise[0])) : undefined,
|
|
270
271
|
location: makeSourceRange(ifNode),
|
|
271
272
|
lexeme: ifNode.text,
|
|
272
|
-
|
|
273
|
+
info: {
|
|
274
|
+
...defaultInfo.info,
|
|
275
|
+
adToks: comments.map(c => c[1]),
|
|
276
|
+
}
|
|
273
277
|
};
|
|
274
278
|
}
|
|
275
279
|
case tree_sitter_types_1.TreeSitterType.ForStatement: {
|
|
276
|
-
const children = nonErrorChildren(node);
|
|
280
|
+
const [comments, children] = splitComments(nonErrorChildren(node));
|
|
277
281
|
const forNode = children[0]; // we follow with a (
|
|
278
282
|
const variable = getNodesUntil(children, 'in', 2); // we follow with the "in"
|
|
279
283
|
const sequence = getNodesUntil(children, ')', 2 + variable.length + 1); // we follow with a (
|
|
@@ -300,31 +304,38 @@ function convertTreeNode(node) {
|
|
|
300
304
|
lexeme: forNode.text,
|
|
301
305
|
info: {
|
|
302
306
|
fullRange: range,
|
|
303
|
-
adToks: variableComments.concat(sequenceComments).map(c => c[1]),
|
|
307
|
+
adToks: variableComments.concat(comments, sequenceComments).map(c => c[1]),
|
|
304
308
|
fullLexeme: node.text,
|
|
305
309
|
tsId: node.id
|
|
306
310
|
}
|
|
307
311
|
};
|
|
308
312
|
}
|
|
309
313
|
case tree_sitter_types_1.TreeSitterType.WhileStatement: {
|
|
310
|
-
const [
|
|
314
|
+
const [comments, children] = splitComments(nonErrorChildren(node));
|
|
315
|
+
const [whileNode, /* ( */ , condition, /* ) */ , body] = children;
|
|
311
316
|
return {
|
|
312
317
|
type: type_1.RType.WhileLoop,
|
|
313
318
|
condition: convertTreeNode(condition),
|
|
314
319
|
body: (0, normalize_meta_1.ensureExpressionList)(convertTreeNode(body)),
|
|
315
320
|
location: makeSourceRange(whileNode),
|
|
316
321
|
lexeme: whileNode.text,
|
|
317
|
-
|
|
322
|
+
info: {
|
|
323
|
+
...defaultInfo.info,
|
|
324
|
+
adToks: comments.map(c => c[1]),
|
|
325
|
+
}
|
|
318
326
|
};
|
|
319
327
|
}
|
|
320
328
|
case tree_sitter_types_1.TreeSitterType.RepeatStatement: {
|
|
321
|
-
const [repeatNode, body] = nonErrorChildren(node);
|
|
329
|
+
const [comments, [repeatNode, body]] = splitComments(nonErrorChildren(node));
|
|
322
330
|
return {
|
|
323
331
|
type: type_1.RType.RepeatLoop,
|
|
324
332
|
body: (0, normalize_meta_1.ensureExpressionList)(convertTreeNode(body)),
|
|
325
333
|
location: makeSourceRange(repeatNode),
|
|
326
334
|
lexeme: repeatNode.text,
|
|
327
|
-
|
|
335
|
+
info: {
|
|
336
|
+
...defaultInfo.info,
|
|
337
|
+
adToks: comments.map(c => c[1]),
|
|
338
|
+
}
|
|
328
339
|
};
|
|
329
340
|
}
|
|
330
341
|
case tree_sitter_types_1.TreeSitterType.Call: {
|
|
@@ -449,7 +460,8 @@ function convertTreeNode(node) {
|
|
|
449
460
|
const [func, content] = nonErrorChildren(node);
|
|
450
461
|
// bracket is now [ or [[ and argsClosing is x] or x]]
|
|
451
462
|
const [bracket, ...argsClosing] = nonErrorChildren(content);
|
|
452
|
-
const
|
|
463
|
+
const [argsComments, argsNoComments] = splitComments(argsClosing.slice(0, -1));
|
|
464
|
+
const args = (0, arrays_1.splitArrayOn)(argsNoComments, x => x.type === 'comma');
|
|
453
465
|
return {
|
|
454
466
|
type: type_1.RType.Access,
|
|
455
467
|
operator: bracket.text,
|
|
@@ -457,7 +469,10 @@ function convertTreeNode(node) {
|
|
|
457
469
|
access: args.map(n => n.length === 0 ? r_function_call_1.EmptyArgument : convertTreeNode(n[0])),
|
|
458
470
|
location: makeSourceRange(bracket),
|
|
459
471
|
lexeme: bracket.text,
|
|
460
|
-
|
|
472
|
+
info: {
|
|
473
|
+
...defaultInfo.info,
|
|
474
|
+
adToks: argsComments.map(c => c[1]),
|
|
475
|
+
}
|
|
461
476
|
};
|
|
462
477
|
}
|
|
463
478
|
case tree_sitter_types_1.TreeSitterType.ExtractOperator: {
|
|
@@ -523,7 +538,7 @@ function convertTreeNode(node) {
|
|
|
523
538
|
};
|
|
524
539
|
}
|
|
525
540
|
case tree_sitter_types_1.TreeSitterType.Argument: {
|
|
526
|
-
const children = nonErrorChildren(node);
|
|
541
|
+
const [commentChildren, children] = splitComments(nonErrorChildren(node));
|
|
527
542
|
if (children.length === 1) {
|
|
528
543
|
const [arg] = children;
|
|
529
544
|
return {
|
|
@@ -532,7 +547,10 @@ function convertTreeNode(node) {
|
|
|
532
547
|
value: convertTreeNode(arg),
|
|
533
548
|
location: range,
|
|
534
549
|
lexeme: node.text,
|
|
535
|
-
|
|
550
|
+
info: {
|
|
551
|
+
...defaultInfo.info,
|
|
552
|
+
adToks: commentChildren.map(c => c[1]),
|
|
553
|
+
}
|
|
536
554
|
};
|
|
537
555
|
}
|
|
538
556
|
else {
|
|
@@ -558,7 +576,7 @@ function convertTreeNode(node) {
|
|
|
558
576
|
lexeme: nameNode.text,
|
|
559
577
|
info: {
|
|
560
578
|
fullRange: nameRange,
|
|
561
|
-
adToks: [],
|
|
579
|
+
adToks: commentChildren.map(c => c[1]),
|
|
562
580
|
fullLexeme: nameNode.text,
|
|
563
581
|
tsId: nameNode.id
|
|
564
582
|
}
|
|
@@ -54,11 +54,11 @@ function visitAccess(info, input) {
|
|
|
54
54
|
let acc = false;
|
|
55
55
|
let idxAcc = false;
|
|
56
56
|
for (const role of roles) {
|
|
57
|
-
if (role === "
|
|
57
|
+
if (role === "acc" /* RoleInParent.Accessed */) {
|
|
58
58
|
acc = true;
|
|
59
59
|
break; // we only account for the first one
|
|
60
60
|
}
|
|
61
|
-
else if (role === "
|
|
61
|
+
else if (role === "idx-acc" /* RoleInParent.IndexAccess */) {
|
|
62
62
|
idxAcc = true;
|
|
63
63
|
break;
|
|
64
64
|
}
|