@eagleoutice/flowr 2.0.16 → 2.0.18
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 +1 -1
- package/cli/repl/commands/commands.js +3 -1
- package/cli/repl/commands/lineage.d.ts +15 -0
- package/cli/repl/commands/lineage.js +66 -0
- package/cli/repl/server/connection.d.ts +1 -0
- package/cli/repl/server/connection.js +36 -2
- package/cli/repl/server/messages/lineage.d.ts +16 -0
- package/cli/repl/server/messages/lineage.js +17 -0
- package/cli/repl/server/messages/messages.d.ts +2 -1
- package/cli/slicer-app.js +2 -2
- package/dataflow/environments/built-in.js +29 -9
- package/dataflow/environments/environment.js +4 -3
- package/dataflow/environments/resolve-by-name.d.ts +1 -1
- package/dataflow/environments/resolve-by-name.js +4 -4
- package/dataflow/graph/diff.js +1 -0
- package/dataflow/graph/graph.d.ts +24 -20
- package/dataflow/graph/graph.js +51 -24
- package/dataflow/graph/vertex.d.ts +6 -1
- package/dataflow/graph/vertex.js +21 -0
- package/dataflow/info.d.ts +1 -1
- package/dataflow/internal/process/functions/call/built-in/built-in-access.d.ts +2 -1
- package/dataflow/internal/process/functions/call/built-in/built-in-access.js +29 -7
- package/dataflow/internal/process/functions/call/built-in/built-in-apply.d.ts +14 -0
- package/dataflow/internal/process/functions/call/built-in/built-in-apply.js +65 -0
- package/dataflow/internal/process/functions/call/built-in/built-in-assignment.d.ts +19 -2
- package/dataflow/internal/process/functions/call/built-in/built-in-assignment.js +40 -24
- package/dataflow/internal/process/functions/call/built-in/built-in-for-loop.js +1 -1
- package/dataflow/internal/process/functions/call/built-in/built-in-function-definition.js +10 -1
- package/dataflow/internal/process/functions/call/built-in/built-in-if-then-else.js +4 -4
- package/dataflow/internal/process/functions/call/built-in/built-in-library.js +7 -3
- package/dataflow/internal/process/functions/call/built-in/built-in-quote.d.ts +3 -2
- package/dataflow/internal/process/functions/call/built-in/built-in-quote.js +1 -1
- package/dataflow/internal/process/functions/call/built-in/built-in-replacement.d.ts +3 -2
- package/dataflow/internal/process/functions/call/built-in/built-in-replacement.js +2 -1
- package/dataflow/internal/process/functions/call/built-in/built-in-source.d.ts +2 -2
- package/dataflow/internal/process/functions/call/built-in/built-in-source.js +18 -7
- package/dataflow/internal/process/functions/call/built-in/{built-in-logical-bin-op.d.ts → built-in-special-bin-op.d.ts} +2 -1
- package/dataflow/internal/process/functions/call/built-in/{built-in-logical-bin-op.js → built-in-special-bin-op.js} +3 -3
- package/dataflow/internal/process/functions/call/common.d.ts +6 -2
- package/dataflow/internal/process/functions/call/common.js +36 -1
- package/dataflow/internal/process/functions/call/known-call-handling.d.ts +8 -3
- package/dataflow/internal/process/functions/call/known-call-handling.js +10 -7
- package/dataflow/internal/process/functions/call/named-call-handling.js +3 -1
- package/dataflow/internal/process/functions/call/unnamed-call-handling.js +1 -0
- package/dataflow/internal/process/functions/process-argument.js +0 -28
- package/package.json +3 -2
- package/r-bridge/data/data.d.ts +10 -0
- package/r-bridge/data/data.js +12 -0
- package/r-bridge/lang-4.x/ast/model/operators.js +1 -1
- package/reconstruct/auto-select/auto-select-defaults.d.ts +1 -6
- package/reconstruct/auto-select/auto-select-defaults.js +1 -13
- package/reconstruct/reconstruct.js +1 -1
- package/slicing/criterion/parse.d.ts +3 -4
- package/slicing/criterion/parse.js +3 -3
- package/slicing/static/static-slicer.d.ts +1 -1
- package/slicing/static/static-slicer.js +10 -4
- package/statistics/statistics.js +1 -1
- package/util/json.d.ts +1 -1
- package/util/json.js +3 -3
- package/util/logic.d.ts +5 -1
- package/util/version.js +1 -1
- package/abstract-interpretation/domain.d.ts +0 -57
- package/abstract-interpretation/domain.js +0 -176
- package/abstract-interpretation/handler/binop/binop.d.ts +0 -15
- package/abstract-interpretation/handler/binop/binop.js +0 -42
- package/abstract-interpretation/handler/binop/operators.d.ts +0 -2
- package/abstract-interpretation/handler/binop/operators.js +0 -28
- package/abstract-interpretation/handler/handler.d.ts +0 -6
- package/abstract-interpretation/handler/handler.js +0 -3
- package/abstract-interpretation/processor.d.ts +0 -11
- package/abstract-interpretation/processor.js +0 -84
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import type { DataflowProcessorInformation } from '../../../../../processor';
|
|
2
2
|
import type { DataflowInformation } from '../../../../../info';
|
|
3
|
+
import type { ForceArguments } from '../common';
|
|
3
4
|
import type { ParentInformation } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/decorate';
|
|
4
5
|
import type { RSymbol } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-symbol';
|
|
5
6
|
import type { RFunctionArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
|
|
6
7
|
import type { NodeId } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/node-id';
|
|
7
8
|
export declare function processReplacementFunction<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>,
|
|
8
|
-
/** last one has to be the value */
|
|
9
|
+
/** The last one has to be the value */
|
|
9
10
|
args: readonly RFunctionArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>, config: {
|
|
10
11
|
makeMaybe?: boolean;
|
|
11
12
|
assignmentOperator?: '<-' | '<<-';
|
|
12
|
-
}): DataflowInformation;
|
|
13
|
+
} & ForceArguments): DataflowInformation;
|
|
@@ -11,7 +11,7 @@ const logger_1 = require("../../../../../logger");
|
|
|
11
11
|
const graph_1 = require("../../../../../graph/graph");
|
|
12
12
|
const dfg_1 = require("../../../../../../util/mermaid/dfg");
|
|
13
13
|
function processReplacementFunction(name,
|
|
14
|
-
/** last one has to be the value */
|
|
14
|
+
/** The last one has to be the value */
|
|
15
15
|
args, rootId, data, config) {
|
|
16
16
|
if (args.length < 2) {
|
|
17
17
|
logger_1.dataflowLogger.warn(`Replacement ${name.content} has less than 2 arguments, skipping`);
|
|
@@ -28,6 +28,7 @@ args, rootId, data, config) {
|
|
|
28
28
|
data,
|
|
29
29
|
functionRootId: rootId,
|
|
30
30
|
finalGraph: res.graph,
|
|
31
|
+
forceArgs: config.forceArgs,
|
|
31
32
|
});
|
|
32
33
|
const fn = res.graph.getVertex(rootId, true);
|
|
33
34
|
(0, assert_1.guard)(fn?.tag === "function-call" /* VertexType.FunctionCall */ && fn.args.length === 2, () => `Function ${rootId} not found in graph or not 2-arg fn-call (${JSON.stringify(fn)}) ${(0, dfg_1.graphToMermaidUrl)(res.graph)}`);
|
|
@@ -10,8 +10,8 @@ export declare function setSourceProvider(provider: RParseRequestProvider): void
|
|
|
10
10
|
export declare function processSourceCall<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly RFunctionArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>, config: {
|
|
11
11
|
/** should this produce an explicit source function call in the graph? */
|
|
12
12
|
includeFunctionCall?: boolean;
|
|
13
|
-
/** should this function call be followed, even when the
|
|
13
|
+
/** should this function call be followed, even when the configuration disables it? */
|
|
14
14
|
forceFollow?: boolean;
|
|
15
15
|
}): DataflowInformation;
|
|
16
|
-
export declare function sourceRequest<OtherInfo>(request: RParseRequest, data: DataflowProcessorInformation<OtherInfo & ParentInformation>, information: DataflowInformation, getId: IdGenerator<NoInfo>): DataflowInformation;
|
|
16
|
+
export declare function sourceRequest<OtherInfo>(rootId: NodeId, request: RParseRequest, data: DataflowProcessorInformation<OtherInfo & ParentInformation>, information: DataflowInformation, getId: IdGenerator<NoInfo>): DataflowInformation;
|
|
17
17
|
export declare function standaloneSourceFile<OtherInfo>(inputRequest: RParseRequest, data: DataflowProcessorInformation<OtherInfo & ParentInformation>, uniqueSourceId: string, information: DataflowInformation): DataflowInformation;
|
|
@@ -12,6 +12,7 @@ const decorate_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/proces
|
|
|
12
12
|
const r_function_call_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
|
|
13
13
|
const logger_1 = require("../../../../../logger");
|
|
14
14
|
const overwrite_1 = require("../../../../../environments/overwrite");
|
|
15
|
+
const log_1 = require("../../../../../../util/log");
|
|
15
16
|
let sourceProvider = (0, retriever_1.requestProviderFromFile)();
|
|
16
17
|
function setSourceProvider(provider) {
|
|
17
18
|
sourceProvider = provider;
|
|
@@ -23,26 +24,29 @@ function processSourceCall(name, args, rootId, data, config) {
|
|
|
23
24
|
: (0, info_1.initializeCleanDataflowInformation)(rootId, data);
|
|
24
25
|
const sourceFile = args[0];
|
|
25
26
|
if (!config.forceFollow && (0, config_1.getConfig)().ignoreSourceCalls) {
|
|
26
|
-
logger_1.dataflowLogger
|
|
27
|
+
(0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Skipping source call ${JSON.stringify(sourceFile)} (disabled in config file)`);
|
|
28
|
+
information.graph.markIdForUnknownSideEffects(rootId);
|
|
27
29
|
return information;
|
|
28
30
|
}
|
|
29
|
-
if (sourceFile !== r_function_call_1.EmptyArgument && sourceFile?.value?.type
|
|
31
|
+
if (sourceFile !== r_function_call_1.EmptyArgument && sourceFile?.value?.type === "RString" /* RType.String */) {
|
|
30
32
|
const path = (0, retriever_1.removeRQuotes)(sourceFile.lexeme);
|
|
31
33
|
const request = sourceProvider.createRequest(path);
|
|
32
34
|
// check if the sourced file has already been dataflow analyzed, and if so, skip it
|
|
33
35
|
if (data.referenceChain.includes((0, retriever_1.requestFingerprint)(request))) {
|
|
34
|
-
logger_1.dataflowLogger
|
|
36
|
+
(0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Found loop in dataflow analysis for ${JSON.stringify(request)}: ${JSON.stringify(data.referenceChain)}, skipping further dataflow analysis`);
|
|
37
|
+
information.graph.markIdForUnknownSideEffects(rootId);
|
|
35
38
|
return information;
|
|
36
39
|
}
|
|
37
|
-
return sourceRequest(request, data, information, (0, decorate_1.sourcedDeterministicCountingIdGenerator)(path, name.location));
|
|
40
|
+
return sourceRequest(rootId, request, data, information, (0, decorate_1.sourcedDeterministicCountingIdGenerator)(path, name.location));
|
|
38
41
|
}
|
|
39
42
|
else {
|
|
40
|
-
logger_1.dataflowLogger
|
|
43
|
+
(0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Non-constant argument ${JSON.stringify(sourceFile)} for source is currently not supported, skipping`);
|
|
44
|
+
information.graph.markIdForUnknownSideEffects(rootId);
|
|
41
45
|
return information;
|
|
42
46
|
}
|
|
43
47
|
}
|
|
44
48
|
exports.processSourceCall = processSourceCall;
|
|
45
|
-
function sourceRequest(request, data, information, getId) {
|
|
49
|
+
function sourceRequest(rootId, request, data, information, getId) {
|
|
46
50
|
const executor = new shell_executor_1.RShellExecutor();
|
|
47
51
|
// parse, normalize and dataflow the sourced file
|
|
48
52
|
let normalized;
|
|
@@ -59,8 +63,14 @@ function sourceRequest(request, data, information, getId) {
|
|
|
59
63
|
}
|
|
60
64
|
catch (e) {
|
|
61
65
|
logger_1.dataflowLogger.warn(`Failed to analyze sourced file ${JSON.stringify(request)}, skipping: ${e.message}`);
|
|
66
|
+
information.graph.markIdForUnknownSideEffects(rootId);
|
|
62
67
|
return information;
|
|
63
68
|
}
|
|
69
|
+
// take the entry point as well as all the written references, and give them a control dependency to the source call to show that they are conditional
|
|
70
|
+
dataflow.graph.addControlDependency(dataflow.entryPoint, rootId);
|
|
71
|
+
for (const out of dataflow.out) {
|
|
72
|
+
dataflow.graph.addControlDependency(out.nodeId, rootId);
|
|
73
|
+
}
|
|
64
74
|
// update our graph with the sourced file's information
|
|
65
75
|
const newInformation = { ...information };
|
|
66
76
|
newInformation.environment = (0, overwrite_1.overwriteEnvironment)(information.environment, dataflow.environment);
|
|
@@ -80,9 +90,10 @@ function standaloneSourceFile(inputRequest, data, uniqueSourceId, information) {
|
|
|
80
90
|
// check if the sourced file has already been dataflow analyzed, and if so, skip it
|
|
81
91
|
if (data.referenceChain.includes(fingerprint)) {
|
|
82
92
|
logger_1.dataflowLogger.info(`Found loop in dataflow analysis for ${JSON.stringify(request)}: ${JSON.stringify(data.referenceChain)}, skipping further dataflow analysis`);
|
|
93
|
+
information.graph.markIdForUnknownSideEffects(uniqueSourceId);
|
|
83
94
|
return information;
|
|
84
95
|
}
|
|
85
|
-
return sourceRequest(request, {
|
|
96
|
+
return sourceRequest(uniqueSourceId, request, {
|
|
86
97
|
...data,
|
|
87
98
|
currentRequest: request,
|
|
88
99
|
environment: information.environment,
|
|
@@ -4,7 +4,8 @@ import type { ParentInformation } from '../../../../../../r-bridge/lang-4.x/ast/
|
|
|
4
4
|
import type { RFunctionArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
|
|
5
5
|
import type { RSymbol } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-symbol';
|
|
6
6
|
import type { NodeId } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/node-id';
|
|
7
|
+
import type { ForceArguments } from '../common';
|
|
7
8
|
export declare function processSpecialBinOp<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly RFunctionArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>, config: {
|
|
8
9
|
lazy: boolean;
|
|
9
10
|
evalRhsWhen: boolean;
|
|
10
|
-
}): DataflowInformation;
|
|
11
|
+
} & ForceArguments): DataflowInformation;
|
|
@@ -9,9 +9,9 @@ function processSpecialBinOp(name, args, rootId, data, config) {
|
|
|
9
9
|
}
|
|
10
10
|
else if (args.length != 2) {
|
|
11
11
|
logger_1.dataflowLogger.warn(`Logical bin-op ${name.content} has something else than 2 arguments, skipping`);
|
|
12
|
-
return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data }).information;
|
|
12
|
+
return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, forceArgs: config.forceArgs }).information;
|
|
13
13
|
}
|
|
14
|
-
const { information, processedArguments } = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data,
|
|
14
|
+
const { information, processedArguments } = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, forceArgs: config.forceArgs,
|
|
15
15
|
patchData: (d, i) => {
|
|
16
16
|
if (i === 1) {
|
|
17
17
|
return { ...d, controlDependencies: [...d.controlDependencies ?? [], { id: name.info.id, when: config.evalRhsWhen }] };
|
|
@@ -31,4 +31,4 @@ function processSpecialBinOp(name, args, rootId, data, config) {
|
|
|
31
31
|
return information;
|
|
32
32
|
}
|
|
33
33
|
exports.processSpecialBinOp = processSpecialBinOp;
|
|
34
|
-
//# sourceMappingURL=built-in-
|
|
34
|
+
//# sourceMappingURL=built-in-special-bin-op.js.map
|
|
@@ -8,7 +8,11 @@ import type { NodeId } from '../../../../../r-bridge/lang-4.x/ast/model/processi
|
|
|
8
8
|
import type { REnvironmentInformation } from '../../../../environments/environment';
|
|
9
9
|
import type { IdentifierReference } from '../../../../environments/identifier';
|
|
10
10
|
import type { RSymbol } from '../../../../../r-bridge/lang-4.x/ast/model/nodes/r-symbol';
|
|
11
|
-
export interface
|
|
11
|
+
export interface ForceArguments {
|
|
12
|
+
/** which of the arguments should be forced? this may be all, e.g., if the function itself is unknown on encounter */
|
|
13
|
+
readonly forceArgs?: 'all' | readonly boolean[];
|
|
14
|
+
}
|
|
15
|
+
export interface ProcessAllArgumentInput<OtherInfo> extends ForceArguments {
|
|
12
16
|
readonly functionName: DataflowInformation;
|
|
13
17
|
readonly args: readonly (RNode<OtherInfo & ParentInformation> | RFunctionArgument<OtherInfo & ParentInformation>)[];
|
|
14
18
|
readonly data: DataflowProcessorInformation<OtherInfo & ParentInformation>;
|
|
@@ -24,7 +28,7 @@ export interface ProcessAllArgumentResult {
|
|
|
24
28
|
readonly remainingReadInArgs: IdentifierReference[];
|
|
25
29
|
readonly processedArguments: (DataflowInformation | undefined)[];
|
|
26
30
|
}
|
|
27
|
-
export declare function processAllArguments<OtherInfo>({ functionName, args, data, finalGraph, functionRootId, patchData }: ProcessAllArgumentInput<OtherInfo>): ProcessAllArgumentResult;
|
|
31
|
+
export declare function processAllArguments<OtherInfo>({ functionName, args, data, finalGraph, functionRootId, forceArgs, patchData }: ProcessAllArgumentInput<OtherInfo>): ProcessAllArgumentResult;
|
|
28
32
|
export interface PatchFunctionCallInput<OtherInfo> {
|
|
29
33
|
readonly nextGraph: DataflowGraph;
|
|
30
34
|
readonly rootId: NodeId;
|
|
@@ -5,7 +5,39 @@ const processor_1 = require("../../../../processor");
|
|
|
5
5
|
const r_function_call_1 = require("../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
|
|
6
6
|
const overwrite_1 = require("../../../../environments/overwrite");
|
|
7
7
|
const resolve_by_name_1 = require("../../../../environments/resolve-by-name");
|
|
8
|
-
|
|
8
|
+
const vertex_1 = require("../../../../graph/vertex");
|
|
9
|
+
function forceVertexArgumentValueReferences(rootId, value, graph, env) {
|
|
10
|
+
const valueVertex = graph.getVertex(value.entryPoint);
|
|
11
|
+
if (!valueVertex) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
// link read if it is function definition directly and reference the exit point
|
|
15
|
+
if (valueVertex.tag !== "value" /* VertexType.Value */) {
|
|
16
|
+
if (valueVertex.tag === "function-definition" /* VertexType.FunctionDefinition */) {
|
|
17
|
+
for (const exit of valueVertex.exitPoints) {
|
|
18
|
+
graph.addEdge(rootId, exit, { type: 1 /* EdgeType.Reads */ });
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
for (const exit of value.exitPoints) {
|
|
23
|
+
graph.addEdge(rootId, exit.nodeId, { type: 1 /* EdgeType.Reads */ });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const containedSubflowIn = [...graph.vertices(true)]
|
|
28
|
+
.filter(([, info]) => (0, vertex_1.isFunctionDefinitionVertex)(info))
|
|
29
|
+
.flatMap(([, info]) => info);
|
|
30
|
+
// try to resolve them against the current environment
|
|
31
|
+
for (const ref of [...value.in, ...containedSubflowIn.flatMap(n => n.subflow.in)]) {
|
|
32
|
+
if (ref.name) {
|
|
33
|
+
const resolved = (0, resolve_by_name_1.resolveByName)(ref.name, env) ?? [];
|
|
34
|
+
for (const resolve of resolved) {
|
|
35
|
+
graph.addEdge(ref.nodeId, resolve.nodeId, { type: 1 /* EdgeType.Reads */ });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function processAllArguments({ functionName, args, data, finalGraph, functionRootId, forceArgs = [], patchData = d => d }) {
|
|
9
41
|
let finalEnv = functionName.environment;
|
|
10
42
|
// arg env contains the environments with other args defined
|
|
11
43
|
let argEnv = functionName.environment;
|
|
@@ -22,6 +54,9 @@ function processAllArguments({ functionName, args, data, finalGraph, functionRoo
|
|
|
22
54
|
continue;
|
|
23
55
|
}
|
|
24
56
|
const processed = (0, processor_1.processDataflowFor)(arg, { ...data, environment: argEnv });
|
|
57
|
+
if (arg.type === "RArgument" /* RType.Argument */ && arg.value && (forceArgs === 'all' || forceArgs[i]) && arg.value.type !== "RNumber" /* RType.Number */ && arg.value.type !== "RString" /* RType.String */ && arg.value.type !== "RLogical" /* RType.Logical */) {
|
|
58
|
+
forceVertexArgumentValueReferences(functionRootId, processed, processed.graph, argEnv);
|
|
59
|
+
}
|
|
25
60
|
processedArguments.push(processed);
|
|
26
61
|
finalEnv = (0, overwrite_1.overwriteEnvironment)(finalEnv, processed.environment);
|
|
27
62
|
// resolve reads within argument, we resolve before adding the `processed.environment` to avoid cyclic dependencies
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { DataflowProcessorInformation } from '../../../../processor';
|
|
2
2
|
import type { DataflowInformation } from '../../../../info';
|
|
3
|
+
import type { ForceArguments } from './common';
|
|
3
4
|
import type { RSymbol } from '../../../../../r-bridge/lang-4.x/ast/model/nodes/r-symbol';
|
|
4
5
|
import type { ParentInformation } from '../../../../../r-bridge/lang-4.x/ast/model/processing/decorate';
|
|
5
6
|
import type { RFunctionArgument } from '../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
|
|
@@ -7,20 +8,24 @@ import type { NodeId } from '../../../../../r-bridge/lang-4.x/ast/model/processi
|
|
|
7
8
|
import type { RNode } from '../../../../../r-bridge/lang-4.x/ast/model/model';
|
|
8
9
|
import type { IdentifierReference } from '../../../../environments/identifier';
|
|
9
10
|
import { DataflowGraph } from '../../../../graph/graph';
|
|
10
|
-
export interface ProcessKnownFunctionCallInput<OtherInfo> {
|
|
11
|
+
export interface ProcessKnownFunctionCallInput<OtherInfo> extends ForceArguments {
|
|
11
12
|
readonly name: RSymbol<OtherInfo & ParentInformation>;
|
|
12
13
|
readonly args: readonly (RNode<OtherInfo & ParentInformation> | RFunctionArgument<OtherInfo & ParentInformation>)[];
|
|
13
14
|
readonly rootId: NodeId;
|
|
14
15
|
readonly data: DataflowProcessorInformation<OtherInfo & ParentInformation>;
|
|
16
|
+
/** should arguments be processed from right to left? This does not affect the order recorded in the call but of the environments */
|
|
15
17
|
readonly reverseOrder?: boolean;
|
|
16
18
|
/** which arguments are to be marked as {@link EdgeType#NonStandardEvaluation|non-standard-evaluation}? */
|
|
17
19
|
readonly markAsNSE?: readonly number[];
|
|
20
|
+
/** allows passing a data processor in-between each argument */
|
|
18
21
|
readonly patchData?: (data: DataflowProcessorInformation<OtherInfo & ParentInformation>, arg: number) => DataflowProcessorInformation<OtherInfo & ParentInformation>;
|
|
22
|
+
/** Does the call have a side effect that we do not know a lot about which may have further consequences? */
|
|
23
|
+
readonly hasUnknownSideEffect?: boolean;
|
|
19
24
|
}
|
|
20
25
|
export interface ProcessKnownFunctionCallResult {
|
|
21
26
|
readonly information: DataflowInformation;
|
|
22
27
|
readonly processedArguments: readonly (DataflowInformation | undefined)[];
|
|
23
28
|
readonly fnRef: IdentifierReference;
|
|
24
29
|
}
|
|
25
|
-
export declare function markNonStandardEvaluationEdges(markAsNSE: readonly number[]
|
|
26
|
-
export declare function processKnownFunctionCall<OtherInfo>({ name, args, rootId, data, reverseOrder, markAsNSE, patchData }: ProcessKnownFunctionCallInput<OtherInfo>): ProcessKnownFunctionCallResult;
|
|
30
|
+
export declare function markNonStandardEvaluationEdges(markAsNSE: readonly number[], callArgs: readonly (DataflowInformation | undefined)[], finalGraph: DataflowGraph, rootId: NodeId): void;
|
|
31
|
+
export declare function processKnownFunctionCall<OtherInfo>({ name, args, rootId, data, reverseOrder, markAsNSE, forceArgs, patchData, hasUnknownSideEffect }: ProcessKnownFunctionCallInput<OtherInfo>): ProcessKnownFunctionCallResult;
|
|
@@ -5,10 +5,8 @@ const processor_1 = require("../../../../processor");
|
|
|
5
5
|
const common_1 = require("./common");
|
|
6
6
|
const graph_1 = require("../../../../graph/graph");
|
|
7
7
|
const logger_1 = require("../../../../logger");
|
|
8
|
+
const log_1 = require("../../../../../util/log");
|
|
8
9
|
function markNonStandardEvaluationEdges(markAsNSE, callArgs, finalGraph, rootId) {
|
|
9
|
-
if (markAsNSE === undefined) {
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
10
|
for (const nse of markAsNSE) {
|
|
13
11
|
if (nse < callArgs.length) {
|
|
14
12
|
const arg = callArgs[nse];
|
|
@@ -22,14 +20,16 @@ function markNonStandardEvaluationEdges(markAsNSE, callArgs, finalGraph, rootId)
|
|
|
22
20
|
}
|
|
23
21
|
}
|
|
24
22
|
exports.markNonStandardEvaluationEdges = markNonStandardEvaluationEdges;
|
|
25
|
-
function processKnownFunctionCall({ name, args, rootId, data, reverseOrder = false, markAsNSE = undefined, patchData = d => d }) {
|
|
23
|
+
function processKnownFunctionCall({ name, args, rootId, data, reverseOrder = false, markAsNSE = undefined, forceArgs, patchData = d => d, hasUnknownSideEffect }) {
|
|
26
24
|
const functionName = (0, processor_1.processDataflowFor)(name, data);
|
|
27
25
|
const finalGraph = new graph_1.DataflowGraph(data.completeAst.idMap);
|
|
28
26
|
const functionCallName = name.content;
|
|
29
|
-
logger_1.dataflowLogger
|
|
27
|
+
(0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Processing known function call ${functionCallName} with ${args.length} arguments`);
|
|
30
28
|
const processArgs = reverseOrder ? [...args].reverse() : args;
|
|
31
|
-
const { finalEnv, callArgs, remainingReadInArgs, processedArguments } = (0, common_1.processAllArguments)({ functionName, args: processArgs, data, finalGraph, functionRootId: rootId, patchData });
|
|
32
|
-
|
|
29
|
+
const { finalEnv, callArgs, remainingReadInArgs, processedArguments } = (0, common_1.processAllArguments)({ functionName, args: processArgs, data, finalGraph, functionRootId: rootId, patchData, forceArgs });
|
|
30
|
+
if (markAsNSE) {
|
|
31
|
+
markNonStandardEvaluationEdges(markAsNSE, processedArguments, finalGraph, rootId);
|
|
32
|
+
}
|
|
33
33
|
finalGraph.addVertex({
|
|
34
34
|
tag: "function-call" /* VertexType.FunctionCall */,
|
|
35
35
|
id: rootId,
|
|
@@ -40,6 +40,9 @@ function processKnownFunctionCall({ name, args, rootId, data, reverseOrder = fal
|
|
|
40
40
|
controlDependencies: data.controlDependencies,
|
|
41
41
|
args: reverseOrder ? [...callArgs].reverse() : callArgs
|
|
42
42
|
});
|
|
43
|
+
if (hasUnknownSideEffect) {
|
|
44
|
+
finalGraph.markIdForUnknownSideEffects(rootId);
|
|
45
|
+
}
|
|
43
46
|
const inIds = remainingReadInArgs;
|
|
44
47
|
const fnRef = { nodeId: rootId, name: functionCallName, controlDependencies: data.controlDependencies, call: true };
|
|
45
48
|
inIds.push(fnRef);
|
|
@@ -20,7 +20,9 @@ function mergeInformation(info, newInfo) {
|
|
|
20
20
|
};
|
|
21
21
|
}
|
|
22
22
|
function processDefaultFunctionProcessor(information, name, args, rootId, data) {
|
|
23
|
-
const
|
|
23
|
+
const resolve = (0, resolve_by_name_1.resolveByName)(name.content, data.environment);
|
|
24
|
+
/* if we do not know where we land, we force! */
|
|
25
|
+
const call = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, forceArgs: (resolve?.length ?? 0) > 0 ? undefined : 'all' });
|
|
24
26
|
return mergeInformation(information, call.information);
|
|
25
27
|
}
|
|
26
28
|
function processNamedCall(name, args, rootId, data) {
|
|
@@ -25,6 +25,7 @@ function processUnnamedFunctionCall(functionCall, data) {
|
|
|
25
25
|
data,
|
|
26
26
|
finalGraph,
|
|
27
27
|
functionRootId
|
|
28
|
+
/* we know the call is right there and fully resolved, there is no need to artificially force arguments as we identify them within the subtree */
|
|
28
29
|
});
|
|
29
30
|
finalGraph.addVertex({
|
|
30
31
|
tag: "function-call" /* VertexType.FunctionCall */,
|
|
@@ -4,8 +4,6 @@ exports.processFunctionArgument = exports.linkReadsForArgument = void 0;
|
|
|
4
4
|
const processor_1 = require("../../../processor");
|
|
5
5
|
const collect_1 = require("../../../../r-bridge/lang-4.x/ast/model/collect");
|
|
6
6
|
const graph_1 = require("../../../graph/graph");
|
|
7
|
-
const edge_1 = require("../../../graph/edge");
|
|
8
|
-
const resolve_by_name_1 = require("../../../environments/resolve-by-name");
|
|
9
7
|
function linkReadsForArgument(root, ingoingRefs, graph) {
|
|
10
8
|
const allIdsBeforeArguments = new Set((0, collect_1.collectAllIds)(root, n => n.type === "RArgument" /* RType.Argument */ && n.info.id !== root.info.id));
|
|
11
9
|
const ingoingBeforeArgs = ingoingRefs.filter(r => allIdsBeforeArguments.has(r.nodeId));
|
|
@@ -15,18 +13,6 @@ function linkReadsForArgument(root, ingoingRefs, graph) {
|
|
|
15
13
|
}
|
|
16
14
|
}
|
|
17
15
|
exports.linkReadsForArgument = linkReadsForArgument;
|
|
18
|
-
function hasNoOutgoingCallEdge(graph, id) {
|
|
19
|
-
const outgoings = graph.outgoingEdges(id);
|
|
20
|
-
if (outgoings === undefined) {
|
|
21
|
-
return true;
|
|
22
|
-
}
|
|
23
|
-
for (const [_, edge] of outgoings) {
|
|
24
|
-
if ((0, edge_1.edgeIncludesType)(edge.types, 4 /* EdgeType.Calls */)) {
|
|
25
|
-
return false;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
return true;
|
|
29
|
-
}
|
|
30
16
|
function processFunctionArgument(argument, data) {
|
|
31
17
|
const name = argument.name === undefined ? undefined : (0, processor_1.processDataflowFor)(argument.name, data);
|
|
32
18
|
const value = argument.value === undefined ? undefined : (0, processor_1.processDataflowFor)(argument.value, data);
|
|
@@ -43,20 +29,6 @@ function processFunctionArgument(argument, data) {
|
|
|
43
29
|
entryPoint = argument.info.id;
|
|
44
30
|
}
|
|
45
31
|
const ingoingRefs = [...value?.unknownReferences ?? [], ...value?.in ?? [], ...(name === undefined ? [] : [...name.in])];
|
|
46
|
-
/* potentially link all function calls here (as maybes with a maybe cd) as the called function may employ calling-env-semantics if unknown */
|
|
47
|
-
if (value) {
|
|
48
|
-
const functionCalls = [...graph.vertices(true)]
|
|
49
|
-
.filter(([_, info]) => info.tag === "function-call" /* VertexType.FunctionCall */)
|
|
50
|
-
.filter(([id]) => hasNoOutgoingCallEdge(graph, id));
|
|
51
|
-
// try to resolve them against the current environment
|
|
52
|
-
for (const [id, info] of functionCalls) {
|
|
53
|
-
const resolved = (0, resolve_by_name_1.resolveByName)(info.name, data.environment) ?? [];
|
|
54
|
-
/* first, only link a read ref */
|
|
55
|
-
for (const resolve of resolved) {
|
|
56
|
-
graph.addEdge(id, resolve.nodeId, { type: 1 /* EdgeType.Reads */ });
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
32
|
if (entryPoint && argument.value?.type === "RFunctionDefinition" /* RType.FunctionDefinition */) {
|
|
61
33
|
graph.addEdge(entryPoint, argument.value.info.id, { type: 1 /* EdgeType.Reads */ });
|
|
62
34
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eagleoutice/flowr",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.18",
|
|
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": {
|
|
@@ -33,7 +33,8 @@
|
|
|
33
33
|
"doc": "typedoc",
|
|
34
34
|
"test": "nyc --no-clean mocha",
|
|
35
35
|
"performance-test": "func() { cd test/performance/ && bash run-all-suites.sh $1 $2 $3; cd ../../; }; func",
|
|
36
|
-
"test-full": "npm run test -- --test-installation"
|
|
36
|
+
"test-full": "npm run test -- --test-installation",
|
|
37
|
+
"detect-circular-deps": "npx madge --extensions ts,tsx --circular src/"
|
|
37
38
|
},
|
|
38
39
|
"keywords": [
|
|
39
40
|
"static code analysis",
|
package/r-bridge/data/data.d.ts
CHANGED
|
@@ -146,6 +146,11 @@ export declare const flowrCapabilities: {
|
|
|
146
146
|
readonly id: "redefinition-of-built-in-functions-primitives";
|
|
147
147
|
readonly supported: "partially";
|
|
148
148
|
readonly description: "_Handle cases like `print <- function(x) x`, `` `for` <- function(a,b,c) a``, ..._ Currently, we can not handle all of them there are no tests. Still wip as part of desugaring";
|
|
149
|
+
}, {
|
|
150
|
+
readonly name: "Functions with global side effects";
|
|
151
|
+
readonly id: "functions-with-global-side-effects";
|
|
152
|
+
readonly supported: "partially";
|
|
153
|
+
readonly description: "_Support functions like `setwd` which have an impact on the subsequent program._";
|
|
149
154
|
}, {
|
|
150
155
|
readonly name: "Index Access";
|
|
151
156
|
readonly id: "index-access";
|
|
@@ -226,6 +231,11 @@ export declare const flowrCapabilities: {
|
|
|
226
231
|
readonly id: "local-equal-assignment";
|
|
227
232
|
readonly supported: "fully";
|
|
228
233
|
readonly description: "_Handle `x = 3`, `x$y := 3`, ..._";
|
|
234
|
+
}, {
|
|
235
|
+
readonly name: "Local Table Assignment";
|
|
236
|
+
readonly id: "local-table-assignment";
|
|
237
|
+
readonly supported: "fully";
|
|
238
|
+
readonly description: "_Handle `x[,a:=3,]`, ..._";
|
|
229
239
|
}, {
|
|
230
240
|
readonly name: "Super Left Assignment";
|
|
231
241
|
readonly id: "super-left-assignment";
|
package/r-bridge/data/data.js
CHANGED
|
@@ -184,6 +184,12 @@ exports.flowrCapabilities = {
|
|
|
184
184
|
supported: 'partially',
|
|
185
185
|
description: '_Handle cases like `print <- function(x) x`, `` `for` <- function(a,b,c) a``, ..._ Currently, we can not handle all of them there are no tests. Still wip as part of desugaring'
|
|
186
186
|
},
|
|
187
|
+
{
|
|
188
|
+
name: 'Functions with global side effects',
|
|
189
|
+
id: 'functions-with-global-side-effects',
|
|
190
|
+
supported: 'partially',
|
|
191
|
+
description: '_Support functions like `setwd` which have an impact on the subsequent program._'
|
|
192
|
+
},
|
|
187
193
|
{
|
|
188
194
|
name: 'Index Access',
|
|
189
195
|
id: 'index-access',
|
|
@@ -282,6 +288,12 @@ exports.flowrCapabilities = {
|
|
|
282
288
|
supported: 'fully',
|
|
283
289
|
description: '_Handle `x = 3`, `x$y := 3`, ..._'
|
|
284
290
|
},
|
|
291
|
+
{
|
|
292
|
+
name: 'Local Table Assignment',
|
|
293
|
+
id: 'local-table-assignment',
|
|
294
|
+
supported: 'fully',
|
|
295
|
+
description: '_Handle `x[,a:=3,]`, ..._'
|
|
296
|
+
},
|
|
285
297
|
{
|
|
286
298
|
name: 'Super Left Assignment',
|
|
287
299
|
id: 'super-left-assignment',
|
|
@@ -34,7 +34,7 @@ exports.OperatorDatabase = {
|
|
|
34
34
|
'%in%': { name: 'matching operator', stringUsedInRAst: '%in%', stringUsedInternally: '%in%', writtenAs: 'infix', arity: 2 /* OperatorArity.Binary */, usedAs: 'operation', capabilities: ['binary-operator', 'infix-calls', 'special-operator', 'function-calls'] },
|
|
35
35
|
/* assignment */
|
|
36
36
|
'<-': { name: 'left assignment', stringUsedInRAst: "LEFT_ASSIGN" /* RawRType.LeftAssign */, stringUsedInternally: '<-', writtenAs: 'infix', arity: 2 /* OperatorArity.Binary */, usedAs: 'assignment', capabilities: ['binary-operator', 'infix-calls', 'assignment-functions', 'local-left-assignment', 'function-calls'] },
|
|
37
|
-
':=': { name: 'left assignment', stringUsedInRAst: "LEFT_ASSIGN" /* RawRType.LeftAssign */, stringUsedInternally: ':=', writtenAs: 'infix', arity: 2 /* OperatorArity.Binary */, usedAs: 'assignment', capabilities: ['binary-operator', 'infix-calls', 'assignment-functions', 'local-
|
|
37
|
+
':=': { name: 'left assignment', stringUsedInRAst: "LEFT_ASSIGN" /* RawRType.LeftAssign */, stringUsedInternally: ':=', writtenAs: 'infix', arity: 2 /* OperatorArity.Binary */, usedAs: 'assignment', capabilities: ['binary-operator', 'infix-calls', 'assignment-functions', 'local-table-assignment', 'function-calls'] },
|
|
38
38
|
'<<-': { name: 'left global assignment', stringUsedInRAst: "LEFT_ASSIGN" /* RawRType.LeftAssign */, stringUsedInternally: '<<-', writtenAs: 'infix', arity: 2 /* OperatorArity.Binary */, usedAs: 'assignment', capabilities: ['binary-operator', 'infix-calls', 'assignment-functions', 'super-left-assignment', 'function-calls'] },
|
|
39
39
|
'->': { name: 'right assignment', stringUsedInRAst: "RIGHT_ASSIGN" /* RawRType.RightAssign */, stringUsedInternally: '->', writtenAs: 'infix', arity: 2 /* OperatorArity.Binary */, usedAs: 'assignment', capabilities: ['binary-operator', 'infix-calls', 'assignment-functions', 'local-right-assignment', 'function-calls'] },
|
|
40
40
|
'->>': { name: 'right global assignment', stringUsedInRAst: "RIGHT_ASSIGN" /* RawRType.RightAssign */, stringUsedInternally: '->>', writtenAs: 'infix', arity: 2 /* OperatorArity.Binary */, usedAs: 'assignment', capabilities: ['binary-operator', 'infix-calls', 'assignment-functions', 'super-right-assignment', 'function-calls'] },
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RNode } from '../../r-bridge/lang-4.x/ast/model/model';
|
|
2
2
|
import type { ParentInformation, NormalizedAst } from '../../r-bridge/lang-4.x/ast/model/processing/decorate';
|
|
3
3
|
/**
|
|
4
4
|
* The structure of the predicate that should be used to determine
|
|
@@ -14,8 +14,3 @@ export type AutoSelectPredicate = (node: RNode<ParentInformation>, fullAst: Norm
|
|
|
14
14
|
* A variant of the {@link AutoSelectPredicate} which does not select any additional statements (~> false)
|
|
15
15
|
*/
|
|
16
16
|
export declare function doNotAutoSelect(_node: RNode): boolean;
|
|
17
|
-
/**
|
|
18
|
-
* A variant of the {@link AutoSelectPredicate} which does its best
|
|
19
|
-
* to select any kind of library import automatically.
|
|
20
|
-
*/
|
|
21
|
-
export declare function autoSelectLibrary<Info = NoInfo>(node: RNode<Info>): boolean;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.doNotAutoSelect = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* A variant of the {@link AutoSelectPredicate} which does not select any additional statements (~> false)
|
|
6
6
|
*/
|
|
@@ -8,16 +8,4 @@ function doNotAutoSelect(_node) {
|
|
|
8
8
|
return false;
|
|
9
9
|
}
|
|
10
10
|
exports.doNotAutoSelect = doNotAutoSelect;
|
|
11
|
-
const libraryFunctionCall = /^(library|require|((require|load|attach)Namespace))$/;
|
|
12
|
-
/**
|
|
13
|
-
* A variant of the {@link AutoSelectPredicate} which does its best
|
|
14
|
-
* to select any kind of library import automatically.
|
|
15
|
-
*/
|
|
16
|
-
function autoSelectLibrary(node) {
|
|
17
|
-
if (node.type !== "RFunctionCall" /* RType.FunctionCall */ || !node.named) {
|
|
18
|
-
return false;
|
|
19
|
-
}
|
|
20
|
-
return libraryFunctionCall.test(node.functionName.content);
|
|
21
|
-
}
|
|
22
|
-
exports.autoSelectLibrary = autoSelectLibrary;
|
|
23
11
|
//# sourceMappingURL=auto-select-defaults.js.map
|
|
@@ -424,7 +424,7 @@ function removeOuterExpressionListIfApplicable(result, linesWithAutoSelected) {
|
|
|
424
424
|
*
|
|
425
425
|
* @returns The number of lines for which `autoSelectIf` triggered, as well as the reconstructed code itself.
|
|
426
426
|
*/
|
|
427
|
-
function reconstructToCode(ast, selection, autoSelectIf = auto_select_defaults_1.
|
|
427
|
+
function reconstructToCode(ast, selection, autoSelectIf = auto_select_defaults_1.doNotAutoSelect) {
|
|
428
428
|
if (exports.reconstructLogger.settings.minLevel <= 1 /* LogLevel.Trace */) {
|
|
429
429
|
exports.reconstructLogger.trace(`reconstruct ast with ids: ${JSON.stringify([...selection])}`);
|
|
430
430
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type { NoInfo } from '../../r-bridge/lang-4.x/ast/model/model';
|
|
2
1
|
import { type NodeId } from '../../r-bridge/lang-4.x/ast/model/processing/node-id';
|
|
3
|
-
import type {
|
|
2
|
+
import type { AstIdMap } from '../../r-bridge/lang-4.x/ast/model/processing/decorate';
|
|
4
3
|
/** Either `line:column`, `line@variable-name`, or `$id` */
|
|
5
4
|
export type SingleSlicingCriterion = `${number}:${number}` | `${number}@${string}` | `$${number}`;
|
|
6
5
|
export type SlicingCriteria = SingleSlicingCriterion[];
|
|
@@ -13,10 +12,10 @@ export declare class CriteriaParseError extends Error {
|
|
|
13
12
|
/**
|
|
14
13
|
* Takes a criterion in the form of `line:column` or `line@variable-name` and returns the corresponding node id
|
|
15
14
|
*/
|
|
16
|
-
export declare function slicingCriterionToId
|
|
15
|
+
export declare function slicingCriterionToId(criterion: SingleSlicingCriterion, idMap: AstIdMap): NodeId;
|
|
17
16
|
export interface DecodedCriterion {
|
|
18
17
|
criterion: SingleSlicingCriterion;
|
|
19
18
|
id: NodeId;
|
|
20
19
|
}
|
|
21
20
|
export type DecodedCriteria = ReadonlyArray<DecodedCriterion>;
|
|
22
|
-
export declare function convertAllSlicingCriteriaToIds(criteria: SlicingCriteria, decorated:
|
|
21
|
+
export declare function convertAllSlicingCriteriaToIds(criteria: SlicingCriteria, decorated: AstIdMap): DecodedCriteria;
|
|
@@ -17,18 +17,18 @@ exports.CriteriaParseError = CriteriaParseError;
|
|
|
17
17
|
/**
|
|
18
18
|
* Takes a criterion in the form of `line:column` or `line@variable-name` and returns the corresponding node id
|
|
19
19
|
*/
|
|
20
|
-
function slicingCriterionToId(criterion,
|
|
20
|
+
function slicingCriterionToId(criterion, idMap) {
|
|
21
21
|
let resolved;
|
|
22
22
|
if (criterion.startsWith('$')) {
|
|
23
23
|
resolved = (0, node_id_1.normalizeIdToNumberIfPossible)(criterion.substring(1));
|
|
24
24
|
}
|
|
25
25
|
else if (criterion.includes(':')) {
|
|
26
26
|
const [line, column] = criterion.split(':').map(c => parseInt(c));
|
|
27
|
-
resolved = locationToId([line, column],
|
|
27
|
+
resolved = locationToId([line, column], idMap);
|
|
28
28
|
}
|
|
29
29
|
else if (criterion.includes('@')) {
|
|
30
30
|
const [line, name] = criterion.split(/@(.*)/s); // only split at first occurrence
|
|
31
|
-
resolved = conventionalCriteriaToId(parseInt(line), name,
|
|
31
|
+
resolved = conventionalCriteriaToId(parseInt(line), name, idMap);
|
|
32
32
|
}
|
|
33
33
|
if (resolved === undefined) {
|
|
34
34
|
throw new CriteriaParseError(`invalid slicing criterion ${criterion}`);
|
|
@@ -13,4 +13,4 @@ export declare const slicerLogger: import("tslog").Logger<import("tslog").ILogOb
|
|
|
13
13
|
* @param criteria - The criteras to slice on.
|
|
14
14
|
* @param threshold - The maximum number of nodes to visit in the graph. If the threshold is reached, the slice will side with inclusion and drop its minimal guarantee. The limit ensures that the algorithm halts.
|
|
15
15
|
*/
|
|
16
|
-
export declare function staticSlicing(graph: DataflowGraph,
|
|
16
|
+
export declare function staticSlicing(graph: DataflowGraph, { idMap }: NormalizedAst, criteria: SlicingCriteria, threshold?: number): Readonly<SliceResult>;
|
|
@@ -20,9 +20,9 @@ exports.slicerLogger = log_1.log.getSubLogger({ name: 'slicer' });
|
|
|
20
20
|
* @param criteria - The criteras to slice on.
|
|
21
21
|
* @param threshold - The maximum number of nodes to visit in the graph. If the threshold is reached, the slice will side with inclusion and drop its minimal guarantee. The limit ensures that the algorithm halts.
|
|
22
22
|
*/
|
|
23
|
-
function staticSlicing(graph,
|
|
23
|
+
function staticSlicing(graph, { idMap }, criteria, threshold = 75) {
|
|
24
24
|
(0, assert_1.guard)(criteria.length > 0, 'must have at least one seed id to calculate slice');
|
|
25
|
-
const decodedCriteria = (0, parse_1.convertAllSlicingCriteriaToIds)(criteria,
|
|
25
|
+
const decodedCriteria = (0, parse_1.convertAllSlicingCriteriaToIds)(criteria, idMap);
|
|
26
26
|
(0, log_1.expensiveTrace)(exports.slicerLogger, () => `calculating slice for ${decodedCriteria.length} seed criteria: ${decodedCriteria.map(s => JSON.stringify(s)).join(', ')}`);
|
|
27
27
|
const queue = new visiting_queue_1.VisitingQueue(threshold);
|
|
28
28
|
let minDepth = Number.MAX_SAFE_INTEGER;
|
|
@@ -34,9 +34,15 @@ function staticSlicing(graph, ast, criteria, threshold = 75) {
|
|
|
34
34
|
for (const { id: startId } of decodedCriteria) {
|
|
35
35
|
queue.add(startId, emptyEnv, basePrint, false);
|
|
36
36
|
// retrieve the minimum depth of all nodes to only add control dependencies if they are "part" of the current execution
|
|
37
|
-
minDepth = Math.min(minDepth,
|
|
37
|
+
minDepth = Math.min(minDepth, idMap.get(startId)?.info.depth ?? minDepth);
|
|
38
38
|
sliceSeedIds.add(startId);
|
|
39
39
|
}
|
|
40
|
+
/* additionally,
|
|
41
|
+
* include all the implicit side effects that we have to consider as we are unable to narrow them down
|
|
42
|
+
*/
|
|
43
|
+
for (const id of graph.unknownSideEffects) {
|
|
44
|
+
queue.add(id, emptyEnv, basePrint, true);
|
|
45
|
+
}
|
|
40
46
|
}
|
|
41
47
|
while (queue.nonEmpty()) {
|
|
42
48
|
const current = queue.next();
|
|
@@ -52,7 +58,7 @@ function staticSlicing(graph, ast, criteria, threshold = 75) {
|
|
|
52
58
|
if (currentVertex.controlDependencies && currentVertex.controlDependencies.length > 0) {
|
|
53
59
|
const topLevel = graph.isRoot(id) || sliceSeedIds.has(id);
|
|
54
60
|
for (const cd of currentVertex.controlDependencies.filter(({ id }) => !queue.hasId(id))) {
|
|
55
|
-
if (!topLevel || (
|
|
61
|
+
if (!topLevel || (idMap.get(cd.id)?.info.depth ?? 0) <= minDepth) {
|
|
56
62
|
queue.add(cd.id, baseEnvironment, baseEnvFingerprint, false);
|
|
57
63
|
}
|
|
58
64
|
}
|
package/statistics/statistics.js
CHANGED
|
@@ -64,7 +64,7 @@ function initializeFeatureStatistics() {
|
|
|
64
64
|
const result = {};
|
|
65
65
|
for (const key of feature_1.allFeatureNames) {
|
|
66
66
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
67
|
-
result[key] = JSON.parse(JSON.stringify(feature_1.ALL_FEATURES[key].initialValue, json_1.jsonReplacer), json_1.
|
|
67
|
+
result[key] = JSON.parse(JSON.stringify(feature_1.ALL_FEATURES[key].initialValue, json_1.jsonReplacer), json_1.jsonBigIntRetriever);
|
|
68
68
|
}
|
|
69
69
|
return result;
|
|
70
70
|
}
|
package/util/json.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export declare function jsonReplacer(key: any, value: any): any;
|
|
2
|
-
export declare function
|
|
2
|
+
export declare function jsonBigIntRetriever(key: string, value: unknown): unknown;
|