@eagleoutice/flowr 2.1.8 → 2.1.9
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/benchmark/summarizer/first-phase/process.js +6 -5
- package/cli/repl/commands/repl-dataflow.js +5 -2
- package/cli/repl/commands/repl-normalize.js +5 -2
- package/cli/repl/commands/repl-query.js +2 -2
- package/cli/repl/server/messages/message-query.js +1 -1
- package/dataflow/environments/default-builtin-config.js +45 -6
- package/dataflow/environments/environment.d.ts +46 -8
- package/dataflow/environments/environment.js +24 -1
- package/dataflow/environments/identifier.d.ts +49 -7
- package/dataflow/environments/identifier.js +11 -2
- package/dataflow/extractor.js +5 -4
- package/dataflow/graph/dataflowgraph-builder.d.ts +6 -0
- package/dataflow/graph/dataflowgraph-builder.js +8 -0
- package/dataflow/graph/edge.d.ts +10 -4
- package/dataflow/graph/edge.js +12 -5
- package/dataflow/graph/graph.d.ts +41 -3
- package/dataflow/graph/graph.js +39 -34
- package/dataflow/graph/vertex.d.ts +66 -7
- package/dataflow/graph/vertex.js +15 -0
- package/dataflow/info.d.ts +79 -11
- package/dataflow/info.js +20 -0
- package/dataflow/internal/linker.d.ts +4 -2
- package/dataflow/internal/linker.js +12 -5
- package/dataflow/internal/process/functions/call/built-in/built-in-assignment.d.ts +2 -0
- package/dataflow/internal/process/functions/call/built-in/built-in-assignment.js +5 -3
- package/dataflow/internal/process/functions/call/built-in/built-in-function-definition.d.ts +16 -0
- package/dataflow/internal/process/functions/call/built-in/built-in-function-definition.js +83 -6
- package/dataflow/internal/process/functions/call/common.js +1 -1
- package/documentation/doc-util/doc-dfg.d.ts +0 -1
- package/documentation/doc-util/doc-dfg.js +1 -14
- package/documentation/print-capabilities-markdown.js +1 -1
- package/documentation/print-dataflow-graph-wiki.js +26 -7
- package/documentation/print-linting-and-testing-wiki.js +60 -26
- package/documentation/print-query-wiki.js +1 -1
- package/package.json +17 -3
- package/queries/catalog/call-context-query/call-context-query-executor.js +1 -1
- package/queries/catalog/call-context-query/call-context-query-format.d.ts +13 -0
- package/queries/catalog/call-context-query/call-context-query-format.js +3 -1
- package/queries/catalog/call-context-query/cascade-action.d.ts +8 -0
- package/queries/catalog/call-context-query/cascade-action.js +13 -0
- package/queries/catalog/call-context-query/identify-link-to-last-call-relation.d.ts +11 -1
- package/queries/catalog/call-context-query/identify-link-to-last-call-relation.js +41 -4
- package/queries/catalog/dependencies-query/dependencies-query-format.js +4 -0
- package/queries/query.d.ts +4 -4
- package/queries/query.js +17 -5
- package/r-bridge/lang-4.x/ast/model/model.d.ts +3 -0
- package/r-bridge/lang-4.x/ast/model/nodes/r-number.d.ts +5 -1
- package/r-bridge/lang-4.x/ast/model/processing/node-id.d.ts +6 -1
- package/r-bridge/lang-4.x/ast/model/processing/node-id.js +6 -1
- package/slicing/static/slice-call.d.ts +7 -2
- package/slicing/static/slice-call.js +33 -44
- package/slicing/static/static-slicer.d.ts +5 -1
- package/slicing/static/static-slicer.js +22 -8
- package/slicing/static/visiting-queue.d.ts +4 -4
- package/slicing/static/visiting-queue.js +5 -3
- package/statistics/output/print-stats.js +2 -1
- package/statistics/summarizer/post-process/histogram.js +2 -1
- package/statistics/summarizer/post-process/post-process-output.js +2 -1
- package/statistics/summarizer/second-phase/process.js +3 -3
- package/util/arrays.d.ts +1 -1
- package/util/arrays.js +3 -3
- package/util/cfg/cfg.js +4 -2
- package/util/mermaid/cfg.js +1 -1
- package/util/summarizer.js +2 -2
- package/util/version.js +1 -1
|
@@ -42,6 +42,7 @@ const shell_1 = require("../../../r-bridge/shell");
|
|
|
42
42
|
const retriever_1 = require("../../../r-bridge/retriever");
|
|
43
43
|
const visitor_1 = require("../../../r-bridge/lang-4.x/ast/model/processing/visitor");
|
|
44
44
|
const type_1 = require("../../../r-bridge/lang-4.x/ast/model/type");
|
|
45
|
+
const arrays_1 = require("../../../util/arrays");
|
|
45
46
|
const tempfile = (() => {
|
|
46
47
|
let _tempfile = undefined;
|
|
47
48
|
return () => {
|
|
@@ -244,15 +245,15 @@ async function summarizeSlicerStats(stats, report = () => {
|
|
|
244
245
|
}
|
|
245
246
|
function summarizeSummarizedMeasurement(data) {
|
|
246
247
|
data = data.filter(assert_1.isNotUndefined);
|
|
247
|
-
const min = data.map(d => d.min).filter(assert_1.isNotUndefined)
|
|
248
|
-
const max = data.map(d => d.max).filter(assert_1.isNotUndefined)
|
|
248
|
+
const min = Math.min(...data.map(d => d.min).filter(assert_1.isNotUndefined));
|
|
249
|
+
const max = Math.max(...data.map(d => d.max).filter(assert_1.isNotUndefined));
|
|
249
250
|
// calculate median of medians (don't just average the median!)
|
|
250
251
|
const medians = data.map(d => d.median).filter(assert_1.isNotUndefined).sort((a, b) => a - b);
|
|
251
252
|
const median = medians[Math.floor(medians.length / 2)];
|
|
252
|
-
const mean = data.map(d => d.mean).filter(assert_1.isNotUndefined)
|
|
253
|
+
const mean = (0, arrays_1.arraySum)(data.map(d => d.mean).filter(assert_1.isNotUndefined)) / data.length;
|
|
253
254
|
// Method 1 of https://www.statology.org/averaging-standard-deviations/
|
|
254
|
-
const std = Math.sqrt(data.map(d => d.std ** 2).filter(assert_1.isNotUndefined)
|
|
255
|
-
const total = data.map(d => d.total).filter(assert_1.isNotUndefined)
|
|
255
|
+
const std = Math.sqrt((0, arrays_1.arraySum)(data.map(d => d.std ** 2).filter(assert_1.isNotUndefined)) / data.length);
|
|
256
|
+
const total = (0, arrays_1.arraySum)(data.map(d => d.total).filter(assert_1.isNotUndefined));
|
|
256
257
|
return { min, max, median, mean, std, total };
|
|
257
258
|
}
|
|
258
259
|
function summarizeSummarizedReductions(reductions) {
|
|
@@ -11,13 +11,16 @@ async function dataflow(shell, remainingLine) {
|
|
|
11
11
|
request: (0, retriever_1.requestFromInput)(remainingLine.trim())
|
|
12
12
|
}).allRemainingSteps();
|
|
13
13
|
}
|
|
14
|
+
function handleString(code) {
|
|
15
|
+
return code.startsWith('"') ? JSON.parse(code) : code;
|
|
16
|
+
}
|
|
14
17
|
exports.dataflowCommand = {
|
|
15
18
|
description: `Get mermaid code for the dataflow graph of R code, start with '${retriever_1.fileProtocol}' to indicate a file`,
|
|
16
19
|
usageExample: ':dataflow',
|
|
17
20
|
aliases: ['d', 'df'],
|
|
18
21
|
script: false,
|
|
19
22
|
fn: async (output, shell, remainingLine) => {
|
|
20
|
-
const result = await dataflow(shell, remainingLine);
|
|
23
|
+
const result = await dataflow(shell, handleString(remainingLine));
|
|
21
24
|
output.stdout((0, dfg_1.graphToMermaid)({ graph: result.dataflow.graph, includeEnvironments: false }).string);
|
|
22
25
|
}
|
|
23
26
|
};
|
|
@@ -27,7 +30,7 @@ exports.dataflowStarCommand = {
|
|
|
27
30
|
aliases: ['d*', 'df*'],
|
|
28
31
|
script: false,
|
|
29
32
|
fn: async (output, shell, remainingLine) => {
|
|
30
|
-
const result = await dataflow(shell, remainingLine);
|
|
33
|
+
const result = await dataflow(shell, handleString(remainingLine));
|
|
31
34
|
output.stdout((0, dfg_1.graphToMermaidUrl)(result.dataflow.graph, false));
|
|
32
35
|
}
|
|
33
36
|
};
|
|
@@ -11,13 +11,16 @@ async function normalize(shell, remainingLine) {
|
|
|
11
11
|
request: (0, retriever_1.requestFromInput)(remainingLine.trim())
|
|
12
12
|
}).allRemainingSteps();
|
|
13
13
|
}
|
|
14
|
+
function handleString(code) {
|
|
15
|
+
return code.startsWith('"') ? JSON.parse(code) : code;
|
|
16
|
+
}
|
|
14
17
|
exports.normalizeCommand = {
|
|
15
18
|
description: `Get mermaid code for the normalized AST of R code, start with '${retriever_1.fileProtocol}' to indicate a file`,
|
|
16
19
|
usageExample: ':normalize',
|
|
17
20
|
aliases: ['n'],
|
|
18
21
|
script: false,
|
|
19
22
|
fn: async (output, shell, remainingLine) => {
|
|
20
|
-
const result = await normalize(shell, remainingLine);
|
|
23
|
+
const result = await normalize(shell, handleString(remainingLine));
|
|
21
24
|
output.stdout((0, ast_1.normalizedAstToMermaid)(result.normalize.ast));
|
|
22
25
|
}
|
|
23
26
|
};
|
|
@@ -27,7 +30,7 @@ exports.normalizeStarCommand = {
|
|
|
27
30
|
aliases: ['n*'],
|
|
28
31
|
script: false,
|
|
29
32
|
fn: async (output, shell, remainingLine) => {
|
|
30
|
-
const result = await normalize(shell, remainingLine);
|
|
33
|
+
const result = await normalize(shell, handleString(remainingLine));
|
|
31
34
|
output.stdout((0, ast_1.normalizedAstToMermaidUrl)(result.normalize.ast));
|
|
32
35
|
}
|
|
33
36
|
};
|
|
@@ -19,7 +19,7 @@ async function getDataflow(shell, remainingLine) {
|
|
|
19
19
|
function printHelp(output) {
|
|
20
20
|
output.stderr(`Format: ${(0, ansi_1.italic)(':query "<query>" <code>', output.formatter)}`);
|
|
21
21
|
output.stdout('The query is an array of query objects to represent multiple queries. Each query object may have the following properties:');
|
|
22
|
-
output.stdout((0, schema_1.describeSchema)(query_1.AnyQuerySchema, output.formatter));
|
|
22
|
+
output.stdout((0, schema_1.describeSchema)((0, query_1.AnyQuerySchema)(), output.formatter));
|
|
23
23
|
output.stdout(`\n\nThe example ${(0, ansi_1.italic)(':query "[{\\"type\\": \\"call-context\\", \\"callName\\": \\"mean\\" }]" mean(1:10)', output.formatter)} would return the call context of the mean function.`);
|
|
24
24
|
output.stdout('As a convenience, we interpret any (non-help) string not starting with \'[\' as a regex for the simple call-context query.');
|
|
25
25
|
output.stdout(`Hence, ${(0, ansi_1.italic)(':query "mean" mean(1:10)', output.formatter)} is equivalent to the above example.`);
|
|
@@ -38,7 +38,7 @@ async function processQueryArgs(line, shell, output) {
|
|
|
38
38
|
let parsedQuery = [];
|
|
39
39
|
if (query.startsWith('[')) {
|
|
40
40
|
parsedQuery = JSON.parse(query);
|
|
41
|
-
const validationResult = query_1.QueriesSchema.validate(parsedQuery);
|
|
41
|
+
const validationResult = (0, query_1.QueriesSchema)().validate(parsedQuery);
|
|
42
42
|
if (validationResult.error) {
|
|
43
43
|
output.stderr(`Invalid query: ${validationResult.error.message}`);
|
|
44
44
|
printHelp(output);
|
|
@@ -12,7 +12,7 @@ exports.requestQueryMessage = {
|
|
|
12
12
|
type: joi_1.default.string().valid('request-query').required().description('The type of the message.'),
|
|
13
13
|
id: joi_1.default.string().optional().description('If you give the id, the response will be sent to the client with the same id.'),
|
|
14
14
|
filetoken: joi_1.default.string().required().description('The filetoken of the file/data retrieved from the analysis request.'),
|
|
15
|
-
query: query_1.QueriesSchema.required().description('The query to run on the file analysis information.')
|
|
15
|
+
query: (0, query_1.QueriesSchema)().required().description('The query to run on the file analysis information.')
|
|
16
16
|
}).description('Request a query to be run on the file analysis information.')
|
|
17
17
|
};
|
|
18
18
|
exports.responseQueryMessage = {
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DefaultBuiltinConfig = void 0;
|
|
4
|
+
const identify_link_to_last_call_relation_1 = require("../../queries/catalog/call-context-query/identify-link-to-last-call-relation");
|
|
5
|
+
const type_1 = require("../../r-bridge/lang-4.x/ast/model/type");
|
|
6
|
+
const cascade_action_1 = require("../../queries/catalog/call-context-query/cascade-action");
|
|
4
7
|
/**
|
|
5
8
|
* Contains the built-in definitions recognized by flowR
|
|
6
9
|
*/
|
|
@@ -40,10 +43,45 @@ exports.DefaultBuiltinConfig = [
|
|
|
40
43
|
{ type: 'function', names: ['print', 'message', 'warning'], processor: 'builtin:default', config: { returnsNthArgument: 0, forceArgs: 'all', hasUnknownSideEffects: { type: 'link-to-last-call', callName: /^sink$/ } }, assumePrimitive: false },
|
|
41
44
|
// graphics base
|
|
42
45
|
{ type: 'function', names: ['plot', 'plot.new', 'curve', 'map', 'image', 'boxplot', 'dotchart', 'sunflowerplot', 'barplot', 'matplot', 'hist', 'stem', 'density', 'smoothScatter', 'contour', 'persp'],
|
|
43
|
-
processor: 'builtin:default',
|
|
46
|
+
processor: 'builtin:default',
|
|
47
|
+
config: {
|
|
48
|
+
forceArgs: 'all',
|
|
49
|
+
hasUnknownSideEffects: {
|
|
50
|
+
type: 'link-to-last-call',
|
|
51
|
+
ignoreIf: (source, graph) => {
|
|
52
|
+
/* map with add = true appends to an existing plot */
|
|
53
|
+
return (source.name === 'map' && (0, identify_link_to_last_call_relation_1.getValueOfArgument)(graph, source, {
|
|
54
|
+
index: 11,
|
|
55
|
+
name: 'add'
|
|
56
|
+
}, [type_1.RType.Logical])?.content === true);
|
|
57
|
+
},
|
|
58
|
+
callName: /^(pdf|jpeg|png|windows|postscript|xfig|bitmap|pictex|cairo_pdf|svg|bmp|tiff|X11|quartz)$/
|
|
59
|
+
}
|
|
60
|
+
}, assumePrimitive: true },
|
|
44
61
|
// graphics addons
|
|
45
|
-
{ type: 'function', names: ['points', 'abline', 'mtext', 'lines', 'text', 'legend', 'title', 'axis', 'polygon', 'polypath', 'pie', 'rect', 'segments', 'arrows', 'symbols', 'tiplabels'],
|
|
46
|
-
processor: 'builtin:default', config: {
|
|
62
|
+
{ type: 'function', names: ['points', 'abline', 'map', 'mtext', 'lines', 'text', 'legend', 'title', 'axis', 'polygon', 'polypath', 'pie', 'rect', 'segments', 'arrows', 'symbols', 'tiplabels'],
|
|
63
|
+
processor: 'builtin:default', config: {
|
|
64
|
+
forceArgs: 'all',
|
|
65
|
+
hasUnknownSideEffects: {
|
|
66
|
+
type: 'link-to-last-call',
|
|
67
|
+
callName: /^(dev\.new|dev\.copy|plot\.new|xspline|sunflowerplot|dotchart|plot|map|image|curve|boxplot|barplot|matplot|hist|stem|density|smoothScatter|contour|persp)$/,
|
|
68
|
+
ignoreIf: (source, graph) => {
|
|
69
|
+
const sourceVertex = graph.getVertex(source);
|
|
70
|
+
/* map with add = true appends to an existing plot */
|
|
71
|
+
return (sourceVertex?.name === 'map' && (0, identify_link_to_last_call_relation_1.getValueOfArgument)(graph, sourceVertex, {
|
|
72
|
+
index: 11,
|
|
73
|
+
name: 'add'
|
|
74
|
+
}, [type_1.RType.Logical])?.content !== true);
|
|
75
|
+
},
|
|
76
|
+
cascadeIf: (target, _, graph) => {
|
|
77
|
+
/* map with add = true appends to an existing plot */
|
|
78
|
+
return target.name === 'map' ? ((0, identify_link_to_last_call_relation_1.getValueOfArgument)(graph, target, {
|
|
79
|
+
index: 11,
|
|
80
|
+
name: 'add'
|
|
81
|
+
}, [type_1.RType.Logical])?.content === true ? cascade_action_1.CascadeAction.Continue : cascade_action_1.CascadeAction.Stop) : cascade_action_1.CascadeAction.Stop;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}, assumePrimitive: true },
|
|
47
85
|
{ type: 'function', names: ['('], processor: 'builtin:default', config: { returnsNthArgument: 0 }, assumePrimitive: true },
|
|
48
86
|
{ type: 'function', names: ['load', 'load_all', 'setwd', 'set.seed'], processor: 'builtin:default', config: { hasUnknownSideEffects: true, forceArgs: [true] }, assumePrimitive: false },
|
|
49
87
|
{ type: 'function', names: ['eval', 'body', 'formals', 'environment'], processor: 'builtin:default', config: { hasUnknownSideEffects: true, forceArgs: [true] }, assumePrimitive: false },
|
|
@@ -60,8 +98,9 @@ exports.DefaultBuiltinConfig = [
|
|
|
60
98
|
{ type: 'function', names: ['get'], processor: 'builtin:get', config: {}, assumePrimitive: false },
|
|
61
99
|
{ type: 'function', names: ['library', 'require'], processor: 'builtin:library', config: {}, assumePrimitive: false },
|
|
62
100
|
{ type: 'function', names: ['<-', '='], processor: 'builtin:assignment', config: { canBeReplacement: true }, assumePrimitive: true },
|
|
63
|
-
{ type: 'function', names: [':='
|
|
64
|
-
{ type: 'function', names: ['
|
|
101
|
+
{ type: 'function', names: [':='], processor: 'builtin:assignment', config: {}, assumePrimitive: true },
|
|
102
|
+
{ type: 'function', names: ['assign'], processor: 'builtin:assignment', config: { targetVariable: true }, assumePrimitive: true },
|
|
103
|
+
{ type: 'function', names: ['delayedAssign'], processor: 'builtin:assignment', config: { quoteSource: true, targetVariable: true }, assumePrimitive: true },
|
|
65
104
|
{ type: 'function', names: ['<<-'], processor: 'builtin:assignment', config: { superAssignment: true, canBeReplacement: true }, assumePrimitive: true },
|
|
66
105
|
{ type: 'function', names: ['->'], processor: 'builtin:assignment', config: { swapSourceAndTarget: true, canBeReplacement: true }, assumePrimitive: true },
|
|
67
106
|
{ type: 'function', names: ['->>'], processor: 'builtin:assignment', config: { superAssignment: true, swapSourceAndTarget: true, canBeReplacement: true }, assumePrimitive: true },
|
|
@@ -83,7 +122,7 @@ exports.DefaultBuiltinConfig = [
|
|
|
83
122
|
/* downloader and installer functions (R, devtools, BiocManager) */
|
|
84
123
|
'library.dynam', 'install.packages', 'install', 'install_github', 'install_gitlab', 'install_bitbucket', 'install_url', 'install_git', 'install_svn', 'install_local', 'install_version', 'update_packages',
|
|
85
124
|
/* weird env attachments */
|
|
86
|
-
'attach', 'unname'
|
|
125
|
+
'attach', 'unname', 'data'
|
|
87
126
|
],
|
|
88
127
|
processor: 'builtin:default',
|
|
89
128
|
config: { hasUnknownSideEffects: true },
|
|
@@ -7,19 +7,22 @@
|
|
|
7
7
|
import type { Identifier, IdentifierDefinition, IdentifierReference } from './identifier';
|
|
8
8
|
import type { DataflowGraph } from '../graph/graph';
|
|
9
9
|
import type { ControlDependency } from '../info';
|
|
10
|
+
/**
|
|
11
|
+
* Marks the reference as maybe (i.e., as controlled by a set of {@link IdentifierReference#controlDependencies|control dependencies}).
|
|
12
|
+
*/
|
|
10
13
|
export declare function makeReferenceMaybe(ref: IdentifierReference, graph: DataflowGraph, environments: REnvironmentInformation, includeDefs: boolean, defaultCd?: ControlDependency | undefined): IdentifierReference;
|
|
11
14
|
export declare function makeAllMaybe(references: readonly IdentifierReference[] | undefined, graph: DataflowGraph, environments: REnvironmentInformation, includeDefs: boolean, defaultCd?: ControlDependency | undefined): IdentifierReference[];
|
|
12
15
|
export type EnvironmentMemory = Map<Identifier, IdentifierDefinition[]>;
|
|
16
|
+
/** A single entry/scope within an {@link REnvironmentInformation} */
|
|
13
17
|
export interface IEnvironment {
|
|
14
|
-
/**
|
|
18
|
+
/** Unique and internally generated identifier -- will not be used for comparison but helps with debugging for tracking identities */
|
|
15
19
|
readonly id: number;
|
|
16
20
|
/** Lexical parent of the environment, if any (can be manipulated by R code) */
|
|
17
21
|
parent: IEnvironment;
|
|
18
|
-
/**
|
|
19
|
-
* Maps to exactly one definition of an identifier if the source is known, otherwise to a list of all possible definitions
|
|
20
|
-
*/
|
|
22
|
+
/** Maps to exactly one definition of an identifier if the source is known, otherwise to a list of all possible definitions */
|
|
21
23
|
memory: EnvironmentMemory;
|
|
22
24
|
}
|
|
25
|
+
/** @see REnvironmentInformation */
|
|
23
26
|
export declare class Environment implements IEnvironment {
|
|
24
27
|
readonly id: number;
|
|
25
28
|
parent: IEnvironment;
|
|
@@ -27,11 +30,26 @@ export declare class Environment implements IEnvironment {
|
|
|
27
30
|
constructor(parent: IEnvironment);
|
|
28
31
|
}
|
|
29
32
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
+
* An environment describes a ({@link IEnvironment#parent|scoped}) mapping of names to their definitions ({@link EnvironmentMemory}).
|
|
34
|
+
*
|
|
35
|
+
* First, yes, R stores its environments differently, potentially even with another differentiation between
|
|
36
|
+
* the `baseenv`, the `emptyenv`, and other default environments (see https://adv-r.hadley.nz/environments.html).
|
|
37
|
+
* Yet, during the dataflow analysis, we want sometimes to know more (static {@link IdentifierDefinition|reference information})
|
|
38
|
+
* and sometimes know less (to be honest, we do not want that,
|
|
33
39
|
* but statically determining all attached environments is theoretically impossible --- consider attachments by user input).
|
|
34
|
-
*
|
|
40
|
+
*
|
|
41
|
+
* One important environment is the {@link BuiltInEnvironment} which contains the default definitions for R's built-in functions and constants.
|
|
42
|
+
* Please use {@link initializeCleanEnvironments} to initialize the environments (which includes the built-ins).
|
|
43
|
+
* During serialization, you may want to rely on the {@link builtInEnvJsonReplacer} to avoid the huge built-in environment.
|
|
44
|
+
*
|
|
45
|
+
*
|
|
46
|
+
* @see {@link define} - to define a new {@link IdentifierDefinition|identifier definition} within an environment
|
|
47
|
+
* @see {@link resolveByName} - to resolve an {@link Identifier|identifier/name} to its {@link IdentifierDefinition|definitions} within an environment
|
|
48
|
+
* @see {@link makeReferenceMaybe} - to attach control dependencies to a reference
|
|
49
|
+
* @see {@link pushLocalEnvironment} - to create a new local scope
|
|
50
|
+
* @see {@link popLocalEnvironment} - to remove the current local scope
|
|
51
|
+
* @see {@link appendEnvironment} - to append an environment to the current one
|
|
52
|
+
* @see {@link overwriteEnvironment} - to overwrite the definitions in the current environment with those of another one
|
|
35
53
|
*/
|
|
36
54
|
export interface REnvironmentInformation {
|
|
37
55
|
/** The currently active environment (the stack is represented by the currently active {@link IEnvironment#parent}). Environments are maintained within the dataflow graph. */
|
|
@@ -39,7 +57,27 @@ export interface REnvironmentInformation {
|
|
|
39
57
|
/** nesting level of the environment, will be `0` for the global/root environment */
|
|
40
58
|
readonly level: number;
|
|
41
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* The built-in {@link REnvironmentInformation|environment} is the root of all environments.
|
|
62
|
+
*
|
|
63
|
+
* For its default content (when not overwritten by a flowR config),
|
|
64
|
+
* see the {@link DefaultBuiltinConfig}.
|
|
65
|
+
*/
|
|
42
66
|
export declare const BuiltInEnvironment: Environment;
|
|
67
|
+
/**
|
|
68
|
+
* The twin of the {@link BuiltInEnvironment} but with less built ins defined for
|
|
69
|
+
* cases in which we want some commonly overwritten variables to remain open.
|
|
70
|
+
* If you do not know if you need the empty environment, you do not need the empty environment (right now).
|
|
71
|
+
*
|
|
72
|
+
* @see {@link BuiltInEnvironment}
|
|
73
|
+
*/
|
|
43
74
|
export declare const EmptyBuiltInEnvironment: IEnvironment;
|
|
75
|
+
/**
|
|
76
|
+
* Initialize a new {@link REnvironmentInformation|environment} with the built-ins.
|
|
77
|
+
* See {@link EmptyBuiltInEnvironment} for the case `fullBuiltIns = false`.
|
|
78
|
+
*/
|
|
44
79
|
export declare function initializeCleanEnvironments(fullBuiltIns?: boolean): REnvironmentInformation;
|
|
80
|
+
/**
|
|
81
|
+
* Helps to serialize an environment, but replaces the built-in environment with a placeholder.
|
|
82
|
+
*/
|
|
45
83
|
export declare function builtInEnvJsonReplacer(k: unknown, v: unknown): unknown;
|
|
@@ -9,6 +9,9 @@ const identifier_1 = require("./identifier");
|
|
|
9
9
|
const built_in_1 = require("./built-in");
|
|
10
10
|
const resolve_by_name_1 = require("./resolve-by-name");
|
|
11
11
|
const json_1 = require("../../util/json");
|
|
12
|
+
/**
|
|
13
|
+
* Marks the reference as maybe (i.e., as controlled by a set of {@link IdentifierReference#controlDependencies|control dependencies}).
|
|
14
|
+
*/
|
|
12
15
|
function makeReferenceMaybe(ref, graph, environments, includeDefs, defaultCd = undefined) {
|
|
13
16
|
const node = graph.get(ref.nodeId, true);
|
|
14
17
|
if (includeDefs) {
|
|
@@ -42,6 +45,7 @@ function makeAllMaybe(references, graph, environments, includeDefs, defaultCd =
|
|
|
42
45
|
return references.map(ref => makeReferenceMaybe(ref, graph, environments, includeDefs, defaultCd));
|
|
43
46
|
}
|
|
44
47
|
let environmentIdCounter = 0;
|
|
48
|
+
/** @see REnvironmentInformation */
|
|
45
49
|
class Environment {
|
|
46
50
|
id = environmentIdCounter++;
|
|
47
51
|
parent;
|
|
@@ -52,14 +56,30 @@ class Environment {
|
|
|
52
56
|
}
|
|
53
57
|
}
|
|
54
58
|
exports.Environment = Environment;
|
|
55
|
-
|
|
59
|
+
/**
|
|
60
|
+
* The built-in {@link REnvironmentInformation|environment} is the root of all environments.
|
|
61
|
+
*
|
|
62
|
+
* For its default content (when not overwritten by a flowR config),
|
|
63
|
+
* see the {@link DefaultBuiltinConfig}.
|
|
64
|
+
*/
|
|
56
65
|
exports.BuiltInEnvironment = new Environment(undefined);
|
|
57
66
|
exports.BuiltInEnvironment.memory = undefined;
|
|
67
|
+
/**
|
|
68
|
+
* The twin of the {@link BuiltInEnvironment} but with less built ins defined for
|
|
69
|
+
* cases in which we want some commonly overwritten variables to remain open.
|
|
70
|
+
* If you do not know if you need the empty environment, you do not need the empty environment (right now).
|
|
71
|
+
*
|
|
72
|
+
* @see {@link BuiltInEnvironment}
|
|
73
|
+
*/
|
|
58
74
|
exports.EmptyBuiltInEnvironment = {
|
|
59
75
|
id: exports.BuiltInEnvironment.id,
|
|
60
76
|
memory: undefined,
|
|
61
77
|
parent: undefined
|
|
62
78
|
};
|
|
79
|
+
/**
|
|
80
|
+
* Initialize a new {@link REnvironmentInformation|environment} with the built-ins.
|
|
81
|
+
* See {@link EmptyBuiltInEnvironment} for the case `fullBuiltIns = false`.
|
|
82
|
+
*/
|
|
63
83
|
function initializeCleanEnvironments(fullBuiltIns = true) {
|
|
64
84
|
exports.BuiltInEnvironment.memory ??= built_in_1.BuiltInMemory;
|
|
65
85
|
exports.EmptyBuiltInEnvironment.memory ??= built_in_1.EmptyBuiltInMemory;
|
|
@@ -68,6 +88,9 @@ function initializeCleanEnvironments(fullBuiltIns = true) {
|
|
|
68
88
|
level: 0
|
|
69
89
|
};
|
|
70
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Helps to serialize an environment, but replaces the built-in environment with a placeholder.
|
|
93
|
+
*/
|
|
71
94
|
function builtInEnvJsonReplacer(k, v) {
|
|
72
95
|
if (v === exports.BuiltInEnvironment) {
|
|
73
96
|
return '<BuiltInEnvironment>';
|
|
@@ -5,9 +5,17 @@ export type Identifier = string & {
|
|
|
5
5
|
__brand?: 'identifier';
|
|
6
6
|
};
|
|
7
7
|
/**
|
|
8
|
-
* Each reference
|
|
9
|
-
* However, when checking we may want to allow for one of several types,
|
|
8
|
+
* Each reference has exactly one reference type, stored as the respective number.
|
|
9
|
+
* However, when checking, we may want to allow for one of several types,
|
|
10
10
|
* allowing the combination of the respective bitmasks.
|
|
11
|
+
*
|
|
12
|
+
* Having reference types is important as R separates a variable definition from
|
|
13
|
+
* a function when resolving an {@link Identifier|identifier}.
|
|
14
|
+
* In `c <- 3; print(c(1, 2))` the call to `c` works normally (as the vector constructor),
|
|
15
|
+
* while writing `c <- function(...) ..1` overshadows the built-in and causes `print` to only output the first element.
|
|
16
|
+
*
|
|
17
|
+
* @see {@link isReferenceType} - for checking if a (potentially joint) reference type contains a certain type
|
|
18
|
+
* @see {@link ReferenceTypeReverseMapping} - for debugging
|
|
11
19
|
*/
|
|
12
20
|
export declare enum ReferenceType {
|
|
13
21
|
/** The identifier type is unknown */
|
|
@@ -27,19 +35,38 @@ export declare enum ReferenceType {
|
|
|
27
35
|
/** The identifier is defined by a built-in function */
|
|
28
36
|
BuiltInFunction = 128
|
|
29
37
|
}
|
|
38
|
+
/** Reverse mapping of the reference types so you can get the name from the bitmask (useful for debugging) */
|
|
30
39
|
export declare const ReferenceTypeReverseMapping: Map<ReferenceType, string>;
|
|
31
40
|
/**
|
|
32
41
|
* Check if the reference types have an overlapping type!
|
|
33
42
|
*/
|
|
34
43
|
export declare function isReferenceType(t: ReferenceType, target: ReferenceType): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Describes all types of reference (definitions) that can appear within a graph (i.e., that are not built-in like the
|
|
46
|
+
* default definition for the assignment operator `<-`).
|
|
47
|
+
*
|
|
48
|
+
* @see {@link InGraphIdentifierDefinition} - for the definition of an identifier within the graph
|
|
49
|
+
*/
|
|
35
50
|
export type InGraphReferenceType = Exclude<ReferenceType, ReferenceType.BuiltInConstant | ReferenceType.BuiltInFunction>;
|
|
36
51
|
/**
|
|
37
|
-
*
|
|
38
|
-
* Without any surrounding
|
|
39
|
-
* Similarly, `b` will create a reference
|
|
52
|
+
* An identifier reference points to a variable like `a` in `b <- a`.
|
|
53
|
+
* Without any surrounding code, `a` will produce the identifier reference `a`.
|
|
54
|
+
* Similarly, `b` will create a reference (although it will be an {@link IdentifierDefinition|identifier definition}
|
|
55
|
+
* which adds even more information).
|
|
56
|
+
*
|
|
57
|
+
* In general,
|
|
58
|
+
* references are merely pointers (with meta-information) to a vertex in the {@link DataflowGraph|dataflow graph}.
|
|
59
|
+
* In the context of the extractor, for example,
|
|
60
|
+
* they indicate the references that are currently (during the analysis at this given node)
|
|
61
|
+
* {@link DataflowInformation#in|read (`in`)}, {@link DataflowInformation#out|written (`out`)},
|
|
62
|
+
* or {@link DataflowInformation#unknownReferences|unknown (`unknownReferences`)}.
|
|
63
|
+
*
|
|
64
|
+
* @see {@link InGraphIdentifierDefinition}
|
|
40
65
|
*/
|
|
41
66
|
export interface IdentifierReference {
|
|
42
|
-
/**
|
|
67
|
+
/**
|
|
68
|
+
* The id of the node which represents the reference in the {@link NormalizedAst|normalized AST} and the {@link DataflowGraph|dataflow graph}.
|
|
69
|
+
*/
|
|
43
70
|
readonly nodeId: NodeId;
|
|
44
71
|
/** Name the reference is identified by (e.g., the name of the variable), undefined if the reference is "artificial" (e.g., anonymous) */
|
|
45
72
|
readonly name: Identifier | undefined;
|
|
@@ -51,13 +78,28 @@ export interface IdentifierReference {
|
|
|
51
78
|
*/
|
|
52
79
|
controlDependencies: ControlDependency[] | undefined;
|
|
53
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* The definition of an {@link Identifier|identifier} within the {@link DataflowGraph|graph}.
|
|
83
|
+
* This extends on the {@link IdentifierReference}
|
|
84
|
+
* by adding the {@link NodeId} of the definition
|
|
85
|
+
* (and using `type` to mark the object type).
|
|
86
|
+
*
|
|
87
|
+
* Within a code snippet like `a <- 3`, the symbol processor will first create an
|
|
88
|
+
* {@link IdentifierReference|identifier reference} for `a` to reference the use
|
|
89
|
+
* and then promote it to an {@link InGraphIdentifierDefinition|identifier definition}.
|
|
90
|
+
*
|
|
91
|
+
* @see {@link IdentifierReference}
|
|
92
|
+
*/
|
|
54
93
|
interface InGraphIdentifierDefinition extends IdentifierReference {
|
|
55
94
|
readonly type: InGraphReferenceType;
|
|
56
95
|
/** The assignment (or whatever, like `assign` function call) node which ultimately defined this identifier */
|
|
57
96
|
readonly definedAt: NodeId;
|
|
58
97
|
}
|
|
59
98
|
/**
|
|
60
|
-
* Stores the definition of an identifier within an {@link IEnvironment}
|
|
99
|
+
* Stores the definition of an identifier within an {@link IEnvironment}.
|
|
100
|
+
*
|
|
101
|
+
* {@link BuiltInIdentifierDefinition} and {@link BuiltInIdentifierConstant} are used for built-in functions and constants only,
|
|
102
|
+
* so the most important one for your day-to-day R script is the {@link InGraphIdentifierDefinition}.
|
|
61
103
|
*/
|
|
62
104
|
export type IdentifierDefinition = InGraphIdentifierDefinition | BuiltInIdentifierDefinition | BuiltInIdentifierConstant;
|
|
63
105
|
export {};
|
|
@@ -3,9 +3,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.ReferenceTypeReverseMapping = exports.ReferenceType = void 0;
|
|
4
4
|
exports.isReferenceType = isReferenceType;
|
|
5
5
|
/**
|
|
6
|
-
* Each reference
|
|
7
|
-
* However, when checking we may want to allow for one of several types,
|
|
6
|
+
* Each reference has exactly one reference type, stored as the respective number.
|
|
7
|
+
* However, when checking, we may want to allow for one of several types,
|
|
8
8
|
* allowing the combination of the respective bitmasks.
|
|
9
|
+
*
|
|
10
|
+
* Having reference types is important as R separates a variable definition from
|
|
11
|
+
* a function when resolving an {@link Identifier|identifier}.
|
|
12
|
+
* In `c <- 3; print(c(1, 2))` the call to `c` works normally (as the vector constructor),
|
|
13
|
+
* while writing `c <- function(...) ..1` overshadows the built-in and causes `print` to only output the first element.
|
|
14
|
+
*
|
|
15
|
+
* @see {@link isReferenceType} - for checking if a (potentially joint) reference type contains a certain type
|
|
16
|
+
* @see {@link ReferenceTypeReverseMapping} - for debugging
|
|
9
17
|
*/
|
|
10
18
|
var ReferenceType;
|
|
11
19
|
(function (ReferenceType) {
|
|
@@ -26,6 +34,7 @@ var ReferenceType;
|
|
|
26
34
|
/** The identifier is defined by a built-in function */
|
|
27
35
|
ReferenceType[ReferenceType["BuiltInFunction"] = 128] = "BuiltInFunction";
|
|
28
36
|
})(ReferenceType || (exports.ReferenceType = ReferenceType = {}));
|
|
37
|
+
/** Reverse mapping of the reference types so you can get the name from the bitmask (useful for debugging) */
|
|
29
38
|
exports.ReferenceTypeReverseMapping = new Map(Object.entries(ReferenceType).map(([k, v]) => [v, k]));
|
|
30
39
|
/**
|
|
31
40
|
* Check if the reference types have an overlapping type!
|
package/dataflow/extractor.js
CHANGED
|
@@ -20,6 +20,7 @@ const built_in_source_1 = require("./internal/process/functions/call/built-in/bu
|
|
|
20
20
|
const cfg_1 = require("../util/cfg/cfg");
|
|
21
21
|
const edge_1 = require("./graph/edge");
|
|
22
22
|
const identify_link_to_last_call_relation_1 = require("../queries/catalog/call-context-query/identify-link-to-last-call-relation");
|
|
23
|
+
const built_in_function_definition_1 = require("./internal/process/functions/call/built-in/built-in-function-definition");
|
|
23
24
|
exports.processors = {
|
|
24
25
|
[type_1.RType.Number]: process_value_1.processValue,
|
|
25
26
|
[type_1.RType.String]: process_value_1.processValue,
|
|
@@ -56,11 +57,9 @@ function resolveLinkToSideEffects(ast, graph) {
|
|
|
56
57
|
if (typeof s !== 'object') {
|
|
57
58
|
continue;
|
|
58
59
|
}
|
|
59
|
-
|
|
60
|
-
cfg = (0, cfg_1.extractCFG)(ast).graph;
|
|
61
|
-
}
|
|
60
|
+
cfg ??= (0, cfg_1.extractCFG)(ast).graph;
|
|
62
61
|
/* this has to change whenever we add a new link to relations because we currently offer no abstraction for the type */
|
|
63
|
-
const potentials = (0, identify_link_to_last_call_relation_1.identifyLinkToLastCallRelation)(s.id, cfg, graph, s.linkTo
|
|
62
|
+
const potentials = (0, identify_link_to_last_call_relation_1.identifyLinkToLastCallRelation)(s.id, cfg, graph, s.linkTo);
|
|
64
63
|
for (const pot of potentials) {
|
|
65
64
|
graph.addEdge(s.id, pot, edge_1.EdgeType.Reads);
|
|
66
65
|
}
|
|
@@ -92,6 +91,8 @@ function produceDataFlowGraph(request, ast) {
|
|
|
92
91
|
df = (0, built_in_source_1.standaloneSourceFile)(request[i], dfData, `root-${i}`, df);
|
|
93
92
|
}
|
|
94
93
|
}
|
|
94
|
+
// finally, resolve linkages
|
|
95
|
+
(0, built_in_function_definition_1.updateNestedFunctionCalls)(df.graph, df.environment);
|
|
95
96
|
resolveLinkToSideEffects(ast, df.graph);
|
|
96
97
|
return df;
|
|
97
98
|
}
|
|
@@ -114,6 +114,12 @@ export declare class DataflowGraphBuilder extends DataflowGraph {
|
|
|
114
114
|
* @see reads for parameters.
|
|
115
115
|
*/
|
|
116
116
|
definesOnCall(from: NodeId, to: DataflowGraphEdgeTarget): this;
|
|
117
|
+
/**
|
|
118
|
+
* Adds a **defined-by-on-call edge** with from as definition, and to as variable.
|
|
119
|
+
*
|
|
120
|
+
* @see reads for parameters.
|
|
121
|
+
*/
|
|
122
|
+
definedByOnCall(from: NodeId, to: DataflowGraphEdgeTarget): this;
|
|
117
123
|
/**
|
|
118
124
|
* Adds an **argument edge** (E9) with from as function call, and to as argument.
|
|
119
125
|
*
|
|
@@ -212,6 +212,14 @@ class DataflowGraphBuilder extends graph_1.DataflowGraph {
|
|
|
212
212
|
definesOnCall(from, to) {
|
|
213
213
|
return this.edgeHelper(from, to, edge_1.EdgeType.DefinesOnCall);
|
|
214
214
|
}
|
|
215
|
+
/**
|
|
216
|
+
* Adds a **defined-by-on-call edge** with from as definition, and to as variable.
|
|
217
|
+
*
|
|
218
|
+
* @see reads for parameters.
|
|
219
|
+
*/
|
|
220
|
+
definedByOnCall(from, to) {
|
|
221
|
+
return this.edgeHelper(from, to, edge_1.EdgeType.DefinedByOnCall);
|
|
222
|
+
}
|
|
215
223
|
/**
|
|
216
224
|
* Adds an **argument edge** (E9) with from as function call, and to as argument.
|
|
217
225
|
*
|
package/dataflow/graph/edge.d.ts
CHANGED
|
@@ -19,9 +19,15 @@ export declare enum EdgeType {
|
|
|
19
19
|
Calls = 4,
|
|
20
20
|
/** The source returns target on call */
|
|
21
21
|
Returns = 8,
|
|
22
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* The edge determines that source (probably argument) defines the target (probably parameter).
|
|
24
|
+
* This may also link a function call to definitions it causes to be active (as part of the closure) of the called function definition.
|
|
25
|
+
*/
|
|
23
26
|
DefinesOnCall = 16,
|
|
24
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* Usually the inverse of `defines-on-call` (in the context of arguments and parameters).
|
|
29
|
+
* This may also link an open read (within a function) to the definition that is active at the call site.
|
|
30
|
+
*/
|
|
25
31
|
DefinedByOnCall = 32,
|
|
26
32
|
/** Formal used as argument to a function call */
|
|
27
33
|
Argument = 64,
|
|
@@ -57,8 +63,8 @@ export declare const enum TraverseEdge {
|
|
|
57
63
|
Never = 0,
|
|
58
64
|
/** Traverse the edge as a side effect */
|
|
59
65
|
SideEffect = 1,
|
|
60
|
-
/** Traverse this edge if the definition is relevant */
|
|
61
|
-
|
|
66
|
+
/** Traverse this edge if the definition is relevant (i.e., if two matching edges trigger this state) */
|
|
67
|
+
OnlyIfBoth = 2,
|
|
62
68
|
/** Always traverse this edge */
|
|
63
69
|
Always = 3
|
|
64
70
|
}
|
package/dataflow/graph/edge.js
CHANGED
|
@@ -22,9 +22,15 @@ var EdgeType;
|
|
|
22
22
|
EdgeType[EdgeType["Calls"] = 4] = "Calls";
|
|
23
23
|
/** The source returns target on call */
|
|
24
24
|
EdgeType[EdgeType["Returns"] = 8] = "Returns";
|
|
25
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* The edge determines that source (probably argument) defines the target (probably parameter).
|
|
27
|
+
* This may also link a function call to definitions it causes to be active (as part of the closure) of the called function definition.
|
|
28
|
+
*/
|
|
26
29
|
EdgeType[EdgeType["DefinesOnCall"] = 16] = "DefinesOnCall";
|
|
27
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* Usually the inverse of `defines-on-call` (in the context of arguments and parameters).
|
|
32
|
+
* This may also link an open read (within a function) to the definition that is active at the call site.
|
|
33
|
+
*/
|
|
28
34
|
EdgeType[EdgeType["DefinedByOnCall"] = 32] = "DefinedByOnCall";
|
|
29
35
|
/** Formal used as argument to a function call */
|
|
30
36
|
EdgeType[EdgeType["Argument"] = 64] = "Argument";
|
|
@@ -89,13 +95,14 @@ function edgeIncludesType(type, types) {
|
|
|
89
95
|
function edgeDoesNotIncludeType(type, types) {
|
|
90
96
|
return (types & type) === 0;
|
|
91
97
|
}
|
|
92
|
-
const alwaysTraverseEdgeTypes = EdgeType.Reads | EdgeType.DefinedBy | EdgeType.Argument | EdgeType.Calls
|
|
98
|
+
const alwaysTraverseEdgeTypes = EdgeType.Reads | EdgeType.DefinedBy | EdgeType.Argument | EdgeType.Calls;
|
|
99
|
+
const definedByOnCallTypes = EdgeType.DefinesOnCall | EdgeType.DefinedByOnCall;
|
|
93
100
|
function shouldTraverseEdge(types) {
|
|
94
101
|
if (edgeIncludesType(types, alwaysTraverseEdgeTypes)) {
|
|
95
102
|
return 3 /* TraverseEdge.Always */;
|
|
96
103
|
}
|
|
97
|
-
else if (edgeIncludesType(types,
|
|
98
|
-
return 2 /* TraverseEdge.
|
|
104
|
+
else if (edgeIncludesType(types, definedByOnCallTypes)) {
|
|
105
|
+
return 2 /* TraverseEdge.OnlyIfBoth */;
|
|
99
106
|
}
|
|
100
107
|
else if (edgeIncludesType(types, EdgeType.SideEffectOnCall)) {
|
|
101
108
|
return 1 /* TraverseEdge.SideEffect */;
|