@eagleoutice/flowr 2.2.0 → 2.2.2
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/cli/flowr.js +2 -1
- package/cli/repl/commands/repl-cfg.js +30 -7
- package/cli/repl/commands/repl-dataflow.js +22 -2
- package/cli/repl/commands/repl-normalize.js +22 -2
- package/cli/repl/commands/repl-parse.js +8 -2
- package/cli/repl/core.js +4 -0
- package/cli/repl/server/connection.js +1 -1
- package/cli/script-core/statistics-helper-core.js +1 -1
- package/config.js +8 -1
- package/dataflow/environments/resolve-by-name.js +5 -4
- package/dataflow/extractor.js +1 -1
- package/dataflow/graph/dataflowgraph-builder.d.ts +11 -10
- package/dataflow/graph/dataflowgraph-builder.js +11 -10
- package/dataflow/graph/edge.d.ts +1 -1
- package/dataflow/graph/edge.js +2 -2
- package/dataflow/graph/vertex.d.ts +6 -6
- package/dataflow/graph/vertex.js +5 -5
- package/dataflow/internal/process/functions/call/built-in/built-in-access.js +9 -5
- package/dataflow/internal/process/functions/call/built-in/built-in-source.js +2 -2
- package/documentation/doc-util/doc-cfg.js +2 -2
- package/documentation/doc-util/doc-types.js +3 -3
- package/documentation/print-interface-wiki.js +0 -2
- package/documentation/print-query-wiki.js +54 -0
- package/package.json +8 -6
- package/queries/catalog/call-context-query/call-context-query-executor.js +1 -1
- package/queries/catalog/happens-before-query/happens-before-query-executor.d.ts +3 -0
- package/queries/catalog/happens-before-query/happens-before-query-executor.js +36 -0
- package/queries/catalog/happens-before-query/happens-before-query-format.d.ts +71 -0
- package/queries/catalog/happens-before-query/happens-before-query-format.js +27 -0
- package/queries/catalog/resolve-value-query/resolve-value-query-executor.d.ts +4 -0
- package/queries/catalog/resolve-value-query/resolve-value-query-executor.js +34 -0
- package/queries/catalog/resolve-value-query/resolve-value-query-format.d.ts +72 -0
- package/queries/catalog/resolve-value-query/resolve-value-query-format.js +49 -0
- package/queries/catalog/search-query/search-query-executor.js +0 -4
- package/queries/query.d.ts +119 -1
- package/queries/query.js +5 -1
- package/r-bridge/data/data.d.ts +2 -2
- package/r-bridge/data/data.js +2 -2
- package/r-bridge/lang-4.x/tree-sitter/tree-sitter-normalize.js +46 -29
- package/r-bridge/lang-4.x/tree-sitter/tree-sitter-types.d.ts +4 -1
- package/r-bridge/lang-4.x/tree-sitter/tree-sitter-types.js +3 -0
- package/util/cfg/cfg.d.ts +12 -1
- package/util/cfg/cfg.js +64 -10
- package/util/cfg/happens-before.d.ts +7 -0
- package/util/cfg/happens-before.js +32 -0
- package/util/logic.d.ts +4 -4
- package/util/logic.js +8 -0
- package/util/range.d.ts +21 -0
- package/util/range.js +3 -0
- package/util/version.js +1 -1
package/queries/query.d.ts
CHANGED
|
@@ -16,7 +16,9 @@ import Joi from 'joi';
|
|
|
16
16
|
import type { LocationMapQuery } from './catalog/location-map-query/location-map-query-format';
|
|
17
17
|
import type { ConfigQuery } from './catalog/config-query/config-query-format';
|
|
18
18
|
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
|
+
import type { ResolveValueQuery } from './catalog/resolve-value-query/resolve-value-query-format';
|
|
21
|
+
export type Query = CallContextQuery | ConfigQuery | SearchQuery | DataflowQuery | NormalizedAstQuery | IdMapQuery | DataflowClusterQuery | StaticSliceQuery | LineageQuery | DependenciesQuery | LocationMapQuery | HappensBeforeQuery | ResolveValueQuery;
|
|
20
22
|
export type QueryArgumentsWithType<QueryType extends BaseQueryFormat['type']> = Query & {
|
|
21
23
|
type: QueryType;
|
|
22
24
|
};
|
|
@@ -509,6 +511,122 @@ export declare const SupportedQueries: {
|
|
|
509
511
|
}>>, queryResults: BaseQueryResult, result: string[]) => true;
|
|
510
512
|
readonly schema: Joi.ObjectSchema<any>;
|
|
511
513
|
};
|
|
514
|
+
readonly 'happens-before': {
|
|
515
|
+
readonly executor: typeof import("./catalog/happens-before-query/happens-before-query-executor").executeHappensBefore;
|
|
516
|
+
readonly asciiSummarizer: (formatter: OutputFormatter, _processed: PipelineOutput<import("../core/steps/pipeline/pipeline").Pipeline<{
|
|
517
|
+
readonly name: "parse";
|
|
518
|
+
readonly humanReadableName: "parse with R shell";
|
|
519
|
+
readonly description: "Parse the given R code into an AST";
|
|
520
|
+
readonly processor: (_results: unknown, input: Partial<import("../r-bridge/parser").ParseRequiredInput<string>>) => Promise<import("../r-bridge/parser").ParseStepOutput<string>>;
|
|
521
|
+
readonly executed: import("../core/steps/pipeline-step").PipelineStepStage.OncePerFile;
|
|
522
|
+
readonly printer: {
|
|
523
|
+
readonly 0: typeof import("../core/print/print").internalPrinter;
|
|
524
|
+
readonly 2: {
|
|
525
|
+
(value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string;
|
|
526
|
+
(value: any, replacer?: (number | string)[] | null, space?: string | number): string;
|
|
527
|
+
};
|
|
528
|
+
readonly 5: ({ parsed }: import("../r-bridge/parser").ParseStepOutput<string>, config: import("../util/quads").QuadSerializationConfiguration) => string;
|
|
529
|
+
};
|
|
530
|
+
readonly dependencies: readonly [];
|
|
531
|
+
readonly requiredInput: import("../r-bridge/parser").ParseRequiredInput<string>;
|
|
532
|
+
} | {
|
|
533
|
+
readonly name: "normalize";
|
|
534
|
+
readonly humanReadableName: "normalize";
|
|
535
|
+
readonly description: "Normalize the AST to flowR's AST";
|
|
536
|
+
readonly processor: (results: {
|
|
537
|
+
parse?: import("../r-bridge/parser").ParseStepOutput<string>;
|
|
538
|
+
}, 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>>;
|
|
539
|
+
readonly executed: import("../core/steps/pipeline-step").PipelineStepStage.OncePerFile;
|
|
540
|
+
readonly printer: {
|
|
541
|
+
readonly 0: typeof import("../core/print/print").internalPrinter;
|
|
542
|
+
readonly 2: typeof import("../core/print/normalize-printer").normalizedAstToJson;
|
|
543
|
+
readonly 5: typeof import("../core/print/normalize-printer").normalizedAstToQuads;
|
|
544
|
+
readonly 3: typeof import("../core/print/normalize-printer").printNormalizedAstToMermaid;
|
|
545
|
+
readonly 4: typeof import("../core/print/normalize-printer").printNormalizedAstToMermaidUrl;
|
|
546
|
+
};
|
|
547
|
+
readonly dependencies: readonly ["parse"];
|
|
548
|
+
readonly requiredInput: import("../core/steps/all/core/10-normalize").NormalizeRequiredInput;
|
|
549
|
+
} | {
|
|
550
|
+
readonly humanReadableName: "dataflow";
|
|
551
|
+
readonly processor: (results: {
|
|
552
|
+
normalize?: import("../r-bridge/lang-4.x/ast/model/processing/decorate").NormalizedAst;
|
|
553
|
+
}, input: {
|
|
554
|
+
request?: import("../r-bridge/retriever").RParseRequests;
|
|
555
|
+
parser?: import("../r-bridge/parser").Parser<import("../r-bridge/parser").KnownParserType>;
|
|
556
|
+
}) => import("../dataflow/info").DataflowInformation;
|
|
557
|
+
readonly requiredInput: {};
|
|
558
|
+
readonly name: "dataflow";
|
|
559
|
+
readonly description: "Construct the dataflow graph";
|
|
560
|
+
readonly executed: import("../core/steps/pipeline-step").PipelineStepStage.OncePerFile;
|
|
561
|
+
readonly printer: {
|
|
562
|
+
readonly 0: typeof import("../core/print/print").internalPrinter;
|
|
563
|
+
readonly 2: typeof import("../core/print/dataflow-printer").dataflowGraphToJson;
|
|
564
|
+
readonly 5: typeof import("../core/print/dataflow-printer").dataflowGraphToQuads;
|
|
565
|
+
readonly 3: typeof import("../core/print/dataflow-printer").dataflowGraphToMermaid;
|
|
566
|
+
readonly 4: typeof import("../core/print/dataflow-printer").dataflowGraphToMermaidUrl;
|
|
567
|
+
};
|
|
568
|
+
readonly dependencies: readonly ["normalize"];
|
|
569
|
+
}>>, queryResults: BaseQueryResult, result: string[]) => true;
|
|
570
|
+
readonly schema: Joi.ObjectSchema<any>;
|
|
571
|
+
};
|
|
572
|
+
readonly 'resolve-value': {
|
|
573
|
+
readonly executor: typeof import("./catalog/resolve-value-query/resolve-value-query-executor").executeResolveValueQuery;
|
|
574
|
+
readonly asciiSummarizer: (formatter: OutputFormatter, _processed: PipelineOutput<import("../core/steps/pipeline/pipeline").Pipeline<{
|
|
575
|
+
readonly name: "parse";
|
|
576
|
+
readonly humanReadableName: "parse with R shell";
|
|
577
|
+
readonly description: "Parse the given R code into an AST";
|
|
578
|
+
readonly processor: (_results: unknown, input: Partial<import("../r-bridge/parser").ParseRequiredInput<string>>) => Promise<import("../r-bridge/parser").ParseStepOutput<string>>;
|
|
579
|
+
readonly executed: import("../core/steps/pipeline-step").PipelineStepStage.OncePerFile;
|
|
580
|
+
readonly printer: {
|
|
581
|
+
readonly 0: typeof import("../core/print/print").internalPrinter;
|
|
582
|
+
readonly 2: {
|
|
583
|
+
(value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string;
|
|
584
|
+
(value: any, replacer?: (number | string)[] | null, space?: string | number): string;
|
|
585
|
+
};
|
|
586
|
+
readonly 5: ({ parsed }: import("../r-bridge/parser").ParseStepOutput<string>, config: import("../util/quads").QuadSerializationConfiguration) => string;
|
|
587
|
+
};
|
|
588
|
+
readonly dependencies: readonly [];
|
|
589
|
+
readonly requiredInput: import("../r-bridge/parser").ParseRequiredInput<string>;
|
|
590
|
+
} | {
|
|
591
|
+
readonly name: "normalize";
|
|
592
|
+
readonly humanReadableName: "normalize";
|
|
593
|
+
readonly description: "Normalize the AST to flowR's AST";
|
|
594
|
+
readonly processor: (results: {
|
|
595
|
+
parse?: import("../r-bridge/parser").ParseStepOutput<string>;
|
|
596
|
+
}, 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>>;
|
|
597
|
+
readonly executed: import("../core/steps/pipeline-step").PipelineStepStage.OncePerFile;
|
|
598
|
+
readonly printer: {
|
|
599
|
+
readonly 0: typeof import("../core/print/print").internalPrinter;
|
|
600
|
+
readonly 2: typeof import("../core/print/normalize-printer").normalizedAstToJson;
|
|
601
|
+
readonly 5: typeof import("../core/print/normalize-printer").normalizedAstToQuads;
|
|
602
|
+
readonly 3: typeof import("../core/print/normalize-printer").printNormalizedAstToMermaid;
|
|
603
|
+
readonly 4: typeof import("../core/print/normalize-printer").printNormalizedAstToMermaidUrl;
|
|
604
|
+
};
|
|
605
|
+
readonly dependencies: readonly ["parse"];
|
|
606
|
+
readonly requiredInput: import("../core/steps/all/core/10-normalize").NormalizeRequiredInput;
|
|
607
|
+
} | {
|
|
608
|
+
readonly humanReadableName: "dataflow";
|
|
609
|
+
readonly processor: (results: {
|
|
610
|
+
normalize?: import("../r-bridge/lang-4.x/ast/model/processing/decorate").NormalizedAst;
|
|
611
|
+
}, input: {
|
|
612
|
+
request?: import("../r-bridge/retriever").RParseRequests;
|
|
613
|
+
parser?: import("../r-bridge/parser").Parser<import("../r-bridge/parser").KnownParserType>;
|
|
614
|
+
}) => import("../dataflow/info").DataflowInformation;
|
|
615
|
+
readonly requiredInput: {};
|
|
616
|
+
readonly name: "dataflow";
|
|
617
|
+
readonly description: "Construct the dataflow graph";
|
|
618
|
+
readonly executed: import("../core/steps/pipeline-step").PipelineStepStage.OncePerFile;
|
|
619
|
+
readonly printer: {
|
|
620
|
+
readonly 0: typeof import("../core/print/print").internalPrinter;
|
|
621
|
+
readonly 2: typeof import("../core/print/dataflow-printer").dataflowGraphToJson;
|
|
622
|
+
readonly 5: typeof import("../core/print/dataflow-printer").dataflowGraphToQuads;
|
|
623
|
+
readonly 3: typeof import("../core/print/dataflow-printer").dataflowGraphToMermaid;
|
|
624
|
+
readonly 4: typeof import("../core/print/dataflow-printer").dataflowGraphToMermaidUrl;
|
|
625
|
+
};
|
|
626
|
+
readonly dependencies: readonly ["normalize"];
|
|
627
|
+
}>>, queryResults: BaseQueryResult, result: string[]) => true;
|
|
628
|
+
readonly schema: Joi.ObjectSchema<any>;
|
|
629
|
+
};
|
|
512
630
|
};
|
|
513
631
|
export type SupportedQueryTypes = keyof typeof SupportedQueries;
|
|
514
632
|
export type QueryResult<Type extends Query['type']> = ReturnType<typeof SupportedQueries[Type]['executor']>;
|
package/queries/query.js
CHANGED
|
@@ -24,6 +24,8 @@ const joi_1 = __importDefault(require("joi"));
|
|
|
24
24
|
const location_map_query_format_1 = require("./catalog/location-map-query/location-map-query-format");
|
|
25
25
|
const config_query_format_1 = require("./catalog/config-query/config-query-format");
|
|
26
26
|
const search_query_format_1 = require("./catalog/search-query/search-query-format");
|
|
27
|
+
const happens_before_query_format_1 = require("./catalog/happens-before-query/happens-before-query-format");
|
|
28
|
+
const resolve_value_query_format_1 = require("./catalog/resolve-value-query/resolve-value-query-format");
|
|
27
29
|
exports.SupportedQueries = {
|
|
28
30
|
'call-context': call_context_query_format_1.CallContextQueryDefinition,
|
|
29
31
|
'config': config_query_format_1.ConfigQueryDefinition,
|
|
@@ -35,7 +37,9 @@ exports.SupportedQueries = {
|
|
|
35
37
|
'lineage': lineage_query_format_1.LineageQueryDefinition,
|
|
36
38
|
'dependencies': dependencies_query_format_1.DependenciesQueryDefinition,
|
|
37
39
|
'location-map': location_map_query_format_1.LocationMapQueryDefinition,
|
|
38
|
-
'search': search_query_format_1.SearchQueryDefinition
|
|
40
|
+
'search': search_query_format_1.SearchQueryDefinition,
|
|
41
|
+
'happens-before': happens_before_query_format_1.HappensBeforeQueryDefinition,
|
|
42
|
+
'resolve-value': resolve_value_query_format_1.ResolveValueQueryDefinition
|
|
39
43
|
};
|
|
40
44
|
function executeQueriesOfSameType(data, ...queries) {
|
|
41
45
|
(0, assert_1.guard)(queries.length > 0, 'At least one query must be provided');
|
package/r-bridge/data/data.d.ts
CHANGED
|
@@ -190,8 +190,8 @@ export declare const flowrCapabilities: {
|
|
|
190
190
|
readonly supported: "fully";
|
|
191
191
|
readonly description: "_Detect calls like `x[]`, `x[2,,42]`, ..._";
|
|
192
192
|
}, {
|
|
193
|
-
readonly name: "Subsetting";
|
|
194
|
-
readonly id: "subsetting";
|
|
193
|
+
readonly name: "Subsetting (Multiple Indices)";
|
|
194
|
+
readonly id: "subsetting-multiple";
|
|
195
195
|
readonly supported: "fully";
|
|
196
196
|
readonly description: "_Detect calls like `x[i > 3]`, `x[c(1,3)]`, ..._";
|
|
197
197
|
}];
|
package/r-bridge/data/data.js
CHANGED
|
@@ -237,8 +237,8 @@ exports.flowrCapabilities = {
|
|
|
237
237
|
description: '_Detect calls like `x[]`, `x[2,,42]`, ..._'
|
|
238
238
|
},
|
|
239
239
|
{
|
|
240
|
-
name: 'Subsetting',
|
|
241
|
-
id: 'subsetting',
|
|
240
|
+
name: 'Subsetting (Multiple Indices)',
|
|
241
|
+
id: 'subsetting-multiple',
|
|
242
242
|
supported: 'fully',
|
|
243
243
|
description: '_Detect calls like `x[i > 3]`, `x[c(1,3)]`, ..._'
|
|
244
244
|
}
|
|
@@ -17,6 +17,9 @@ function normalizeTreeSitterTreeToAst(tree) {
|
|
|
17
17
|
}
|
|
18
18
|
return root;
|
|
19
19
|
}
|
|
20
|
+
function nonErrorChildren(node) {
|
|
21
|
+
return node.children.filter(n => n.type !== tree_sitter_types_1.TreeSitterType.Error);
|
|
22
|
+
}
|
|
20
23
|
function convertTreeNode(node) {
|
|
21
24
|
// generally, the grammar source file dictates what children a node has in what order:
|
|
22
25
|
// https://github.com/r-lib/tree-sitter-r/blob/main/grammar.js
|
|
@@ -30,7 +33,7 @@ function convertTreeNode(node) {
|
|
|
30
33
|
};
|
|
31
34
|
switch (node.type) {
|
|
32
35
|
case tree_sitter_types_1.TreeSitterType.Program: {
|
|
33
|
-
const [comments, children] = splitComments(node
|
|
36
|
+
const [comments, children] = splitComments(nonErrorChildren(node));
|
|
34
37
|
const body = children.map(n => [n, convertTreeNode(n)]);
|
|
35
38
|
const remainingComments = linkCommentsToNextNodes(body, comments);
|
|
36
39
|
return {
|
|
@@ -45,7 +48,7 @@ function convertTreeNode(node) {
|
|
|
45
48
|
}
|
|
46
49
|
case tree_sitter_types_1.TreeSitterType.BracedExpression:
|
|
47
50
|
case tree_sitter_types_1.TreeSitterType.ParenthesizedExpression: {
|
|
48
|
-
const [comments, children] = splitComments(node
|
|
51
|
+
const [comments, children] = splitComments(nonErrorChildren(node));
|
|
49
52
|
const opening = children[0];
|
|
50
53
|
const body = children.slice(1, -1).map(n => [n, convertTreeNode(n)]);
|
|
51
54
|
const remainingComments = linkCommentsToNextNodes(body, comments);
|
|
@@ -78,9 +81,10 @@ function convertTreeNode(node) {
|
|
|
78
81
|
};
|
|
79
82
|
}
|
|
80
83
|
case tree_sitter_types_1.TreeSitterType.BinaryOperator: {
|
|
81
|
-
const
|
|
82
|
-
const
|
|
83
|
-
const
|
|
84
|
+
const children = nonErrorChildren(node);
|
|
85
|
+
const lhs = convertTreeNode(children[0]);
|
|
86
|
+
const rhs = convertTreeNode(children[children.length - 1]);
|
|
87
|
+
const [commentsBoth, [op]] = splitComments(children.slice(1, -1));
|
|
84
88
|
const comments = commentsBoth.map(c => c[1]);
|
|
85
89
|
const opSource = makeSourceRange(op);
|
|
86
90
|
const lhsAsArg = {
|
|
@@ -150,7 +154,7 @@ function convertTreeNode(node) {
|
|
|
150
154
|
}
|
|
151
155
|
}
|
|
152
156
|
case tree_sitter_types_1.TreeSitterType.UnaryOperator: {
|
|
153
|
-
const [op, operand] = node
|
|
157
|
+
const [op, operand] = nonErrorChildren(node);
|
|
154
158
|
return {
|
|
155
159
|
type: type_1.RType.UnaryOp,
|
|
156
160
|
operand: convertTreeNode(operand),
|
|
@@ -161,7 +165,7 @@ function convertTreeNode(node) {
|
|
|
161
165
|
};
|
|
162
166
|
}
|
|
163
167
|
case tree_sitter_types_1.TreeSitterType.NamespaceOperator: {
|
|
164
|
-
const [lhs, /* :: or ::: */ , rhs] = node
|
|
168
|
+
const [lhs, /* :: or ::: */ , rhs] = nonErrorChildren(node);
|
|
165
169
|
return {
|
|
166
170
|
type: type_1.RType.Symbol,
|
|
167
171
|
location: makeSourceRange(rhs),
|
|
@@ -176,6 +180,7 @@ function convertTreeNode(node) {
|
|
|
176
180
|
case tree_sitter_types_1.TreeSitterType.Na:
|
|
177
181
|
case tree_sitter_types_1.TreeSitterType.Null:
|
|
178
182
|
case tree_sitter_types_1.TreeSitterType.Dots:
|
|
183
|
+
case tree_sitter_types_1.TreeSitterType.DotDotI:
|
|
179
184
|
case tree_sitter_types_1.TreeSitterType.Identifier:
|
|
180
185
|
case tree_sitter_types_1.TreeSitterType.Return:
|
|
181
186
|
return {
|
|
@@ -187,22 +192,24 @@ function convertTreeNode(node) {
|
|
|
187
192
|
...defaultInfo
|
|
188
193
|
};
|
|
189
194
|
case tree_sitter_types_1.TreeSitterType.IfStatement: {
|
|
190
|
-
const [ifNode, /* ( */ , condition, /* ) */ , then, /* else */ , ...otherwise] = node
|
|
195
|
+
const [ifNode, /* ( */ , condition, /* ) */ , then, /* else */ , ...otherwise] = nonErrorChildren(node);
|
|
196
|
+
const filteredOtherwise = otherwise.filter(n => n.type !== tree_sitter_types_1.TreeSitterType.ElseStatement);
|
|
191
197
|
return {
|
|
192
198
|
type: type_1.RType.IfThenElse,
|
|
193
199
|
condition: convertTreeNode(condition),
|
|
194
200
|
then: (0, normalize_meta_1.ensureExpressionList)(convertTreeNode(then)),
|
|
195
|
-
otherwise:
|
|
201
|
+
otherwise: filteredOtherwise.length > 0 ? (0, normalize_meta_1.ensureExpressionList)(convertTreeNode(filteredOtherwise[0])) : undefined,
|
|
196
202
|
location: makeSourceRange(ifNode),
|
|
197
203
|
lexeme: ifNode.text,
|
|
198
204
|
...defaultInfo
|
|
199
205
|
};
|
|
200
206
|
}
|
|
201
207
|
case tree_sitter_types_1.TreeSitterType.ForStatement: {
|
|
202
|
-
const
|
|
203
|
-
const
|
|
204
|
-
const
|
|
205
|
-
const
|
|
208
|
+
const children = nonErrorChildren(node);
|
|
209
|
+
const forNode = children[0]; // we follow with a (
|
|
210
|
+
const variable = getNodesUntil(children, 'in', 2); // we follow with the "in"
|
|
211
|
+
const sequence = getNodesUntil(children, ')', 2 + variable.length + 1); // we follow with a (
|
|
212
|
+
const body = children[2 + variable.length + 1 + sequence.length + 1];
|
|
206
213
|
const [variableComments, [variableNode]] = splitComments(variable);
|
|
207
214
|
const [sequenceComments, [sequenceNode]] = splitComments(sequence);
|
|
208
215
|
return {
|
|
@@ -231,7 +238,7 @@ function convertTreeNode(node) {
|
|
|
231
238
|
};
|
|
232
239
|
}
|
|
233
240
|
case tree_sitter_types_1.TreeSitterType.WhileStatement: {
|
|
234
|
-
const [whileNode, /* ( */ , condition, /* ) */ , body] = node
|
|
241
|
+
const [whileNode, /* ( */ , condition, /* ) */ , body] = nonErrorChildren(node);
|
|
235
242
|
return {
|
|
236
243
|
type: type_1.RType.WhileLoop,
|
|
237
244
|
condition: convertTreeNode(condition),
|
|
@@ -242,7 +249,7 @@ function convertTreeNode(node) {
|
|
|
242
249
|
};
|
|
243
250
|
}
|
|
244
251
|
case tree_sitter_types_1.TreeSitterType.RepeatStatement: {
|
|
245
|
-
const [repeatNode, body] = node
|
|
252
|
+
const [repeatNode, body] = nonErrorChildren(node);
|
|
246
253
|
return {
|
|
247
254
|
type: type_1.RType.RepeatLoop,
|
|
248
255
|
body: (0, normalize_meta_1.ensureExpressionList)(convertTreeNode(body)),
|
|
@@ -252,7 +259,7 @@ function convertTreeNode(node) {
|
|
|
252
259
|
};
|
|
253
260
|
}
|
|
254
261
|
case tree_sitter_types_1.TreeSitterType.Call: {
|
|
255
|
-
const [func, argsParentheses] = node
|
|
262
|
+
const [func, argsParentheses] = nonErrorChildren(node);
|
|
256
263
|
// tree-sitter wraps next and break in a function call, but we don't, so unwrap
|
|
257
264
|
if (func.type === tree_sitter_types_1.TreeSitterType.Next || func.type == tree_sitter_types_1.TreeSitterType.Break) {
|
|
258
265
|
return {
|
|
@@ -260,7 +267,7 @@ function convertTreeNode(node) {
|
|
|
260
267
|
...defaultInfo
|
|
261
268
|
};
|
|
262
269
|
}
|
|
263
|
-
const args = (0, arrays_1.splitArrayOn)(argsParentheses.
|
|
270
|
+
const args = (0, arrays_1.splitArrayOn)(nonErrorChildren(argsParentheses).slice(1, -1), x => x.type === 'comma');
|
|
264
271
|
const funcRange = makeSourceRange(func);
|
|
265
272
|
const call = {
|
|
266
273
|
arguments: args.map(n => n.length == 0 ? r_function_call_1.EmptyArgument : convertTreeNode(n[0])),
|
|
@@ -302,7 +309,7 @@ function convertTreeNode(node) {
|
|
|
302
309
|
}
|
|
303
310
|
}
|
|
304
311
|
case tree_sitter_types_1.TreeSitterType.FunctionDefinition: {
|
|
305
|
-
const [name, paramsParens, body] = node
|
|
312
|
+
const [name, paramsParens, body] = nonErrorChildren(node);
|
|
306
313
|
const params = (0, arrays_1.splitArrayOn)(paramsParens.children.slice(1, -1), x => x.type === 'comma');
|
|
307
314
|
return {
|
|
308
315
|
type: type_1.RType.FunctionDefinition,
|
|
@@ -353,9 +360,9 @@ function convertTreeNode(node) {
|
|
|
353
360
|
case tree_sitter_types_1.TreeSitterType.Subset:
|
|
354
361
|
case tree_sitter_types_1.TreeSitterType.Subset2: {
|
|
355
362
|
// subset has children like a and [x]
|
|
356
|
-
const [func, content] = node
|
|
363
|
+
const [func, content] = nonErrorChildren(node);
|
|
357
364
|
// bracket is now [ or [[ and argsClosing is x] or x]]
|
|
358
|
-
const [bracket, ...argsClosing] = content
|
|
365
|
+
const [bracket, ...argsClosing] = nonErrorChildren(content);
|
|
359
366
|
const args = (0, arrays_1.splitArrayOn)(argsClosing.slice(0, -1), x => x.type === 'comma');
|
|
360
367
|
return {
|
|
361
368
|
type: type_1.RType.Access,
|
|
@@ -368,7 +375,7 @@ function convertTreeNode(node) {
|
|
|
368
375
|
};
|
|
369
376
|
}
|
|
370
377
|
case tree_sitter_types_1.TreeSitterType.ExtractOperator: {
|
|
371
|
-
const [lhs, operator, rhs] = node
|
|
378
|
+
const [lhs, operator, rhs] = nonErrorChildren(node);
|
|
372
379
|
const rhsRange = makeSourceRange(rhs);
|
|
373
380
|
return {
|
|
374
381
|
type: type_1.RType.Access,
|
|
@@ -395,11 +402,12 @@ function convertTreeNode(node) {
|
|
|
395
402
|
};
|
|
396
403
|
}
|
|
397
404
|
case tree_sitter_types_1.TreeSitterType.Parameter: {
|
|
398
|
-
const
|
|
405
|
+
const children = nonErrorChildren(node);
|
|
406
|
+
const name = children[0];
|
|
399
407
|
const nameRange = makeSourceRange(name);
|
|
400
408
|
let defaultValue = undefined;
|
|
401
|
-
if (
|
|
402
|
-
defaultValue = convertTreeNode(
|
|
409
|
+
if (children.length == 3) {
|
|
410
|
+
defaultValue = convertTreeNode(children[2]);
|
|
403
411
|
}
|
|
404
412
|
return {
|
|
405
413
|
type: type_1.RType.Parameter,
|
|
@@ -427,8 +435,9 @@ function convertTreeNode(node) {
|
|
|
427
435
|
};
|
|
428
436
|
}
|
|
429
437
|
case tree_sitter_types_1.TreeSitterType.Argument: {
|
|
430
|
-
|
|
431
|
-
|
|
438
|
+
const children = nonErrorChildren(node);
|
|
439
|
+
if (children.length == 1) {
|
|
440
|
+
const [arg] = children;
|
|
432
441
|
return {
|
|
433
442
|
type: type_1.RType.Argument,
|
|
434
443
|
name: undefined,
|
|
@@ -439,7 +448,7 @@ function convertTreeNode(node) {
|
|
|
439
448
|
};
|
|
440
449
|
}
|
|
441
450
|
else {
|
|
442
|
-
const [nameNode, /* = */ , valueNode] =
|
|
451
|
+
const [nameNode, /* = */ , valueNode] = children;
|
|
443
452
|
let name = convertTreeNode(nameNode);
|
|
444
453
|
// unescape argument names
|
|
445
454
|
if (name.type === type_1.RType.String) {
|
|
@@ -457,7 +466,7 @@ function convertTreeNode(node) {
|
|
|
457
466
|
return {
|
|
458
467
|
type: type_1.RType.Argument,
|
|
459
468
|
name: name,
|
|
460
|
-
value: convertTreeNode(valueNode),
|
|
469
|
+
value: valueNode ? convertTreeNode(valueNode) : undefined,
|
|
461
470
|
location: nameRange,
|
|
462
471
|
lexeme: nameNode.text,
|
|
463
472
|
info: {
|
|
@@ -468,8 +477,16 @@ function convertTreeNode(node) {
|
|
|
468
477
|
};
|
|
469
478
|
}
|
|
470
479
|
}
|
|
480
|
+
case tree_sitter_types_1.TreeSitterType.Comment:
|
|
481
|
+
return {
|
|
482
|
+
type: type_1.RType.Comment,
|
|
483
|
+
location: range,
|
|
484
|
+
content: node.text.slice(1),
|
|
485
|
+
lexeme: node.text,
|
|
486
|
+
...defaultInfo
|
|
487
|
+
};
|
|
471
488
|
default:
|
|
472
|
-
throw new normalizer_data_1.ParseError(`unexpected node type ${node.type}`);
|
|
489
|
+
throw new normalizer_data_1.ParseError(`unexpected node type ${node.type} at ${JSON.stringify(range)}`);
|
|
473
490
|
}
|
|
474
491
|
}
|
|
475
492
|
function makeSourceRange(node) {
|
|
@@ -7,6 +7,7 @@ export declare enum TreeSitterType {
|
|
|
7
7
|
NamespaceOperator = "namespace_operator",
|
|
8
8
|
Identifier = "identifier",
|
|
9
9
|
IfStatement = "if_statement",
|
|
10
|
+
ElseStatement = "else",
|
|
10
11
|
ForStatement = "for_statement",
|
|
11
12
|
WhileStatement = "while_statement",
|
|
12
13
|
RepeatStatement = "repeat_statement",
|
|
@@ -31,5 +32,7 @@ export declare enum TreeSitterType {
|
|
|
31
32
|
Parameter = "parameter",
|
|
32
33
|
Argument = "argument",
|
|
33
34
|
Dots = "dots",
|
|
34
|
-
|
|
35
|
+
DotDotI = "dot_dot_i",
|
|
36
|
+
Comment = "comment",
|
|
37
|
+
Error = "ERROR"
|
|
35
38
|
}
|
|
@@ -11,6 +11,7 @@ var TreeSitterType;
|
|
|
11
11
|
TreeSitterType["NamespaceOperator"] = "namespace_operator";
|
|
12
12
|
TreeSitterType["Identifier"] = "identifier";
|
|
13
13
|
TreeSitterType["IfStatement"] = "if_statement";
|
|
14
|
+
TreeSitterType["ElseStatement"] = "else";
|
|
14
15
|
TreeSitterType["ForStatement"] = "for_statement";
|
|
15
16
|
TreeSitterType["WhileStatement"] = "while_statement";
|
|
16
17
|
TreeSitterType["RepeatStatement"] = "repeat_statement";
|
|
@@ -35,6 +36,8 @@ var TreeSitterType;
|
|
|
35
36
|
TreeSitterType["Parameter"] = "parameter";
|
|
36
37
|
TreeSitterType["Argument"] = "argument";
|
|
37
38
|
TreeSitterType["Dots"] = "dots";
|
|
39
|
+
TreeSitterType["DotDotI"] = "dot_dot_i";
|
|
38
40
|
TreeSitterType["Comment"] = "comment";
|
|
41
|
+
TreeSitterType["Error"] = "ERROR";
|
|
39
42
|
})(TreeSitterType || (exports.TreeSitterType = TreeSitterType = {}));
|
|
40
43
|
//# sourceMappingURL=tree-sitter-types.js.map
|
package/util/cfg/cfg.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { QuadSerializationConfiguration } from '../quads';
|
|
|
3
3
|
import type { NodeId } from '../../r-bridge/lang-4.x/ast/model/processing/node-id';
|
|
4
4
|
import type { NormalizedAst, ParentInformation } from '../../r-bridge/lang-4.x/ast/model/processing/decorate';
|
|
5
5
|
import { RFalse, RTrue } from '../../r-bridge/lang-4.x/convert-values';
|
|
6
|
+
import type { DataflowGraph } from '../../dataflow/graph/graph';
|
|
6
7
|
export declare const enum CfgVertexType {
|
|
7
8
|
/** Marks a break point in a construct (e.g., between the name and the value of an argument, or the formals and the body of a function) */
|
|
8
9
|
MidMarker = "mid-marker",
|
|
@@ -25,6 +26,8 @@ interface CfgFlowDependencyEdge extends MergeableRecord {
|
|
|
25
26
|
}
|
|
26
27
|
interface CfgControlDependencyEdge extends MergeableRecord {
|
|
27
28
|
label: 'CD';
|
|
29
|
+
/** the id which caused the control dependency */
|
|
30
|
+
caused: NodeId;
|
|
28
31
|
when: typeof RTrue | typeof RFalse;
|
|
29
32
|
}
|
|
30
33
|
export type CfgEdge = CfgFlowDependencyEdge | CfgControlDependencyEdge;
|
|
@@ -55,7 +58,15 @@ export interface ControlFlowInformation extends MergeableRecord {
|
|
|
55
58
|
graph: ControlFlowGraph;
|
|
56
59
|
}
|
|
57
60
|
export declare function emptyControlFlowInformation(): ControlFlowInformation;
|
|
58
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Given a normalized AST this approximates the control flow graph of the program.
|
|
63
|
+
* This few is different from the computation of the dataflow graph and may differ,
|
|
64
|
+
* especially because it focuses on intra-procedural analysis.
|
|
65
|
+
*
|
|
66
|
+
* @param ast - the normalized AST
|
|
67
|
+
* @param graph - additional dataflow facts to consider by the control flow extraction
|
|
68
|
+
*/
|
|
69
|
+
export declare function extractCFG<Info = ParentInformation>(ast: NormalizedAst<Info>, graph?: DataflowGraph): ControlFlowInformation;
|
|
59
70
|
/**
|
|
60
71
|
* Returns true if the given CFG equals the other CFG. False otherwise.
|
|
61
72
|
*/
|
package/util/cfg/cfg.js
CHANGED
|
@@ -12,6 +12,8 @@ const json_1 = require("../json");
|
|
|
12
12
|
const fold_1 = require("../../r-bridge/lang-4.x/ast/model/processing/fold");
|
|
13
13
|
const convert_values_1 = require("../../r-bridge/lang-4.x/convert-values");
|
|
14
14
|
const r_function_call_1 = require("../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
|
|
15
|
+
const linker_1 = require("../../dataflow/internal/linker");
|
|
16
|
+
const vertex_1 = require("../../dataflow/graph/vertex");
|
|
15
17
|
/**
|
|
16
18
|
* This class represents the control flow graph of an R program.
|
|
17
19
|
* The control flow may be hierarchical when confronted with function definitions (see {@link CfgVertex} and {@link CFG#rootVertexIds|rootVertexIds()}).
|
|
@@ -101,8 +103,25 @@ const cfgFolds = {
|
|
|
101
103
|
foldArgument: cfgArgumentOrParameter
|
|
102
104
|
}
|
|
103
105
|
};
|
|
104
|
-
function
|
|
105
|
-
return
|
|
106
|
+
function dataflowCfgFolds(dataflowGraph) {
|
|
107
|
+
return {
|
|
108
|
+
...cfgFolds,
|
|
109
|
+
functions: {
|
|
110
|
+
...cfgFolds.functions,
|
|
111
|
+
foldFunctionCall: cfgFunctionCallWithDataflow(dataflowGraph)
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Given a normalized AST this approximates the control flow graph of the program.
|
|
117
|
+
* This few is different from the computation of the dataflow graph and may differ,
|
|
118
|
+
* especially because it focuses on intra-procedural analysis.
|
|
119
|
+
*
|
|
120
|
+
* @param ast - the normalized AST
|
|
121
|
+
* @param graph - additional dataflow facts to consider by the control flow extraction
|
|
122
|
+
*/
|
|
123
|
+
function extractCFG(ast, graph) {
|
|
124
|
+
return (0, fold_1.foldAst)(ast.ast, graph ? dataflowCfgFolds(graph) : cfgFolds);
|
|
106
125
|
}
|
|
107
126
|
function cfgLeaf(type) {
|
|
108
127
|
return (leaf) => {
|
|
@@ -134,10 +153,10 @@ function cfgIfThenElse(ifNode, condition, then, otherwise) {
|
|
|
134
153
|
}
|
|
135
154
|
for (const exitPoint of condition.exitPoints) {
|
|
136
155
|
for (const entryPoint of then.entryPoints) {
|
|
137
|
-
graph.addEdge(entryPoint, exitPoint, { label: 'CD', when: convert_values_1.RTrue });
|
|
156
|
+
graph.addEdge(entryPoint, exitPoint, { label: 'CD', when: convert_values_1.RTrue, caused: ifNode.info.id });
|
|
138
157
|
}
|
|
139
158
|
for (const entryPoint of otherwise?.entryPoints ?? []) {
|
|
140
|
-
graph.addEdge(entryPoint, exitPoint, { label: 'CD', when: convert_values_1.RFalse });
|
|
159
|
+
graph.addEdge(entryPoint, exitPoint, { label: 'CD', when: convert_values_1.RFalse, caused: ifNode.info.id });
|
|
141
160
|
}
|
|
142
161
|
}
|
|
143
162
|
for (const entryPoint of condition.entryPoints) {
|
|
@@ -148,7 +167,7 @@ function cfgIfThenElse(ifNode, condition, then, otherwise) {
|
|
|
148
167
|
}
|
|
149
168
|
if (!otherwise) {
|
|
150
169
|
for (const exitPoint of condition.exitPoints) {
|
|
151
|
-
graph.addEdge(ifNode.info.id + '-exit', exitPoint, { label: 'CD', when: convert_values_1.RFalse });
|
|
170
|
+
graph.addEdge(ifNode.info.id + '-exit', exitPoint, { label: 'CD', when: convert_values_1.RFalse, caused: ifNode.info.id });
|
|
152
171
|
}
|
|
153
172
|
}
|
|
154
173
|
return {
|
|
@@ -165,7 +184,7 @@ function cfgRepeat(repeat, body) {
|
|
|
165
184
|
graph.addVertex({ id: repeat.info.id, name: repeat.type, type: identifyMayStatementType(repeat) });
|
|
166
185
|
graph.addVertex({ id: repeat.info.id + '-exit', name: 'repeat-exit', type: "end-marker" /* CfgVertexType.EndMarker */ });
|
|
167
186
|
for (const entryPoint of body.entryPoints) {
|
|
168
|
-
graph.addEdge(repeat.info.id,
|
|
187
|
+
graph.addEdge(entryPoint, repeat.info.id, { label: 'FD' });
|
|
169
188
|
}
|
|
170
189
|
// loops automatically
|
|
171
190
|
for (const next of [...body.nexts, ...body.exitPoints]) {
|
|
@@ -186,7 +205,7 @@ function cfgWhile(whileLoop, condition, body) {
|
|
|
186
205
|
}
|
|
187
206
|
for (const exit of condition.exitPoints) {
|
|
188
207
|
for (const entry of body.entryPoints) {
|
|
189
|
-
graph.addEdge(entry, exit, { label: 'CD', when: convert_values_1.RTrue });
|
|
208
|
+
graph.addEdge(entry, exit, { label: 'CD', when: convert_values_1.RTrue, caused: whileLoop.info.id });
|
|
190
209
|
}
|
|
191
210
|
}
|
|
192
211
|
for (const entryPoint of body.entryPoints) {
|
|
@@ -200,7 +219,7 @@ function cfgWhile(whileLoop, condition, body) {
|
|
|
200
219
|
}
|
|
201
220
|
// while can break on the condition as well
|
|
202
221
|
for (const exit of condition.exitPoints) {
|
|
203
|
-
graph.addEdge(whileLoop.info.id + '-exit', exit, { label: 'CD', when: convert_values_1.RFalse });
|
|
222
|
+
graph.addEdge(whileLoop.info.id + '-exit', exit, { label: 'CD', when: convert_values_1.RFalse, caused: whileLoop.info.id });
|
|
204
223
|
}
|
|
205
224
|
return { graph, breaks: [], nexts: [], returns: body.returns, exitPoints: [whileLoop.info.id + '-exit'], entryPoints: [whileLoop.info.id] };
|
|
206
225
|
}
|
|
@@ -220,7 +239,7 @@ function cfgFor(forLoop, variable, vector, body) {
|
|
|
220
239
|
}
|
|
221
240
|
for (const exit of variable.exitPoints) {
|
|
222
241
|
for (const entry of body.entryPoints) {
|
|
223
|
-
graph.addEdge(entry, exit, { label: 'CD', when: convert_values_1.RTrue });
|
|
242
|
+
graph.addEdge(entry, exit, { label: 'CD', when: convert_values_1.RTrue, caused: forLoop.info.id });
|
|
224
243
|
}
|
|
225
244
|
}
|
|
226
245
|
for (const next of [...body.nexts, ...body.exitPoints]) {
|
|
@@ -231,7 +250,7 @@ function cfgFor(forLoop, variable, vector, body) {
|
|
|
231
250
|
}
|
|
232
251
|
// while can break on the condition as well
|
|
233
252
|
for (const exit of variable.exitPoints) {
|
|
234
|
-
graph.addEdge(forLoop.info.id + '-exit', exit, { label: 'CD', when: convert_values_1.RFalse });
|
|
253
|
+
graph.addEdge(forLoop.info.id + '-exit', exit, { label: 'CD', when: convert_values_1.RFalse, caused: forLoop.info.id });
|
|
235
254
|
}
|
|
236
255
|
return { graph, breaks: [], nexts: [], returns: body.returns, exitPoints: [forLoop.info.id + '-exit'], entryPoints: [forLoop.info.id] };
|
|
237
256
|
}
|
|
@@ -253,6 +272,9 @@ function cfgFunctionDefinition(fn, params, body) {
|
|
|
253
272
|
graph.addEdge(fn.info.id + '-params', exit, { label: 'FD' });
|
|
254
273
|
}
|
|
255
274
|
}
|
|
275
|
+
if (params.length === 0) {
|
|
276
|
+
graph.addEdge(fn.info.id + '-params', fn.info.id, { label: 'FD' });
|
|
277
|
+
}
|
|
256
278
|
for (const entry of body.entryPoints) {
|
|
257
279
|
graph.addEdge(entry, fn.info.id + '-params', { label: 'FD' });
|
|
258
280
|
}
|
|
@@ -296,6 +318,38 @@ function cfgFunctionCall(call, name, args) {
|
|
|
296
318
|
// should not contain any breaks, nexts, or returns, (except for the body if something like 'break()')
|
|
297
319
|
return info;
|
|
298
320
|
}
|
|
321
|
+
function cfgFunctionCallWithDataflow(graph) {
|
|
322
|
+
return (call, name, args) => {
|
|
323
|
+
const baseCFG = cfgFunctionCall(call, name, args);
|
|
324
|
+
/* try to resolve the call and link the target definitions */
|
|
325
|
+
const targets = (0, linker_1.getAllFunctionCallTargets)(call.info.id, graph);
|
|
326
|
+
const exits = [];
|
|
327
|
+
for (const target of targets) {
|
|
328
|
+
// we have to filter out non func-call targets as the call targets contains names and call ids
|
|
329
|
+
if ((0, vertex_1.isFunctionDefinitionVertex)(graph.getVertex(target))) {
|
|
330
|
+
baseCFG.graph.addEdge(call.info.id, target, { label: 'FD' });
|
|
331
|
+
exits.push(target + '-exit');
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
if (exits.length > 0) {
|
|
335
|
+
baseCFG.graph.addVertex({
|
|
336
|
+
id: call.info.id + '-resolved-call-exit',
|
|
337
|
+
name: 'resolved-call-exit',
|
|
338
|
+
type: "end-marker" /* CfgVertexType.EndMarker */
|
|
339
|
+
});
|
|
340
|
+
for (const exit of [...baseCFG.exitPoints, ...exits]) {
|
|
341
|
+
baseCFG.graph.addEdge(call.info.id + '-resolved-call-exit', exit, { label: 'FD' });
|
|
342
|
+
}
|
|
343
|
+
return {
|
|
344
|
+
...baseCFG,
|
|
345
|
+
exitPoints: [call.info.id + '-resolved-call-exit']
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
return baseCFG;
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
}
|
|
299
353
|
function cfgArgumentOrParameter(node, name, value) {
|
|
300
354
|
const graph = new ControlFlowGraph();
|
|
301
355
|
const info = { graph, breaks: [], nexts: [], returns: [], exitPoints: [node.info.id + '-exit'], entryPoints: [node.info.id] };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ControlFlowGraph } from './cfg';
|
|
2
|
+
import type { NodeId } from '../../r-bridge/lang-4.x/ast/model/processing/node-id';
|
|
3
|
+
import { Ternary } from '../logic';
|
|
4
|
+
/**
|
|
5
|
+
* Determines if node `a` happens before node `b` in the control flow graph.
|
|
6
|
+
*/
|
|
7
|
+
export declare function happensBefore(cfg: ControlFlowGraph, a: NodeId, b: NodeId): Ternary;
|