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); 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>) { fn visit_for_statement(&mut self, stmt: &ForStatement<'a>) {
let kind = AstKind::ForStatement(self.alloc(stmt)); let kind = AstKind::ForStatement(self.alloc(stmt));
let is_lexical_declaration = let is_lexical_declaration =

View file

@ -5,5 +5,20 @@ input_file: crates/oxc_semantic/tests/cfg_fixtures/assignment_operators.js
--- ---
digraph { digraph {
0 [ label = ""] 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: { bb0: {
} }
bb1: {
}
bb2: {
}
bb3: {
}
bb4: {
}
bb5: {
}
bb6: {
}

View file

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

View file

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

View file

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

View file

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

View file

@ -5,5 +5,10 @@ input_file: crates/oxc_semantic/tests/cfg_fixtures/infix_operators.js
--- ---
digraph { digraph {
0 [ label = ""] 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: { bb0: {
} }
bb1: {
}
bb2: {
}

View file

@ -11,14 +11,29 @@ digraph {
4 [ label = ""] 4 [ label = ""]
5 [ label = ""] 5 [ label = ""]
6 [ label = ""] 6 [ label = ""]
1 -> 2 [ ] 7 [ label = ""]
8 [ label = ""]
9 [ label = ""]
10 [ label = ""]
11 [ label = ""]
12 [ label = ""]
0 -> 1 [ ] 0 -> 1 [ ]
0 -> 2 [ ] 0 -> 2 [ ]
1 -> 2 [ ]
3 -> 4 [ ] 3 -> 4 [ ]
2 -> 3 [ ] 2 -> 3 [ ]
2 -> 4 [ ] 2 -> 4 [ ]
5 -> 6 [ ]
4 -> 5 [ ] 4 -> 5 [ ]
4 -> 6 [ ] 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: { bb6: {
} }
bb7: {
}
bb8: {
}
bb9: {
}
bb10: {
}
bb11: {
}
bb12: {
}

View file

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

View file

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