fix(oxc_semantic): Handle short-circuiting operators in CFG (#2252)

Closes #2239
This commit is contained in:
Tzvi Melamed 2024-02-01 08:04:28 -05:00 committed by GitHub
parent 27681951e1
commit f4674f33b2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 253 additions and 43 deletions

View file

@ -666,6 +666,73 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
self.leave_node(kind);
}
fn visit_logical_expression(&mut self, expr: &LogicalExpression<'a>) {
// logical expressions are short-circuiting, and therefore
// also represent control flow.
// For example, in:
// foo && bar();
// the bar() call will only be executed if foo is truthy.
let kind = AstKind::LogicalExpression(self.alloc(expr));
self.enter_node(kind);
self.visit_expression(&expr.left);
/* cfg */
let left_expr_end_ix = self.cfg.current_node_ix;
let right_expr_start_ix = self.cfg.new_basic_block();
/* cfg */
self.visit_expression(&expr.right);
/* cfg */
let right_expr_end_ix = self.cfg.current_node_ix;
let after_logical_expr_ix = self.cfg.new_basic_block();
self.cfg.add_edge(left_expr_end_ix, right_expr_start_ix, EdgeType::Normal);
self.cfg.add_edge(left_expr_end_ix, after_logical_expr_ix, EdgeType::Normal);
self.cfg.add_edge(right_expr_end_ix, after_logical_expr_ix, EdgeType::Normal);
/* cfg */
self.leave_node(kind);
}
fn visit_assignment_expression(&mut self, expr: &AssignmentExpression<'a>) {
// assignment expressions can include an operator, which
// can be used to determine the control flow of the expression.
// For example, in:
// foo &&= super();
// the super() call will only be executed if foo is truthy.
let kind = AstKind::AssignmentExpression(self.alloc(expr));
self.enter_node(kind);
self.visit_assignment_target(&expr.left);
/* cfg */
let cfg_ixs = if expr.operator.is_logical() {
let target_end_ix = self.cfg.current_node_ix;
let expr_start_ix = self.cfg.new_basic_block();
Some((target_end_ix, expr_start_ix))
} else {
None
};
/* cfg */
self.visit_expression(&expr.right);
/* cfg */
if let Some((target_end_ix, expr_start_ix)) = cfg_ixs {
let expr_end_ix = self.cfg.current_node_ix;
let after_assignment_ix = self.cfg.new_basic_block();
self.cfg.add_edge(target_end_ix, expr_start_ix, EdgeType::Normal);
self.cfg.add_edge(target_end_ix, after_assignment_ix, EdgeType::Normal);
self.cfg.add_edge(expr_end_ix, after_assignment_ix, EdgeType::Normal);
}
/* cfg */
self.leave_node(kind);
}
fn visit_for_statement(&mut self, stmt: &ForStatement<'a>) {
let kind = AstKind::ForStatement(self.alloc(stmt));
let is_lexical_declaration =

View file

@ -5,5 +5,20 @@ input_file: crates/oxc_semantic/tests/cfg_fixtures/assignment_operators.js
---
digraph {
0 [ label = ""]
1 [ label = ""]
2 [ label = ""]
3 [ label = ""]
4 [ label = ""]
5 [ label = ""]
6 [ label = ""]
0 -> 1 [ ]
0 -> 2 [ ]
1 -> 2 [ ]
2 -> 3 [ ]
2 -> 4 [ ]
3 -> 4 [ ]
4 -> 5 [ ]
4 -> 6 [ ]
5 -> 6 [ ]
}

View file

@ -6,3 +6,27 @@ input_file: crates/oxc_semantic/tests/cfg_fixtures/assignment_operators.js
bb0: {
}
bb1: {
}
bb2: {
}
bb3: {
}
bb4: {
}
bb5: {
}
bb6: {
}

View file

@ -27,6 +27,10 @@ digraph {
20 [ label = ""]
21 [ label = ""]
22 [ label = ""]
23 [ label = ""]
24 [ label = ""]
25 [ label = ""]
26 [ label = ""]
0 -> 1 [ ]
0 -> 2 [ ]
2 -> 3 [ ]
@ -45,9 +49,15 @@ digraph {
14 -> 16 [ ]
16 -> 17 [ ]
16 -> 18 [ ]
17 -> 18 [ ]
18 -> 19 [ ]
18 -> 20 [ ]
20 -> 21 [ ]
20 -> 22 [ ]
21 -> 22 [ ]
22 -> 23 [ ]
22 -> 24 [ ]
24 -> 25 [ ]
24 -> 26 [ ]
}

View file

@ -94,3 +94,19 @@ bb21: {
bb22: {
}
bb23: {
}
bb24: {
}
bb25: {
}
bb26: {
}

View file

@ -6,26 +6,31 @@ input_file: crates/oxc_semantic/tests/cfg_fixtures/if_else.js
digraph {
0 [ label = ""]
1 [ label = ""]
2 [ label = "$return = <value>"]
2 [ label = ""]
3 [ label = ""]
4 [ label = "Unreachable()"]
5 [ label = "$return = <value>"]
6 [ label = ""]
7 [ label = "Unreachable()"]
4 [ label = "$return = <value>"]
5 [ label = ""]
6 [ label = "Unreachable()"]
7 [ label = "$return = <value>"]
8 [ label = ""]
9 [ label = "Unreachable()\n$return = <value>"]
9 [ label = "Unreachable()"]
10 [ label = ""]
11 [ label = "Unreachable()"]
11 [ label = "Unreachable()\n$return = <value>"]
12 [ label = ""]
13 [ label = "Unreachable()"]
14 [ label = ""]
0 -> 1 [ ]
3 -> 4 [ ]
6 -> 7 [ ]
8 -> 9 [ ]
4 -> 8 [ ]
1 -> 2 [ ]
1 -> 5 [ ]
7 -> 8 [ ]
1 -> 3 [ ]
2 -> 3 [ ]
5 -> 6 [ ]
8 -> 9 [ ]
10 -> 11 [ ]
0 -> 12 [ ]
6 -> 10 [ ]
3 -> 4 [ ]
3 -> 7 [ ]
9 -> 10 [ ]
12 -> 13 [ ]
0 -> 14 [ ]
}

View file

@ -12,7 +12,7 @@ bb1: {
}
bb2: {
$return = <value>
}
bb3: {
@ -20,28 +20,27 @@ bb3: {
}
bb4: {
Unreachable()
}
bb5: {
$return = <value>
}
bb6: {
bb5: {
}
bb7: {
bb6: {
Unreachable()
}
bb7: {
$return = <value>
}
bb8: {
}
bb9: {
Unreachable()
$return = <value>
}
bb10: {
@ -50,8 +49,17 @@ bb10: {
bb11: {
Unreachable()
$return = <value>
}
bb12: {
}
bb13: {
Unreachable()
}
bb14: {
}

View file

@ -5,5 +5,10 @@ input_file: crates/oxc_semantic/tests/cfg_fixtures/infix_operators.js
---
digraph {
0 [ label = ""]
1 [ label = ""]
2 [ label = ""]
0 -> 1 [ ]
0 -> 2 [ ]
1 -> 2 [ ]
}

View file

@ -6,3 +6,11 @@ input_file: crates/oxc_semantic/tests/cfg_fixtures/infix_operators.js
bb0: {
}
bb1: {
}
bb2: {
}

View file

@ -11,14 +11,29 @@ digraph {
4 [ label = ""]
5 [ label = ""]
6 [ label = ""]
1 -> 2 [ ]
7 [ label = ""]
8 [ label = ""]
9 [ label = ""]
10 [ label = ""]
11 [ label = ""]
12 [ label = ""]
0 -> 1 [ ]
0 -> 2 [ ]
1 -> 2 [ ]
3 -> 4 [ ]
2 -> 3 [ ]
2 -> 4 [ ]
5 -> 6 [ ]
4 -> 5 [ ]
4 -> 6 [ ]
5 -> 6 [ ]
7 -> 8 [ ]
6 -> 7 [ ]
6 -> 8 [ ]
8 -> 9 [ ]
8 -> 10 [ ]
9 -> 10 [ ]
11 -> 12 [ ]
10 -> 11 [ ]
10 -> 12 [ ]
}

View file

@ -30,3 +30,27 @@ bb5: {
bb6: {
}
bb7: {
}
bb8: {
}
bb9: {
}
bb10: {
}
bb11: {
}
bb12: {
}

View file

@ -7,21 +7,26 @@ digraph {
0 [ label = ""]
1 [ label = ""]
2 [ label = ""]
3 [ label = "$return = <value>"]
3 [ label = ""]
4 [ label = ""]
5 [ label = "Unreachable()"]
6 [ label = "$return = <value>"]
7 [ label = ""]
8 [ label = "Unreachable()"]
5 [ label = "$return = <value>"]
6 [ label = ""]
7 [ label = "Unreachable()"]
8 [ label = "$return = <value>"]
9 [ label = ""]
10 [ label = "Unreachable()"]
11 [ label = ""]
0 -> 1 [ ]
4 -> 5 [ ]
1 -> 2 [ ]
2 -> 3 [ ]
3 -> 6 [ ]
3 -> 2 [ ]
2 -> 6 [ ]
7 -> 8 [ ]
0 -> 9 [ ]
2 -> 4 [ ]
3 -> 4 [ ]
6 -> 7 [ ]
1 -> 2 [ ]
2 -> 5 [ ]
5 -> 8 [ ]
5 -> 2 [ ]
2 -> 8 [ ]
9 -> 10 [ ]
0 -> 11 [ ]
}

View file

@ -16,7 +16,7 @@ bb2: {
}
bb3: {
$return = <value>
}
bb4: {
@ -24,21 +24,29 @@ bb4: {
}
bb5: {
Unreachable()
}
bb6: {
$return = <value>
}
bb7: {
bb6: {
}
bb8: {
bb7: {
Unreachable()
}
bb8: {
$return = <value>
}
bb9: {
}
bb10: {
Unreachable()
}
bb11: {
}