@eagleoutice/flowr 2.0.16 → 2.0.17
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 +34 -0
- 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/dataflow/environments/built-in.js +18 -8
- 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 +18 -19
- package/dataflow/graph/graph.js +41 -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 +3 -3
- package/dataflow/internal/process/functions/call/built-in/built-in-assignment.d.ts +2 -1
- package/dataflow/internal/process/functions/call/built-in/built-in-assignment.js +5 -4
- 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 +1 -0
- 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 +3 -2
- package/dataflow/internal/process/functions/call/known-call-handling.js +2 -2
- 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 +5 -0
- package/r-bridge/data/data.js +6 -0
- 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
package/dataflow/info.d.ts
CHANGED
|
@@ -57,7 +57,7 @@ export interface DataflowInformation extends DataflowCfgInformation {
|
|
|
57
57
|
graph: DataflowGraph;
|
|
58
58
|
}
|
|
59
59
|
export declare function initializeCleanDataflowInformation<T>(entryPoint: NodeId, data: Pick<DataflowProcessorInformation<T>, 'environment' | 'completeAst'>): DataflowInformation;
|
|
60
|
-
export declare function happensInEveryBranch(controlDependencies: ControlDependency[] | undefined): boolean;
|
|
60
|
+
export declare function happensInEveryBranch(controlDependencies: readonly ControlDependency[] | undefined): boolean;
|
|
61
61
|
export declare function alwaysExits(data: DataflowInformation): boolean;
|
|
62
62
|
export declare function filterOutLoopExitPoints(exitPoints: readonly ExitPoint[]): readonly ExitPoint[];
|
|
63
63
|
export declare function diffControlDependency<Report extends WriteableDifferenceReport>(a: ControlDependency | undefined, b: ControlDependency | undefined, info: GenericDifferenceInformation<Report>): void;
|
|
@@ -4,6 +4,7 @@ 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 processAccess<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly RFunctionArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>, config: {
|
|
8
9
|
treatIndicesAsString: boolean;
|
|
9
|
-
}): DataflowInformation;
|
|
10
|
+
} & ForceArguments): DataflowInformation;
|
|
@@ -9,13 +9,13 @@ const environment_1 = require("../../../../../environments/environment");
|
|
|
9
9
|
function processAccess(name, args, rootId, data, config) {
|
|
10
10
|
if (args.length < 2) {
|
|
11
11
|
logger_1.dataflowLogger.warn(`Access ${name.content} has less 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
14
|
const head = args[0];
|
|
15
15
|
(0, assert_1.guard)(head !== r_function_call_1.EmptyArgument, () => `Access ${name.content} has no source, impossible!`);
|
|
16
16
|
let fnCall;
|
|
17
17
|
if (!config.treatIndicesAsString) {
|
|
18
|
-
fnCall = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data });
|
|
18
|
+
fnCall = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, forceArgs: config.forceArgs });
|
|
19
19
|
}
|
|
20
20
|
else {
|
|
21
21
|
const newArgs = [...args];
|
|
@@ -38,7 +38,7 @@ function processAccess(name, args, rootId, data, config) {
|
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
|
-
fnCall = (0, known_call_handling_1.processKnownFunctionCall)({ name, args: newArgs, rootId, data });
|
|
41
|
+
fnCall = (0, known_call_handling_1.processKnownFunctionCall)({ name, args: newArgs, rootId, data, forceArgs: config.forceArgs });
|
|
42
42
|
}
|
|
43
43
|
const info = fnCall.information;
|
|
44
44
|
info.graph.addEdge(name.info.id, fnCall.processedArguments[0]?.entryPoint ?? head.info.id, { type: 8 /* EdgeType.Returns */ });
|
|
@@ -5,7 +5,8 @@ import type { RNode } from '../../../../../../r-bridge/lang-4.x/ast/model/model'
|
|
|
5
5
|
import type { RSymbol } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-symbol';
|
|
6
6
|
import type { RFunctionArgument } from '../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call';
|
|
7
7
|
import type { NodeId } from '../../../../../../r-bridge/lang-4.x/ast/model/processing/node-id';
|
|
8
|
-
|
|
8
|
+
import type { ForceArguments } from '../common';
|
|
9
|
+
export interface AssignmentConfiguration extends ForceArguments {
|
|
9
10
|
readonly superAssignment?: boolean;
|
|
10
11
|
readonly swapSourceAndTarget?: boolean;
|
|
11
12
|
readonly makeMaybe?: boolean;
|
|
@@ -35,13 +35,13 @@ function processAssignment(name,
|
|
|
35
35
|
args, rootId, data, config) {
|
|
36
36
|
if (args.length != 2) {
|
|
37
37
|
logger_1.dataflowLogger.warn(`Assignment ${name.content} has something else than 2 arguments, skipping`);
|
|
38
|
-
return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data }).information;
|
|
38
|
+
return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, forceArgs: config.forceArgs }).information;
|
|
39
39
|
}
|
|
40
40
|
const effectiveArgs = getEffectiveOrder(config, args);
|
|
41
41
|
const { target, source } = extractSourceAndTarget(effectiveArgs, name);
|
|
42
42
|
const { type, named } = target;
|
|
43
43
|
if (type === "RSymbol" /* RType.Symbol */) {
|
|
44
|
-
const res = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, reverseOrder: !config.swapSourceAndTarget });
|
|
44
|
+
const res = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, reverseOrder: !config.swapSourceAndTarget, forceArgs: config.forceArgs });
|
|
45
45
|
return processAssignmentToSymbol({
|
|
46
46
|
...config,
|
|
47
47
|
name,
|
|
@@ -68,7 +68,7 @@ args, rootId, data, config) {
|
|
|
68
68
|
return processAssignmentToString(target, args, name, rootId, data, config, source);
|
|
69
69
|
}
|
|
70
70
|
logger_1.dataflowLogger.warn(`Assignment ${name.content} has an unknown target type ${target.type}, skipping`);
|
|
71
|
-
return (0, known_call_handling_1.processKnownFunctionCall)({ name, args: effectiveArgs, rootId, data }).information;
|
|
71
|
+
return (0, known_call_handling_1.processKnownFunctionCall)({ name, args: effectiveArgs, rootId, data, forceArgs: config.forceArgs }).information;
|
|
72
72
|
}
|
|
73
73
|
exports.processAssignment = processAssignment;
|
|
74
74
|
function extractSourceAndTarget(args, name) {
|
|
@@ -105,7 +105,8 @@ function processAssignmentToString(target, args, name, rootId, data, config, sou
|
|
|
105
105
|
args: mappedArgs,
|
|
106
106
|
rootId,
|
|
107
107
|
data,
|
|
108
|
-
reverseOrder: !config.swapSourceAndTarget
|
|
108
|
+
reverseOrder: !config.swapSourceAndTarget,
|
|
109
|
+
forceArgs: config.forceArgs
|
|
109
110
|
});
|
|
110
111
|
return processAssignmentToSymbol({
|
|
111
112
|
...config,
|
|
@@ -63,7 +63,7 @@ function processForLoop(name, args, rootId, data) {
|
|
|
63
63
|
for (const readId of readIdsToLink) {
|
|
64
64
|
nextGraph.addEdge(readId.nodeId, write.nodeId, { type: 1 /* EdgeType.Reads */ });
|
|
65
65
|
}
|
|
66
|
-
// now, we remove the name from the id shares as they are no longer
|
|
66
|
+
// now, we remove the name from the id shares as they are no longer necessary
|
|
67
67
|
nameIdShares.delete(name);
|
|
68
68
|
nextGraph.setDefinitionOfVertex(write);
|
|
69
69
|
}
|
|
@@ -45,8 +45,17 @@ function processFunctionDefinition(name, args, rootId, data) {
|
|
|
45
45
|
const readInBody = [...body.in, ...body.unknownReferences];
|
|
46
46
|
// there is no uncertainty regarding the arguments, as if a function header is executed, so is its body
|
|
47
47
|
const remainingRead = (0, linker_1.linkInputs)(readInBody, paramsEnvironments, readInParameters.slice(), body.graph, true /* functions do not have to be called */);
|
|
48
|
+
// functions can be called multiple times,
|
|
49
|
+
// so if they have a global effect, we have to link them as if they would be executed a loop
|
|
50
|
+
/* theoretically, we should just check if there is a global effect-write somewhere within */
|
|
51
|
+
if (remainingRead.length > 0) {
|
|
52
|
+
const nameIdShares = (0, linker_1.produceNameSharedIdMap)(remainingRead);
|
|
53
|
+
const definedInLocalEnvironment = new Set([...bodyEnvironment.current.memory.values()].flat().map(d => d.nodeId));
|
|
54
|
+
// Everything that is in body.out but not within the local environment populated for the function scope is a potential escape ~> global definition
|
|
55
|
+
const globalBodyOut = body.out.filter(d => !definedInLocalEnvironment.has(d.nodeId));
|
|
56
|
+
(0, linker_1.linkCircularRedefinitionsWithinALoop)(body.graph, nameIdShares, globalBodyOut);
|
|
57
|
+
}
|
|
48
58
|
subgraph.mergeWith(body.graph);
|
|
49
|
-
logger_1.dataflowLogger.trace(`Function definition with id ${name.info.id} has ${remainingRead.length} remaining reads`);
|
|
50
59
|
const outEnvironment = (0, overwrite_1.overwriteEnvironment)(paramsEnvironments, bodyEnvironment);
|
|
51
60
|
for (const read of remainingRead) {
|
|
52
61
|
if (read.name) {
|
|
@@ -33,23 +33,23 @@ function processIfThenElse(name, args, rootId, data) {
|
|
|
33
33
|
// we should defer this to the abstract interpretation
|
|
34
34
|
const conditionIsFalse = (0, resolve_by_name_1.resolvesToBuiltInConstant)(condArg?.lexeme, data.environment, false);
|
|
35
35
|
const conditionIsTrue = (0, resolve_by_name_1.resolvesToBuiltInConstant)(condArg?.lexeme, data.environment, true);
|
|
36
|
-
if (conditionIsFalse !==
|
|
36
|
+
if (conditionIsFalse !== 0 /* Ternary.Always */) {
|
|
37
37
|
then = (0, processor_1.processDataflowFor)(thenArg, data);
|
|
38
38
|
if (then.entryPoint) {
|
|
39
39
|
then.graph.addEdge(name.info.id, then.entryPoint, { type: 8 /* EdgeType.Returns */ });
|
|
40
40
|
}
|
|
41
|
-
if (conditionIsTrue !==
|
|
41
|
+
if (conditionIsTrue !== 0 /* Ternary.Always */) {
|
|
42
42
|
makeThenMaybe = true;
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
let otherwise;
|
|
46
46
|
let makeOtherwiseMaybe = false;
|
|
47
|
-
if (otherwiseArg !== undefined && conditionIsTrue !==
|
|
47
|
+
if (otherwiseArg !== undefined && conditionIsTrue !== 0 /* Ternary.Always */) {
|
|
48
48
|
otherwise = (0, processor_1.processDataflowFor)(otherwiseArg, data);
|
|
49
49
|
if (otherwise.entryPoint) {
|
|
50
50
|
otherwise.graph.addEdge(name.info.id, otherwise.entryPoint, { type: 8 /* EdgeType.Returns */ });
|
|
51
51
|
}
|
|
52
|
-
if (conditionIsFalse !==
|
|
52
|
+
if (conditionIsFalse !== 0 /* Ternary.Always */) {
|
|
53
53
|
makeOtherwiseMaybe = true;
|
|
54
54
|
}
|
|
55
55
|
}
|
|
@@ -5,6 +5,7 @@ const known_call_handling_1 = require("../known-call-handling");
|
|
|
5
5
|
const logger_1 = require("../../../../../logger");
|
|
6
6
|
const unpack_argument_1 = require("../argument/unpack-argument");
|
|
7
7
|
const make_argument_1 = require("../argument/make-argument");
|
|
8
|
+
/* we currently do not mark this as an unknown side effect, as we can enable/disable this with a toggle */
|
|
8
9
|
function processLibrary(name, args, rootId, data) {
|
|
9
10
|
if (args.length !== 1) {
|
|
10
11
|
logger_1.dataflowLogger.warn(`Currently only one-arg library-likes are allows (for ${name.content}), skipping`);
|
|
@@ -4,6 +4,7 @@ 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
|
-
|
|
7
|
+
import type { ForceArguments } from '../common';
|
|
8
|
+
export declare function processQuote<OtherInfo>(name: RSymbol<OtherInfo & ParentInformation>, args: readonly RFunctionArgument<OtherInfo & ParentInformation>[], rootId: NodeId, data: DataflowProcessorInformation<OtherInfo & ParentInformation>, config: {
|
|
8
9
|
quoteArgumentsWithIndex?: number;
|
|
9
|
-
}): DataflowInformation;
|
|
10
|
+
} & ForceArguments): DataflowInformation;
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.processQuote = void 0;
|
|
4
4
|
const known_call_handling_1 = require("../known-call-handling");
|
|
5
5
|
function processQuote(name, args, rootId, data, config) {
|
|
6
|
-
const { information, processedArguments, fnRef } = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data });
|
|
6
|
+
const { information, processedArguments, fnRef } = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, forceArgs: config.forceArgs });
|
|
7
7
|
const inRefs = [fnRef];
|
|
8
8
|
const outRefs = [];
|
|
9
9
|
const unknownRefs = [];
|
|
@@ -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,7 +8,7 @@ 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;
|
|
@@ -23,4 +24,4 @@ export interface ProcessKnownFunctionCallResult {
|
|
|
23
24
|
readonly fnRef: IdentifierReference;
|
|
24
25
|
}
|
|
25
26
|
export declare function markNonStandardEvaluationEdges(markAsNSE: readonly number[] | undefined, callArgs: readonly (DataflowInformation | undefined)[], finalGraph: DataflowGraph, rootId: NodeId): void;
|
|
26
|
-
export declare function processKnownFunctionCall<OtherInfo>({ name, args, rootId, data, reverseOrder, markAsNSE, patchData }: ProcessKnownFunctionCallInput<OtherInfo>): ProcessKnownFunctionCallResult;
|
|
27
|
+
export declare function processKnownFunctionCall<OtherInfo>({ name, args, rootId, data, reverseOrder, markAsNSE, forceArgs, patchData }: ProcessKnownFunctionCallInput<OtherInfo>): ProcessKnownFunctionCallResult;
|
|
@@ -22,13 +22,13 @@ function markNonStandardEvaluationEdges(markAsNSE, callArgs, finalGraph, rootId)
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
exports.markNonStandardEvaluationEdges = markNonStandardEvaluationEdges;
|
|
25
|
-
function processKnownFunctionCall({ name, args, rootId, data, reverseOrder = false, markAsNSE = undefined, patchData = d => d }) {
|
|
25
|
+
function processKnownFunctionCall({ name, args, rootId, data, reverseOrder = false, markAsNSE = undefined, forceArgs, patchData = d => d }) {
|
|
26
26
|
const functionName = (0, processor_1.processDataflowFor)(name, data);
|
|
27
27
|
const finalGraph = new graph_1.DataflowGraph(data.completeAst.idMap);
|
|
28
28
|
const functionCallName = name.content;
|
|
29
29
|
logger_1.dataflowLogger.debug(`Using ${rootId} (name: ${functionCallName}) as root for the named function call`);
|
|
30
30
|
const processArgs = reverseOrder ? [...args].reverse() : args;
|
|
31
|
-
const { finalEnv, callArgs, remainingReadInArgs, processedArguments } = (0, common_1.processAllArguments)({ functionName, args: processArgs, data, finalGraph, functionRootId: rootId, patchData });
|
|
31
|
+
const { finalEnv, callArgs, remainingReadInArgs, processedArguments } = (0, common_1.processAllArguments)({ functionName, args: processArgs, data, finalGraph, functionRootId: rootId, patchData, forceArgs });
|
|
32
32
|
markNonStandardEvaluationEdges(markAsNSE, processedArguments, finalGraph, rootId);
|
|
33
33
|
finalGraph.addVertex({
|
|
34
34
|
tag: "function-call" /* VertexType.FunctionCall */,
|
|
@@ -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.17",
|
|
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";
|
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',
|
|
@@ -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}`);
|