@eagleoutice/flowr 2.2.10 → 2.2.11
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 +4 -4
- package/cli/repl/server/compact.d.ts +2 -2
- package/cli/repl/server/compact.js +3 -3
- package/cli/repl/server/messages/message-analysis.d.ts +2 -2
- package/cli/repl/server/messages/message-analysis.js +2 -2
- package/config.d.ts +9 -0
- package/config.js +8 -2
- package/dataflow/environments/default-builtin-config.js +3 -1
- package/dataflow/environments/environment.js +4 -2
- package/dataflow/extractor.d.ts +1 -1
- package/dataflow/extractor.js +8 -6
- package/dataflow/graph/edge.js +4 -1
- package/dataflow/graph/graph.d.ts +6 -0
- package/dataflow/graph/graph.js +13 -0
- package/dataflow/internal/process/functions/call/argument/unpack-argument.d.ts +3 -0
- package/dataflow/internal/process/functions/call/argument/unpack-argument.js +4 -10
- package/dataflow/internal/process/functions/call/built-in/built-in-expression-list.js +4 -3
- package/dataflow/internal/process/functions/call/built-in/built-in-pipe.js +21 -3
- package/dataflow/internal/process/functions/call/built-in/built-in-source.js +4 -2
- package/dataflow/processor.d.ts +7 -7
- package/documentation/data/server/doc-data-server-messages.js +2 -2
- package/documentation/print-interface-wiki.js +3 -0
- package/documentation/print-query-wiki.js +20 -1
- package/package.json +3 -3
- package/queries/catalog/call-context-query/call-context-query-executor.js +13 -0
- package/queries/catalog/call-context-query/call-context-query-format.d.ts +4 -0
- package/queries/catalog/call-context-query/call-context-query-format.js +1 -0
- package/queries/catalog/location-map-query/location-map-query-executor.d.ts +1 -1
- package/queries/catalog/location-map-query/location-map-query-executor.js +38 -3
- package/queries/catalog/location-map-query/location-map-query-format.d.ts +10 -1
- package/queries/catalog/location-map-query/location-map-query-format.js +5 -1
- package/queries/catalog/project-query/project-query-executor.d.ts +3 -0
- package/queries/catalog/project-query/project-query-executor.js +17 -0
- package/queries/catalog/project-query/project-query-format.d.ts +67 -0
- package/queries/catalog/project-query/project-query-format.js +26 -0
- package/queries/query.d.ts +60 -1
- package/queries/query.js +3 -1
- package/slicing/static/fingerprint.js +8 -1
- package/slicing/static/slice-call.d.ts +1 -1
- package/slicing/static/slice-call.js +5 -16
- package/slicing/static/slicer-types.d.ts +2 -0
- package/slicing/static/static-slicer.d.ts +4 -2
- package/slicing/static/static-slicer.js +24 -18
- package/slicing/static/visiting-queue.d.ts +7 -1
- package/slicing/static/visiting-queue.js +20 -6
- package/util/version.js +1 -1
|
@@ -2,14 +2,49 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.executeLocationMapQuery = executeLocationMapQuery;
|
|
4
4
|
const log_1 = require("../../../util/log");
|
|
5
|
-
|
|
5
|
+
const fileIdRegex = /^(?<file>.*(\.[rR]))-/;
|
|
6
|
+
function fuzzyFindFile(node, idMap) {
|
|
7
|
+
if (node?.info.file) {
|
|
8
|
+
return node.info.file;
|
|
9
|
+
}
|
|
10
|
+
else if (node?.info.id) {
|
|
11
|
+
const file = fileIdRegex.exec(String(node.info.id));
|
|
12
|
+
if (file && file.groups?.file.trim()) {
|
|
13
|
+
return file.groups?.file.trim();
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
else if (node?.info.parent) {
|
|
17
|
+
const parent = idMap.get(node.info.parent);
|
|
18
|
+
if (parent) {
|
|
19
|
+
return fuzzyFindFile(parent, idMap);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return '<inline>';
|
|
23
|
+
}
|
|
24
|
+
function executeLocationMapQuery({ ast, dataflow: { graph } }, queries) {
|
|
6
25
|
if (queries.length !== 1) {
|
|
7
26
|
log_1.log.warn('Id-Map query expects only up to one query, but got', queries.length);
|
|
8
27
|
}
|
|
9
28
|
const start = Date.now();
|
|
10
|
-
const locationMap = {
|
|
29
|
+
const locationMap = {
|
|
30
|
+
files: {},
|
|
31
|
+
ids: {}
|
|
32
|
+
};
|
|
33
|
+
let count = 0;
|
|
34
|
+
const inverseMap = new Map();
|
|
35
|
+
for (const file of graph.sourced) {
|
|
36
|
+
locationMap.files[count] = file;
|
|
37
|
+
inverseMap.set(file, count);
|
|
38
|
+
count++;
|
|
39
|
+
}
|
|
11
40
|
for (const [id, node] of ast.idMap.entries()) {
|
|
12
|
-
|
|
41
|
+
if (node.location) {
|
|
42
|
+
const file = fuzzyFindFile(node, ast.idMap);
|
|
43
|
+
locationMap.ids[id] = [
|
|
44
|
+
inverseMap.get(file) ?? -1,
|
|
45
|
+
node.location
|
|
46
|
+
];
|
|
47
|
+
}
|
|
13
48
|
}
|
|
14
49
|
return {
|
|
15
50
|
'.meta': {
|
|
@@ -7,8 +7,17 @@ import type { SourceRange } from '../../../util/range';
|
|
|
7
7
|
export interface LocationMapQuery extends BaseQueryFormat {
|
|
8
8
|
readonly type: 'location-map';
|
|
9
9
|
}
|
|
10
|
+
export type FileId = number & {
|
|
11
|
+
readonly __fileId?: unique symbol;
|
|
12
|
+
};
|
|
13
|
+
export type FilePath = string & {
|
|
14
|
+
readonly __filePath?: unique symbol;
|
|
15
|
+
};
|
|
10
16
|
export interface LocationMapQueryResult extends BaseQueryResult {
|
|
11
|
-
readonly map:
|
|
17
|
+
readonly map: {
|
|
18
|
+
files: Record<FileId, FilePath>;
|
|
19
|
+
ids: Record<NodeId, [FileId, SourceRange]>;
|
|
20
|
+
};
|
|
12
21
|
}
|
|
13
22
|
export declare const LocationMapQueryDefinition: {
|
|
14
23
|
readonly executor: typeof executeLocationMapQuery;
|
|
@@ -14,7 +14,11 @@ exports.LocationMapQueryDefinition = {
|
|
|
14
14
|
asciiSummarizer: (formatter, _processed, queryResults, result) => {
|
|
15
15
|
const out = queryResults;
|
|
16
16
|
result.push(`Query: ${(0, ansi_1.bold)('location-map', formatter)} (${(0, time_1.printAsMs)(out['.meta'].timing, 0)})`);
|
|
17
|
-
result.push(
|
|
17
|
+
result.push(' ╰ File List:');
|
|
18
|
+
for (const [id, file] of Object.entries(out.map.files)) {
|
|
19
|
+
result.push(` ╰ ${id}: \`${file}\``);
|
|
20
|
+
}
|
|
21
|
+
result.push(` ╰ Id List: {${(0, query_print_1.summarizeIdsIfTooLong)(formatter, [...Object.keys(out.map.ids)])}}`);
|
|
18
22
|
return true;
|
|
19
23
|
},
|
|
20
24
|
schema: joi_1.default.object({
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.executeProjectQuery = executeProjectQuery;
|
|
4
|
+
const log_1 = require("../../../util/log");
|
|
5
|
+
function executeProjectQuery({ dataflow }, queries) {
|
|
6
|
+
if (queries.length !== 1) {
|
|
7
|
+
log_1.log.warn('Id-Map query expects only up to one query, but got', queries.length);
|
|
8
|
+
}
|
|
9
|
+
return {
|
|
10
|
+
'.meta': {
|
|
11
|
+
/* there is no sense in measuring a get */
|
|
12
|
+
timing: 0
|
|
13
|
+
},
|
|
14
|
+
files: dataflow.graph.sourced
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=project-query-executor.js.map
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { BaseQueryFormat, BaseQueryResult } from '../../base-query-format';
|
|
2
|
+
import { executeProjectQuery } from './project-query-executor';
|
|
3
|
+
import Joi from 'joi';
|
|
4
|
+
export interface ProjectQuery extends BaseQueryFormat {
|
|
5
|
+
readonly type: 'project';
|
|
6
|
+
}
|
|
7
|
+
export interface ProjectQueryResult extends BaseQueryResult {
|
|
8
|
+
readonly files: (string | '<inline>')[];
|
|
9
|
+
}
|
|
10
|
+
export declare const ProjectQueryDefinition: {
|
|
11
|
+
readonly executor: typeof executeProjectQuery;
|
|
12
|
+
readonly asciiSummarizer: (formatter: import("../../../util/ansi").OutputFormatter, _processed: import("../../../core/steps/pipeline/pipeline").PipelineOutput<import("../../../core/steps/pipeline/pipeline").Pipeline<{
|
|
13
|
+
readonly name: "parse";
|
|
14
|
+
readonly humanReadableName: "parse with R shell";
|
|
15
|
+
readonly description: "Parse the given R code into an AST";
|
|
16
|
+
readonly processor: (_results: unknown, input: Partial<import("../../../r-bridge/parser").ParseRequiredInput<string>>) => Promise<import("../../../r-bridge/parser").ParseStepOutput<string>>;
|
|
17
|
+
readonly executed: import("../../../core/steps/pipeline-step").PipelineStepStage.OncePerFile;
|
|
18
|
+
readonly printer: {
|
|
19
|
+
readonly 0: typeof import("../../../core/print/print").internalPrinter;
|
|
20
|
+
readonly 2: {
|
|
21
|
+
(value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string;
|
|
22
|
+
(value: any, replacer?: (number | string)[] | null, space?: string | number): string;
|
|
23
|
+
};
|
|
24
|
+
readonly 5: ({ parsed }: import("../../../r-bridge/parser").ParseStepOutput<string>, config: import("../../../util/quads").QuadSerializationConfiguration) => string;
|
|
25
|
+
};
|
|
26
|
+
readonly dependencies: readonly [];
|
|
27
|
+
readonly requiredInput: import("../../../r-bridge/parser").ParseRequiredInput<string>;
|
|
28
|
+
} | {
|
|
29
|
+
readonly name: "normalize";
|
|
30
|
+
readonly humanReadableName: "normalize";
|
|
31
|
+
readonly description: "Normalize the AST to flowR's AST";
|
|
32
|
+
readonly processor: (results: {
|
|
33
|
+
parse?: import("../../../r-bridge/parser").ParseStepOutput<string>;
|
|
34
|
+
}, input: Partial<import("../../../core/steps/all/core/10-normalize").NormalizeRequiredInput>) => import("../../../r-bridge/lang-4.x/ast/model/processing/decorate").NormalizedAst<import("../../../r-bridge/lang-4.x/ast/model/processing/decorate").ParentInformation, import("../../../r-bridge/lang-4.x/ast/model/model").RNode<import("../../../r-bridge/lang-4.x/ast/model/processing/decorate").ParentInformation>>;
|
|
35
|
+
readonly executed: import("../../../core/steps/pipeline-step").PipelineStepStage.OncePerFile;
|
|
36
|
+
readonly printer: {
|
|
37
|
+
readonly 0: typeof import("../../../core/print/print").internalPrinter;
|
|
38
|
+
readonly 2: typeof import("../../../core/print/normalize-printer").normalizedAstToJson;
|
|
39
|
+
readonly 5: typeof import("../../../core/print/normalize-printer").normalizedAstToQuads;
|
|
40
|
+
readonly 3: typeof import("../../../core/print/normalize-printer").printNormalizedAstToMermaid;
|
|
41
|
+
readonly 4: typeof import("../../../core/print/normalize-printer").printNormalizedAstToMermaidUrl;
|
|
42
|
+
};
|
|
43
|
+
readonly dependencies: readonly ["parse"];
|
|
44
|
+
readonly requiredInput: import("../../../core/steps/all/core/10-normalize").NormalizeRequiredInput;
|
|
45
|
+
} | {
|
|
46
|
+
readonly humanReadableName: "dataflow";
|
|
47
|
+
readonly processor: (results: {
|
|
48
|
+
normalize?: import("../../../r-bridge/lang-4.x/ast/model/processing/decorate").NormalizedAst;
|
|
49
|
+
}, input: {
|
|
50
|
+
request?: import("../../../r-bridge/retriever").RParseRequests;
|
|
51
|
+
parser?: import("../../../r-bridge/parser").Parser<import("../../../r-bridge/parser").KnownParserType>;
|
|
52
|
+
}) => import("../../../dataflow/info").DataflowInformation;
|
|
53
|
+
readonly requiredInput: {};
|
|
54
|
+
readonly name: "dataflow";
|
|
55
|
+
readonly description: "Construct the dataflow graph";
|
|
56
|
+
readonly executed: import("../../../core/steps/pipeline-step").PipelineStepStage.OncePerFile;
|
|
57
|
+
readonly printer: {
|
|
58
|
+
readonly 0: typeof import("../../../core/print/print").internalPrinter;
|
|
59
|
+
readonly 2: typeof import("../../../core/print/dataflow-printer").dataflowGraphToJson;
|
|
60
|
+
readonly 5: typeof import("../../../core/print/dataflow-printer").dataflowGraphToQuads;
|
|
61
|
+
readonly 3: typeof import("../../../core/print/dataflow-printer").dataflowGraphToMermaid;
|
|
62
|
+
readonly 4: typeof import("../../../core/print/dataflow-printer").dataflowGraphToMermaidUrl;
|
|
63
|
+
};
|
|
64
|
+
readonly dependencies: readonly ["normalize"];
|
|
65
|
+
}>>, queryResults: BaseQueryResult, result: string[]) => true;
|
|
66
|
+
readonly schema: Joi.ObjectSchema<any>;
|
|
67
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ProjectQueryDefinition = void 0;
|
|
7
|
+
const project_query_executor_1 = require("./project-query-executor");
|
|
8
|
+
const ansi_1 = require("../../../util/ansi");
|
|
9
|
+
const time_1 = require("../../../util/time");
|
|
10
|
+
const joi_1 = __importDefault(require("joi"));
|
|
11
|
+
exports.ProjectQueryDefinition = {
|
|
12
|
+
executor: project_query_executor_1.executeProjectQuery,
|
|
13
|
+
asciiSummarizer: (formatter, _processed, queryResults, result) => {
|
|
14
|
+
const out = queryResults;
|
|
15
|
+
result.push(`Query: ${(0, ansi_1.bold)('project', formatter)} (${(0, time_1.printAsMs)(out['.meta'].timing, 0)})`);
|
|
16
|
+
result.push(` ╰ Contains ${out.files.length} file${out.files.length === 1 ? '' : 's'}`);
|
|
17
|
+
for (const file of out.files) {
|
|
18
|
+
result.push(` ╰ \`${file}\``);
|
|
19
|
+
}
|
|
20
|
+
return true;
|
|
21
|
+
},
|
|
22
|
+
schema: joi_1.default.object({
|
|
23
|
+
type: joi_1.default.string().valid('project').required().description('The type of the query.'),
|
|
24
|
+
}).description('The project query provides information on the analyzed project.')
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=project-query-format.js.map
|
package/queries/query.d.ts
CHANGED
|
@@ -19,7 +19,8 @@ import type { SearchQuery } from './catalog/search-query/search-query-format';
|
|
|
19
19
|
import type { HappensBeforeQuery } from './catalog/happens-before-query/happens-before-query-format';
|
|
20
20
|
import type { ResolveValueQuery } from './catalog/resolve-value-query/resolve-value-query-format';
|
|
21
21
|
import type { DataflowLensQuery } from './catalog/dataflow-lens-query/dataflow-lens-query-format';
|
|
22
|
-
|
|
22
|
+
import type { ProjectQuery } from './catalog/project-query/project-query-format';
|
|
23
|
+
export type Query = CallContextQuery | ConfigQuery | SearchQuery | DataflowQuery | DataflowLensQuery | NormalizedAstQuery | IdMapQuery | DataflowClusterQuery | StaticSliceQuery | LineageQuery | DependenciesQuery | LocationMapQuery | HappensBeforeQuery | ResolveValueQuery | ProjectQuery;
|
|
23
24
|
export type QueryArgumentsWithType<QueryType extends BaseQueryFormat['type']> = Query & {
|
|
24
25
|
type: QueryType;
|
|
25
26
|
};
|
|
@@ -686,6 +687,64 @@ export declare const SupportedQueries: {
|
|
|
686
687
|
}>>, queryResults: BaseQueryResult, result: string[]) => true;
|
|
687
688
|
readonly schema: Joi.ObjectSchema<any>;
|
|
688
689
|
};
|
|
690
|
+
readonly project: {
|
|
691
|
+
readonly executor: typeof import("./catalog/project-query/project-query-executor").executeProjectQuery;
|
|
692
|
+
readonly asciiSummarizer: (formatter: OutputFormatter, _processed: PipelineOutput<import("../core/steps/pipeline/pipeline").Pipeline<{
|
|
693
|
+
readonly name: "parse";
|
|
694
|
+
readonly humanReadableName: "parse with R shell";
|
|
695
|
+
readonly description: "Parse the given R code into an AST";
|
|
696
|
+
readonly processor: (_results: unknown, input: Partial<import("../r-bridge/parser").ParseRequiredInput<string>>) => Promise<import("../r-bridge/parser").ParseStepOutput<string>>;
|
|
697
|
+
readonly executed: import("../core/steps/pipeline-step").PipelineStepStage.OncePerFile;
|
|
698
|
+
readonly printer: {
|
|
699
|
+
readonly 0: typeof import("../core/print/print").internalPrinter;
|
|
700
|
+
readonly 2: {
|
|
701
|
+
(value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string;
|
|
702
|
+
(value: any, replacer?: (number | string)[] | null, space?: string | number): string;
|
|
703
|
+
};
|
|
704
|
+
readonly 5: ({ parsed }: import("../r-bridge/parser").ParseStepOutput<string>, config: import("../util/quads").QuadSerializationConfiguration) => string;
|
|
705
|
+
};
|
|
706
|
+
readonly dependencies: readonly [];
|
|
707
|
+
readonly requiredInput: import("../r-bridge/parser").ParseRequiredInput<string>;
|
|
708
|
+
} | {
|
|
709
|
+
readonly name: "normalize";
|
|
710
|
+
readonly humanReadableName: "normalize";
|
|
711
|
+
readonly description: "Normalize the AST to flowR's AST";
|
|
712
|
+
readonly processor: (results: {
|
|
713
|
+
parse?: import("../r-bridge/parser").ParseStepOutput<string>;
|
|
714
|
+
}, input: Partial<import("../core/steps/all/core/10-normalize").NormalizeRequiredInput>) => import("../r-bridge/lang-4.x/ast/model/processing/decorate").NormalizedAst<import("../r-bridge/lang-4.x/ast/model/processing/decorate").ParentInformation, import("../r-bridge/lang-4.x/ast/model/model").RNode<import("../r-bridge/lang-4.x/ast/model/processing/decorate").ParentInformation>>;
|
|
715
|
+
readonly executed: import("../core/steps/pipeline-step").PipelineStepStage.OncePerFile;
|
|
716
|
+
readonly printer: {
|
|
717
|
+
readonly 0: typeof import("../core/print/print").internalPrinter;
|
|
718
|
+
readonly 2: typeof import("../core/print/normalize-printer").normalizedAstToJson;
|
|
719
|
+
readonly 5: typeof import("../core/print/normalize-printer").normalizedAstToQuads;
|
|
720
|
+
readonly 3: typeof import("../core/print/normalize-printer").printNormalizedAstToMermaid;
|
|
721
|
+
readonly 4: typeof import("../core/print/normalize-printer").printNormalizedAstToMermaidUrl;
|
|
722
|
+
};
|
|
723
|
+
readonly dependencies: readonly ["parse"];
|
|
724
|
+
readonly requiredInput: import("../core/steps/all/core/10-normalize").NormalizeRequiredInput;
|
|
725
|
+
} | {
|
|
726
|
+
readonly humanReadableName: "dataflow";
|
|
727
|
+
readonly processor: (results: {
|
|
728
|
+
normalize?: import("../r-bridge/lang-4.x/ast/model/processing/decorate").NormalizedAst;
|
|
729
|
+
}, input: {
|
|
730
|
+
request?: import("../r-bridge/retriever").RParseRequests;
|
|
731
|
+
parser?: import("../r-bridge/parser").Parser<import("../r-bridge/parser").KnownParserType>;
|
|
732
|
+
}) => import("../dataflow/info").DataflowInformation;
|
|
733
|
+
readonly requiredInput: {};
|
|
734
|
+
readonly name: "dataflow";
|
|
735
|
+
readonly description: "Construct the dataflow graph";
|
|
736
|
+
readonly executed: import("../core/steps/pipeline-step").PipelineStepStage.OncePerFile;
|
|
737
|
+
readonly printer: {
|
|
738
|
+
readonly 0: typeof import("../core/print/print").internalPrinter;
|
|
739
|
+
readonly 2: typeof import("../core/print/dataflow-printer").dataflowGraphToJson;
|
|
740
|
+
readonly 5: typeof import("../core/print/dataflow-printer").dataflowGraphToQuads;
|
|
741
|
+
readonly 3: typeof import("../core/print/dataflow-printer").dataflowGraphToMermaid;
|
|
742
|
+
readonly 4: typeof import("../core/print/dataflow-printer").dataflowGraphToMermaidUrl;
|
|
743
|
+
};
|
|
744
|
+
readonly dependencies: readonly ["normalize"];
|
|
745
|
+
}>>, queryResults: BaseQueryResult, result: string[]) => true;
|
|
746
|
+
readonly schema: Joi.ObjectSchema<any>;
|
|
747
|
+
};
|
|
689
748
|
};
|
|
690
749
|
export type SupportedQueryTypes = keyof typeof SupportedQueries;
|
|
691
750
|
export type QueryResult<Type extends Query['type']> = ReturnType<typeof SupportedQueries[Type]['executor']>;
|
package/queries/query.js
CHANGED
|
@@ -27,6 +27,7 @@ const search_query_format_1 = require("./catalog/search-query/search-query-forma
|
|
|
27
27
|
const happens_before_query_format_1 = require("./catalog/happens-before-query/happens-before-query-format");
|
|
28
28
|
const resolve_value_query_format_1 = require("./catalog/resolve-value-query/resolve-value-query-format");
|
|
29
29
|
const dataflow_lens_query_format_1 = require("./catalog/dataflow-lens-query/dataflow-lens-query-format");
|
|
30
|
+
const project_query_format_1 = require("./catalog/project-query/project-query-format");
|
|
30
31
|
exports.SupportedQueries = {
|
|
31
32
|
'call-context': call_context_query_format_1.CallContextQueryDefinition,
|
|
32
33
|
'config': config_query_format_1.ConfigQueryDefinition,
|
|
@@ -41,7 +42,8 @@ exports.SupportedQueries = {
|
|
|
41
42
|
'location-map': location_map_query_format_1.LocationMapQueryDefinition,
|
|
42
43
|
'search': search_query_format_1.SearchQueryDefinition,
|
|
43
44
|
'happens-before': happens_before_query_format_1.HappensBeforeQueryDefinition,
|
|
44
|
-
'resolve-value': resolve_value_query_format_1.ResolveValueQueryDefinition
|
|
45
|
+
'resolve-value': resolve_value_query_format_1.ResolveValueQueryDefinition,
|
|
46
|
+
'project': project_query_format_1.ProjectQueryDefinition
|
|
45
47
|
};
|
|
46
48
|
function executeQueriesOfSameType(data, ...queries) {
|
|
47
49
|
(0, assert_1.guard)(queries.length > 0, 'At least one query must be provided');
|
|
@@ -9,7 +9,14 @@ const object_hash_1 = __importDefault(require("object-hash"));
|
|
|
9
9
|
const environment_1 = require("../../dataflow/environments/environment");
|
|
10
10
|
const built_in_1 = require("../../dataflow/environments/built-in");
|
|
11
11
|
function envFingerprint(env) {
|
|
12
|
-
return (0, object_hash_1.default)(env, {
|
|
12
|
+
return (0, object_hash_1.default)(env, {
|
|
13
|
+
algorithm: 'md5',
|
|
14
|
+
excludeKeys: key => key === 'id' || key === 'value',
|
|
15
|
+
respectFunctionProperties: false,
|
|
16
|
+
respectFunctionNames: false,
|
|
17
|
+
ignoreUnknown: true,
|
|
18
|
+
replacer: (v) => (v === environment_1.BuiltInEnvironment || v === built_in_1.EmptyBuiltInMemory) ? undefined : v
|
|
19
|
+
});
|
|
13
20
|
}
|
|
14
21
|
function fingerprint(id, envFingerprint, onlyForSideEffects) {
|
|
15
22
|
return `${id}-${envFingerprint}-${onlyForSideEffects ? '0' : '1'}`;
|
|
@@ -8,7 +8,7 @@ import type { NodeId } from '../../r-bridge/lang-4.x/ast/model/processing/node-i
|
|
|
8
8
|
/**
|
|
9
9
|
* Returns the function call targets (definitions) by the given caller
|
|
10
10
|
*/
|
|
11
|
-
export declare function getAllFunctionCallTargets(dataflowGraph: DataflowGraph, callerInfo: DataflowGraphVertexFunctionCall, baseEnvironment: REnvironmentInformation): [Set<DataflowGraphVertexInfo>, REnvironmentInformation];
|
|
11
|
+
export declare function getAllFunctionCallTargets(dataflowGraph: DataflowGraph, callerInfo: DataflowGraphVertexFunctionCall, baseEnvironment: REnvironmentInformation, queue: VisitingQueue): [Set<DataflowGraphVertexInfo>, REnvironmentInformation];
|
|
12
12
|
/** returns the new threshold hit count */
|
|
13
13
|
export declare function sliceForCall(current: NodeToSlice, callerInfo: DataflowGraphVertexFunctionCall, dataflowGraph: DataflowGraph, queue: VisitingQueue): void;
|
|
14
14
|
/** Returns true if we found at least one return edge */
|
|
@@ -16,7 +16,7 @@ const static_slicer_1 = require("./static-slicer");
|
|
|
16
16
|
/**
|
|
17
17
|
* Returns the function call targets (definitions) by the given caller
|
|
18
18
|
*/
|
|
19
|
-
function getAllFunctionCallTargets(dataflowGraph, callerInfo, baseEnvironment) {
|
|
19
|
+
function getAllFunctionCallTargets(dataflowGraph, callerInfo, baseEnvironment, queue) {
|
|
20
20
|
// bind with call-local environments during slicing
|
|
21
21
|
const outgoingEdges = dataflowGraph.get(callerInfo.id, true);
|
|
22
22
|
(0, assert_1.guard)(outgoingEdges !== undefined, () => `outgoing edges of id: ${callerInfo.id} must be in graph but can not be found, keep in slice to be sure`);
|
|
@@ -30,7 +30,7 @@ function getAllFunctionCallTargets(dataflowGraph, callerInfo, baseEnvironment) {
|
|
|
30
30
|
functionCallDefs.push(target);
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
-
const functionCallTargets = (0, linker_1.getAllLinkedFunctionDefinitions)(new Set(functionCallDefs), dataflowGraph);
|
|
33
|
+
const functionCallTargets = queue.memoizeCallTargets(functionCallDefs.join(';'), () => (0, linker_1.getAllLinkedFunctionDefinitions)(new Set(functionCallDefs), dataflowGraph));
|
|
34
34
|
return [functionCallTargets, activeEnvironment];
|
|
35
35
|
}
|
|
36
36
|
function includeArgumentFunctionCallClosure(arg, baseEnvironment, activeEnvironment, queue, dataflowGraph) {
|
|
@@ -38,22 +38,11 @@ function includeArgumentFunctionCallClosure(arg, baseEnvironment, activeEnvironm
|
|
|
38
38
|
if (!valueRoot) {
|
|
39
39
|
return;
|
|
40
40
|
}
|
|
41
|
-
const callTargets = (0, linker_1.getAllLinkedFunctionDefinitions)(new Set([valueRoot]), dataflowGraph);
|
|
41
|
+
const callTargets = queue.memoizeCallTargets(valueRoot, () => (0, linker_1.getAllLinkedFunctionDefinitions)(new Set([valueRoot]), dataflowGraph));
|
|
42
42
|
linkCallTargets(false, callTargets, activeEnvironment, (0, fingerprint_1.envFingerprint)(activeEnvironment), queue);
|
|
43
43
|
}
|
|
44
44
|
function linkCallTargets(onlyForSideEffects, functionCallTargets, activeEnvironment, activeEnvironmentFingerprint, queue) {
|
|
45
45
|
for (const functionCallTarget of functionCallTargets) {
|
|
46
|
-
// all those linked within the scopes of other functions are already linked when exiting a function definition
|
|
47
|
-
/* for(const openIn of (functionCallTarget as DataflowGraphVertexFunctionDefinition).subflow.in) {
|
|
48
|
-
// only if the outgoing path does not already have a defined by linkage
|
|
49
|
-
const defs = openIn.name ? resolveByName(openIn.name, activeEnvironment, openIn.type) : undefined;
|
|
50
|
-
if(defs === undefined) {
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
for(const def of defs.filter(d => d.nodeId !== BuiltIn)) {
|
|
54
|
-
queue.add(def.nodeId, baseEnvironment, baseEnvPrint, onlyForSideEffects);
|
|
55
|
-
}
|
|
56
|
-
}*/
|
|
57
46
|
for (const exitPoint of functionCallTarget.exitPoints) {
|
|
58
47
|
queue.add(exitPoint, activeEnvironment, activeEnvironmentFingerprint, onlyForSideEffects);
|
|
59
48
|
}
|
|
@@ -62,7 +51,7 @@ function linkCallTargets(onlyForSideEffects, functionCallTargets, activeEnvironm
|
|
|
62
51
|
/** returns the new threshold hit count */
|
|
63
52
|
function sliceForCall(current, callerInfo, dataflowGraph, queue) {
|
|
64
53
|
const baseEnvironment = current.baseEnvironment;
|
|
65
|
-
const [functionCallTargets, activeEnvironment] = getAllFunctionCallTargets(dataflowGraph, callerInfo, current.baseEnvironment);
|
|
54
|
+
const [functionCallTargets, activeEnvironment] = getAllFunctionCallTargets(dataflowGraph, callerInfo, current.baseEnvironment, queue);
|
|
66
55
|
const activeEnvironmentFingerprint = (0, fingerprint_1.envFingerprint)(activeEnvironment);
|
|
67
56
|
if (functionCallTargets.size === 0) {
|
|
68
57
|
/*
|
|
@@ -91,7 +80,7 @@ function handleReturns(from, queue, currentEdges, baseEnvFingerprint, baseEnviro
|
|
|
91
80
|
queue.add(target, baseEnvironment, baseEnvFingerprint, false);
|
|
92
81
|
}
|
|
93
82
|
else if ((0, edge_1.edgeIncludesType)(edge.types, edge_1.EdgeType.DefinesOnCall | edge_1.EdgeType.DefinedByOnCall | edge_1.EdgeType.Argument)) {
|
|
94
|
-
(0, static_slicer_1.updatePotentialAddition)(queue, from, target, baseEnvironment);
|
|
83
|
+
(0, static_slicer_1.updatePotentialAddition)(queue, from, target, baseEnvironment, baseEnvFingerprint);
|
|
95
84
|
}
|
|
96
85
|
}
|
|
97
86
|
return true;
|
|
@@ -9,6 +9,8 @@ export interface NodeToSlice {
|
|
|
9
9
|
readonly id: NodeId;
|
|
10
10
|
/** used for calling context, etc. */
|
|
11
11
|
readonly baseEnvironment: REnvironmentInformation;
|
|
12
|
+
/** the fingerprint of the environment */
|
|
13
|
+
readonly envFingerprint: string;
|
|
12
14
|
/** if we add a function call, we may need it only for its side effects (e.g., a redefinition of a global variable), if so, 'returns' links will not be traced */
|
|
13
15
|
readonly onlyForSideEffects: boolean;
|
|
14
16
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { SliceResult } from './slicer-types';
|
|
2
|
+
import type { Fingerprint } from './fingerprint';
|
|
2
3
|
import { VisitingQueue } from './visiting-queue';
|
|
3
4
|
import type { DataflowGraph } from '../../dataflow/graph/graph';
|
|
4
5
|
import type { NormalizedAst } from '../../r-bridge/lang-4.x/ast/model/processing/decorate';
|
|
@@ -15,6 +16,7 @@ export declare const slicerLogger: import("tslog").Logger<import("tslog").ILogOb
|
|
|
15
16
|
* @param ast - The normalized AST of the code (used to get static nesting information of the lexemes in case of control flow dependencies that may have no effect on the slicing scope).
|
|
16
17
|
* @param criteria - The criterias to slice on.
|
|
17
18
|
* @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.
|
|
19
|
+
* @param cache - A cache to store the results of the slice. If provided, the slice may use this cache to speed up the slicing process.
|
|
18
20
|
*/
|
|
19
|
-
export declare function staticSlicing(graph: DataflowGraph, { idMap }: NormalizedAst, criteria: SlicingCriteria, threshold?: number): Readonly<SliceResult>;
|
|
20
|
-
export declare function updatePotentialAddition(queue: VisitingQueue, id: NodeId, target: NodeId, baseEnvironment: REnvironmentInformation): void;
|
|
21
|
+
export declare function staticSlicing(graph: DataflowGraph, { idMap }: NormalizedAst, criteria: SlicingCriteria, threshold?: number, cache?: Map<Fingerprint, Set<NodeId>>): Readonly<SliceResult>;
|
|
22
|
+
export declare function updatePotentialAddition(queue: VisitingQueue, id: NodeId, target: NodeId, baseEnvironment: REnvironmentInformation, envFingerprint: string): void;
|
|
@@ -12,6 +12,7 @@ const parse_1 = require("../criterion/parse");
|
|
|
12
12
|
const environment_1 = require("../../dataflow/environments/environment");
|
|
13
13
|
const vertex_1 = require("../../dataflow/graph/vertex");
|
|
14
14
|
const edge_1 = require("../../dataflow/graph/edge");
|
|
15
|
+
const config_1 = require("../../config");
|
|
15
16
|
exports.slicerLogger = log_1.log.getSubLogger({ name: 'slicer' });
|
|
16
17
|
/**
|
|
17
18
|
* This returns the ids to include in the static backward slice, when slicing with the given seed id's (must be at least one).
|
|
@@ -22,12 +23,13 @@ exports.slicerLogger = log_1.log.getSubLogger({ name: 'slicer' });
|
|
|
22
23
|
* @param ast - The normalized AST of the code (used to get static nesting information of the lexemes in case of control flow dependencies that may have no effect on the slicing scope).
|
|
23
24
|
* @param criteria - The criterias to slice on.
|
|
24
25
|
* @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.
|
|
26
|
+
* @param cache - A cache to store the results of the slice. If provided, the slice may use this cache to speed up the slicing process.
|
|
25
27
|
*/
|
|
26
|
-
function staticSlicing(graph, { idMap }, criteria, threshold = 75) {
|
|
28
|
+
function staticSlicing(graph, { idMap }, criteria, threshold = (0, config_1.getConfig)().solver.slicer?.threshold ?? 75, cache) {
|
|
27
29
|
(0, assert_1.guard)(criteria.length > 0, 'must have at least one seed id to calculate slice');
|
|
28
30
|
const decodedCriteria = (0, parse_1.convertAllSlicingCriteriaToIds)(criteria, idMap);
|
|
29
31
|
(0, log_1.expensiveTrace)(exports.slicerLogger, () => `calculating slice for ${decodedCriteria.length} seed criteria: ${decodedCriteria.map(s => JSON.stringify(s)).join(', ')}`);
|
|
30
|
-
const queue = new visiting_queue_1.VisitingQueue(threshold);
|
|
32
|
+
const queue = new visiting_queue_1.VisitingQueue(threshold, cache);
|
|
31
33
|
let minNesting = Number.MAX_SAFE_INTEGER;
|
|
32
34
|
const sliceSeedIds = new Set();
|
|
33
35
|
// every node ships the call environment which registers the calling environment
|
|
@@ -44,15 +46,15 @@ function staticSlicing(graph, { idMap }, criteria, threshold = 75) {
|
|
|
44
46
|
* include all the implicit side effects that we have to consider as we are unable to narrow them down
|
|
45
47
|
*/
|
|
46
48
|
for (const id of graph.unknownSideEffects) {
|
|
47
|
-
if (typeof id !== 'object') {
|
|
49
|
+
if (typeof id !== 'object') {
|
|
50
|
+
/* otherwise, their target is just missing */
|
|
48
51
|
queue.add(id, emptyEnv, basePrint, true);
|
|
49
52
|
}
|
|
50
53
|
}
|
|
51
54
|
}
|
|
52
55
|
while (queue.nonEmpty()) {
|
|
53
56
|
const current = queue.next();
|
|
54
|
-
const { baseEnvironment, id, onlyForSideEffects } = current;
|
|
55
|
-
const baseEnvFingerprint = (0, fingerprint_1.envFingerprint)(baseEnvironment);
|
|
57
|
+
const { baseEnvironment, id, onlyForSideEffects, envFingerprint: baseEnvFingerprint } = current;
|
|
56
58
|
const currentInfo = graph.get(id, true);
|
|
57
59
|
if (currentInfo === undefined) {
|
|
58
60
|
exports.slicerLogger.warn(`id: ${id} must be in graph but can not be found, keep in slice to be sure`);
|
|
@@ -78,29 +80,32 @@ function staticSlicing(graph, { idMap }, criteria, threshold = 75) {
|
|
|
78
80
|
}
|
|
79
81
|
}
|
|
80
82
|
for (const [target, { types }] of currentEdges) {
|
|
81
|
-
if ((0, edge_1.edgeIncludesType)(types, edge_1.EdgeType.NonStandardEvaluation)) {
|
|
82
|
-
continue;
|
|
83
|
-
}
|
|
84
83
|
const t = (0, edge_1.shouldTraverseEdge)(types);
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
84
|
+
switch (t) {
|
|
85
|
+
case 0 /* TraverseEdge.Never */:
|
|
86
|
+
continue;
|
|
87
|
+
case 3 /* TraverseEdge.Always */:
|
|
88
|
+
queue.add(target, baseEnvironment, baseEnvFingerprint, false);
|
|
89
|
+
continue;
|
|
90
|
+
case 2 /* TraverseEdge.OnlyIfBoth */:
|
|
91
|
+
updatePotentialAddition(queue, id, target, baseEnvironment, baseEnvFingerprint);
|
|
92
|
+
continue;
|
|
93
|
+
case 1 /* TraverseEdge.SideEffect */:
|
|
94
|
+
queue.add(target, baseEnvironment, baseEnvFingerprint, true);
|
|
95
|
+
continue;
|
|
96
|
+
default:
|
|
97
|
+
(0, assert_1.assertUnreachable)(t);
|
|
93
98
|
}
|
|
94
99
|
}
|
|
95
100
|
}
|
|
96
101
|
return { ...queue.status(), decodedCriteria };
|
|
97
102
|
}
|
|
98
|
-
function updatePotentialAddition(queue, id, target, baseEnvironment) {
|
|
103
|
+
function updatePotentialAddition(queue, id, target, baseEnvironment, envFingerprint) {
|
|
99
104
|
const n = queue.potentialAdditions.get(target);
|
|
100
105
|
if (n) {
|
|
101
106
|
const [addedBy, { baseEnvironment, onlyForSideEffects }] = n;
|
|
102
107
|
if (addedBy !== id) {
|
|
103
|
-
queue.add(target, baseEnvironment,
|
|
108
|
+
queue.add(target, baseEnvironment, envFingerprint, onlyForSideEffects);
|
|
104
109
|
queue.potentialAdditions.delete(target);
|
|
105
110
|
}
|
|
106
111
|
}
|
|
@@ -108,6 +113,7 @@ function updatePotentialAddition(queue, id, target, baseEnvironment) {
|
|
|
108
113
|
queue.potentialAdditions.set(target, [id, {
|
|
109
114
|
id: target,
|
|
110
115
|
baseEnvironment,
|
|
116
|
+
envFingerprint,
|
|
111
117
|
onlyForSideEffects: false
|
|
112
118
|
}]);
|
|
113
119
|
}
|
|
@@ -1,14 +1,19 @@
|
|
|
1
|
+
import type { Fingerprint } from './fingerprint';
|
|
1
2
|
import type { NodeToSlice, SliceResult } from './slicer-types';
|
|
2
3
|
import type { REnvironmentInformation } from '../../dataflow/environments/environment';
|
|
3
4
|
import type { NodeId } from '../../r-bridge/lang-4.x/ast/model/processing/node-id';
|
|
5
|
+
import type { DataflowGraphVertexInfo } from '../../dataflow/graph/vertex';
|
|
4
6
|
export declare class VisitingQueue {
|
|
5
7
|
private readonly threshold;
|
|
6
8
|
private timesHitThreshold;
|
|
7
9
|
private readonly seen;
|
|
10
|
+
private readonly seenByCache;
|
|
8
11
|
private readonly idThreshold;
|
|
9
12
|
private readonly queue;
|
|
13
|
+
private readonly cache?;
|
|
10
14
|
potentialAdditions: Map<NodeId, [NodeId, NodeToSlice]>;
|
|
11
|
-
|
|
15
|
+
private cachedCallTargets;
|
|
16
|
+
constructor(threshold: number, cache?: Map<Fingerprint, Set<NodeId>>);
|
|
12
17
|
/**
|
|
13
18
|
* Adds a node to the queue if it has not been seen before.
|
|
14
19
|
* @param target - the node to add
|
|
@@ -20,5 +25,6 @@ export declare class VisitingQueue {
|
|
|
20
25
|
next(): NodeToSlice;
|
|
21
26
|
nonEmpty(): boolean;
|
|
22
27
|
hasId(id: NodeId): boolean;
|
|
28
|
+
memoizeCallTargets(id: NodeId, targets: () => Set<DataflowGraphVertexInfo>): Set<DataflowGraphVertexInfo>;
|
|
23
29
|
status(): Readonly<Pick<SliceResult, 'timesHitThreshold' | 'result'>>;
|
|
24
30
|
}
|
|
@@ -2,19 +2,21 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.VisitingQueue = void 0;
|
|
4
4
|
const fingerprint_1 = require("./fingerprint");
|
|
5
|
-
const static_slicer_1 = require("./static-slicer");
|
|
6
|
-
const assert_1 = require("../../util/assert");
|
|
7
5
|
class VisitingQueue {
|
|
8
6
|
threshold;
|
|
9
7
|
timesHitThreshold = 0;
|
|
10
8
|
seen = new Map();
|
|
9
|
+
seenByCache = new Set();
|
|
11
10
|
idThreshold = new Map();
|
|
12
11
|
queue = [];
|
|
12
|
+
cache = new Map();
|
|
13
13
|
// the set of potential additions holds nodes which may be added if a second edge deems them relevant (e.g., found with the `defined-by-on-call` edge)
|
|
14
14
|
// additionally it holds which node id added the addition so we can separate their inclusion on the structure
|
|
15
15
|
potentialAdditions = new Map();
|
|
16
|
-
|
|
16
|
+
cachedCallTargets = new Map();
|
|
17
|
+
constructor(threshold, cache) {
|
|
17
18
|
this.threshold = threshold;
|
|
19
|
+
this.cache = cache;
|
|
18
20
|
}
|
|
19
21
|
/**
|
|
20
22
|
* Adds a node to the queue if it has not been seen before.
|
|
@@ -26,16 +28,22 @@ class VisitingQueue {
|
|
|
26
28
|
add(target, env, envFingerprint, onlyForSideEffects) {
|
|
27
29
|
const idCounter = this.idThreshold.get(target) ?? 0;
|
|
28
30
|
if (idCounter > this.threshold) {
|
|
29
|
-
static_slicer_1.slicerLogger.warn(`id: ${target} has been visited ${idCounter} times, skipping`);
|
|
30
31
|
this.timesHitThreshold++;
|
|
31
32
|
return;
|
|
32
33
|
}
|
|
33
34
|
/* we do not include the in call part in the fingerprint as it is 'deterministic' from the source position */
|
|
34
35
|
const print = (0, fingerprint_1.fingerprint)(target, envFingerprint, onlyForSideEffects);
|
|
35
36
|
if (!this.seen.has(print)) {
|
|
37
|
+
const cached = this.cache?.get(print);
|
|
38
|
+
if (cached) {
|
|
39
|
+
this.seenByCache.add(target);
|
|
40
|
+
for (const id of cached) {
|
|
41
|
+
this.queue.push({ id, baseEnvironment: env, envFingerprint, onlyForSideEffects });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
36
44
|
this.idThreshold.set(target, idCounter + 1);
|
|
37
45
|
this.seen.set(print, target);
|
|
38
|
-
this.queue.push({ id: target, baseEnvironment: env, onlyForSideEffects });
|
|
46
|
+
this.queue.push({ id: target, baseEnvironment: env, envFingerprint, onlyForSideEffects });
|
|
39
47
|
}
|
|
40
48
|
}
|
|
41
49
|
next() {
|
|
@@ -47,10 +55,16 @@ class VisitingQueue {
|
|
|
47
55
|
hasId(id) {
|
|
48
56
|
return this.idThreshold.has(id);
|
|
49
57
|
}
|
|
58
|
+
memoizeCallTargets(id, targets) {
|
|
59
|
+
if (!this.cachedCallTargets.has(id)) {
|
|
60
|
+
this.cachedCallTargets.set(id, targets());
|
|
61
|
+
}
|
|
62
|
+
return this.cachedCallTargets.get(id);
|
|
63
|
+
}
|
|
50
64
|
status() {
|
|
51
65
|
return {
|
|
52
66
|
timesHitThreshold: this.timesHitThreshold,
|
|
53
|
-
result: new Set([...this.seen.values()]
|
|
67
|
+
result: new Set([...this.seen.values(), ...this.seenByCache])
|
|
54
68
|
};
|
|
55
69
|
}
|
|
56
70
|
}
|
package/util/version.js
CHANGED
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.flowrVersion = flowrVersion;
|
|
4
4
|
const semver_1 = require("semver");
|
|
5
5
|
// this is automatically replaced with the current version by release-it
|
|
6
|
-
const version = '2.2.
|
|
6
|
+
const version = '2.2.11';
|
|
7
7
|
function flowrVersion() {
|
|
8
8
|
return new semver_1.SemVer(version);
|
|
9
9
|
}
|