Skip to content

Commit

Permalink
feat(semantic/cfg): add Condition instruction. (#3567)
Browse files Browse the repository at this point in the history
  • Loading branch information
rzvxa committed Jun 13, 2024
1 parent e6d6870 commit 5301909
Show file tree
Hide file tree
Showing 28 changed files with 299 additions and 168 deletions.
1 change: 1 addition & 0 deletions crates/oxc_linter/src/rules/eslint/getter_return.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ impl GetterReturn {
| InstructionKind::Break(_)
| InstructionKind::Continue(_)
| InstructionKind::Iteration(_)
| InstructionKind::Condition
| InstructionKind::Statement => {}
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/oxc_linter/src/rules/react/require_render_return.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ fn contains_return_statement<'a>(node: &AstNode<'a>, ctx: &LintContext<'a>) -> b
| InstructionKind::Break(_)
| InstructionKind::Continue(_)
| InstructionKind::Iteration(_)
| InstructionKind::Condition
| InstructionKind::Statement => {}
}
}
Expand Down
61 changes: 45 additions & 16 deletions crates/oxc_semantic/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,10 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
let start_of_condition_graph_ix = self.cfg.new_basic_block_normal();
/* cfg */

self.record_ast_nodes();
self.visit_expression(&stmt.test);
let test_node = self.retrieve_recorded_ast_nodes().into_iter().next();
self.cfg.append_condition_to(start_of_condition_graph_ix, test_node);

/* cfg */
let end_of_condition_graph_ix = self.cfg.current_node_ix;
Expand Down Expand Up @@ -675,10 +678,18 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
let kind = AstKind::ConditionalExpression(self.alloc(expr));
self.enter_node(kind);

/* cfg - condition basic block */
let before_conditional_graph_ix = self.cfg.current_node_ix;
let start_of_condition_graph_ix = self.cfg.new_basic_block_normal();
/* cfg */

self.record_ast_nodes();
self.visit_expression(&expr.test);
let test_node = self.retrieve_recorded_ast_nodes().into_iter().next();
self.cfg.append_condition_to(start_of_condition_graph_ix, test_node);

/* cfg */
let before_conditional_expr_graph_ix = self.cfg.current_node_ix;
let after_condition_graph_ix = self.cfg.current_node_ix;
// conditional expression basic block
let before_consequent_expr_graph_ix = self.cfg.new_basic_block_normal();
/* cfg */
Expand All @@ -696,25 +707,28 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
let after_alternate_graph_ix = self.cfg.current_node_ix;
/* bb after conditional expression joins consequent and alternate */
let after_conditional_graph_ix = self.cfg.new_basic_block_normal();
/* cfg */

self.cfg.add_edge(
before_conditional_graph_ix,
start_of_condition_graph_ix,
EdgeType::Normal,
);

self.cfg.add_edge(
after_consequent_expr_graph_ix,
after_conditional_graph_ix,
EdgeType::Normal,
);
self.cfg.add_edge(
before_conditional_expr_graph_ix,
after_condition_graph_ix,
before_consequent_expr_graph_ix,
EdgeType::Normal,
EdgeType::Jump,
);

self.cfg.add_edge(
before_conditional_expr_graph_ix,
start_alternate_graph_ix,
EdgeType::Normal,
);
self.cfg.add_edge(after_condition_graph_ix, start_alternate_graph_ix, EdgeType::Normal);
self.cfg.add_edge(after_alternate_graph_ix, after_conditional_graph_ix, EdgeType::Normal);
/* cfg */

self.leave_node(kind);
}

Expand All @@ -735,7 +749,10 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
let test_graph_ix = self.cfg.new_basic_block_normal();
/* cfg */
if let Some(test) = &stmt.test {
self.record_ast_nodes();
self.visit_expression(test);
let test_node = self.retrieve_recorded_ast_nodes().into_iter().next();
self.cfg.append_condition_to(test_graph_ix, test_node);
}

/* cfg */
Expand Down Expand Up @@ -916,10 +933,18 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
let kind = AstKind::IfStatement(self.alloc(stmt));
self.enter_node(kind);

/* cfg - condition basic block */
let before_if_stmt_graph_ix = self.cfg.current_node_ix;
let start_of_condition_graph_ix = self.cfg.new_basic_block_normal();
/* cfg */

self.record_ast_nodes();
self.visit_expression(&stmt.test);
let test_node = self.retrieve_recorded_ast_nodes().into_iter().next();
self.cfg.append_condition_to(start_of_condition_graph_ix, test_node);

/* cfg */
let before_if_stmt_graph_ix = self.cfg.current_node_ix;
let after_test_graph_ix = self.cfg.current_node_ix;
let before_consequent_stmt_graph_ix = self.cfg.new_basic_block_normal();
/* cfg */

Expand All @@ -944,13 +969,11 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
/* cfg - bb after if statement joins consequent and alternate */
let after_if_graph_ix = self.cfg.new_basic_block_normal();

self.cfg.add_edge(before_if_stmt_graph_ix, start_of_condition_graph_ix, EdgeType::Normal);

self.cfg.add_edge(after_consequent_stmt_graph_ix, after_if_graph_ix, EdgeType::Normal);

self.cfg.add_edge(
before_if_stmt_graph_ix,
before_consequent_stmt_graph_ix,
EdgeType::Normal,
);
self.cfg.add_edge(after_test_graph_ix, before_consequent_stmt_graph_ix, EdgeType::Jump);

if let Some((start_of_alternate_stmt_graph_ix, after_alternate_stmt_graph_ix)) =
else_graph_ix
Expand Down Expand Up @@ -1105,13 +1128,16 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
self.enter_node(kind);

if let Some(expr) = &case.test {
self.record_ast_nodes();
self.visit_expression(expr);
let test_node = self.retrieve_recorded_ast_nodes().into_iter().next();
self.cfg.append_condition_to(self.cfg.current_node_ix, test_node);
}

/* cfg */
let after_test_graph_ix = self.cfg.current_node_ix;
let statements_in_switch_graph_ix = self.cfg.new_basic_block_normal();
self.cfg.add_edge(after_test_graph_ix, statements_in_switch_graph_ix, EdgeType::Normal);
self.cfg.add_edge(after_test_graph_ix, statements_in_switch_graph_ix, EdgeType::Jump);
/* cfg */

self.visit_statements(&case.consequent);
Expand Down Expand Up @@ -1274,7 +1300,10 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
let condition_graph_ix = self.cfg.new_basic_block_normal();
/* cfg */

self.record_ast_nodes();
self.visit_expression(&stmt.test);
let test_node = self.retrieve_recorded_ast_nodes().into_iter().next();
self.cfg.append_condition_to(condition_graph_ix, test_node);

/* cfg - body basic block */
let body_graph_ix = self.cfg.new_basic_block_normal();
Expand Down
24 changes: 21 additions & 3 deletions crates/oxc_semantic/src/control_flow/builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,15 @@ impl<'a> ControlFlowGraphBuilder<'a> {
ControlFlowGraph { graph: self.graph, basic_blocks: self.basic_blocks }
}

/// # Panics
pub fn current_basic_block(&mut self) -> &mut BasicBlock {
self.basic_block_mut(self.current_node_ix)
}

/// # Panics
pub fn basic_block_mut(&mut self, basic_block: BasicBlockId) -> &mut BasicBlock {
let idx = *self
.graph
.node_weight(self.current_node_ix)
.node_weight(basic_block)
.expect("expected `self.current_node_ix` to be a valid node index in self.graph");
self.basic_blocks
.get_mut(idx)
Expand Down Expand Up @@ -145,6 +149,10 @@ impl<'a> ControlFlowGraphBuilder<'a> {
);
}

pub fn append_condition_to(&mut self, block: BasicBlockId, node: Option<AstNodeId>) {
self.push_instruction_to(block, InstructionKind::Condition, node);
}

pub fn append_iteration(&mut self, node: Option<AstNodeId>, kind: IterationInstructionKind) {
self.push_instruction(InstructionKind::Iteration(kind), node);
}
Expand Down Expand Up @@ -195,7 +203,17 @@ impl<'a> ControlFlowGraphBuilder<'a> {

#[inline]
pub(self) fn push_instruction(&mut self, kind: InstructionKind, node_id: Option<AstNodeId>) {
self.current_basic_block().instructions.push(Instruction { kind, node_id });
self.push_instruction_to(self.current_node_ix, kind, node_id);
}

#[inline]
pub(self) fn push_instruction_to(
&mut self,
block: BasicBlockId,
kind: InstructionKind,
node_id: Option<AstNodeId>,
) {
self.basic_block_mut(block).instructions.push(Instruction { kind, node_id });
}

#[must_use]
Expand Down
22 changes: 20 additions & 2 deletions crates/oxc_semantic/src/control_flow/dot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,17 @@ impl<'a, 'b> DebugDotContext<'a, 'b> {
fn debug_ast_kind(self, id: AstNodeId) -> String {
self.0.kind(id).debug_name().into_owned()
}

fn try_eval_literal(self, id: AstNodeId) -> Option<String> {
match self.0.kind(id) {
AstKind::NumericLiteral(lit) => Some(lit.value.to_string()),
AstKind::BooleanLiteral(lit) => Some(lit.value.to_string()),
AstKind::StringLiteral(lit) => Some(lit.value.to_string()),
AstKind::BigintLiteral(lit) => Some(lit.raw.to_string()),
AstKind::NullLiteral(_) => Some("null".to_string()),
_ => None,
}
}
}

impl<'a, 'b> From<&'b AstNodes<'a>> for DebugDotContext<'a, 'b> {
Expand Down Expand Up @@ -76,12 +87,13 @@ impl DisplayDot for Instruction {
InstructionKind::Statement => "statement",
InstructionKind::Unreachable => "unreachable",
InstructionKind::Throw => "throw",
InstructionKind::Condition => "condition",
InstructionKind::Iteration(IterationInstructionKind::Of) => "iteration <of>",
InstructionKind::Iteration(IterationInstructionKind::In) => "iteration <in>",
InstructionKind::Break(LabeledInstruction::Labeled) => "break <label>",
InstructionKind::Break(LabeledInstruction::Unlabeled) => "break",
InstructionKind::Continue(LabeledInstruction::Labeled) => "continue <label>",
InstructionKind::Continue(LabeledInstruction::Unlabeled) => "continue",
InstructionKind::Iteration(IterationInstructionKind::Of) => "iteration <of>",
InstructionKind::Iteration(IterationInstructionKind::In) => "iteration <in>",
InstructionKind::Return(ReturnInstructionKind::ImplicitUndefined) => {
"return <implicit undefined>"
}
Expand Down Expand Up @@ -136,6 +148,12 @@ impl DebugDot for Instruction {
}
InstructionKind::Unreachable => "unreachable".to_string(),
InstructionKind::Throw => "throw".to_string(),
InstructionKind::Condition => self.node_id.map_or("None".to_string(), |id| {
format!(
"Condition({})",
ctx.try_eval_literal(id).unwrap_or_else(|| ctx.debug_ast_kind(id))
)
}),
InstructionKind::Iteration(ref kind) => {
format!(
"Iteration({} {} {})",
Expand Down
1 change: 1 addition & 0 deletions crates/oxc_semantic/src/control_flow/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ pub enum InstructionKind {
Break(LabeledInstruction),
Continue(LabeledInstruction),
Throw,
Condition,
Iteration(IterationInstructionKind),
}
#[derive(Debug, Clone)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,20 @@ digraph {
1 [ label = "VariableDeclaration" ]
2 [ label = "" ]
3 [ label = "ExpressionStatement" ]
4 [ label = "" ]
4 [ label = "Condition(CallExpression)" ]
5 [ label = "" ]
6 [ label = "" ]
7 [ label = "" ]
1 -> 0 [ label = "Error(Implicit)" ]
3 -> 2 [ label = "Error(Implicit)" ]
1 -> 3 [ label = "NewFunction" ]
4 -> 2 [ label = "Error(Implicit)" ]
5 -> 2 [ label = "Error(Implicit)" ]
6 -> 2 [ label = "Error(Implicit)" ]
4 -> 6 [ label = "Normal" ]
7 -> 2 [ label = "Error(Implicit)" ]
3 -> 4 [ label = "Normal" ]
3 -> 5 [ label = "Normal" ]
5 -> 6 [ label = "Normal" ]
5 -> 7 [ label = "Normal" ]
4 -> 5 [ label = "Jump" ]
4 -> 6 [ label = "Normal" ]
6 -> 7 [ label = "Normal" ]
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ bb3: {
}

bb4: {

condition
}

bb5: {
Expand All @@ -30,3 +30,7 @@ bb5: {
bb6: {

}

bb7: {

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@ input_file: crates/oxc_semantic/tests/integration/cfg_fixtures/conditional_expre
digraph {
0 [ label = "" ]
1 [ label = "VariableDeclaration" ]
2 [ label = "" ]
2 [ label = "Condition(CallExpression)" ]
3 [ label = "" ]
4 [ label = "VariableDeclaration" ]
4 [ label = "" ]
5 [ label = "VariableDeclaration" ]
1 -> 0 [ label = "Error(Implicit)" ]
2 -> 0 [ label = "Error(Implicit)" ]
3 -> 0 [ label = "Error(Implicit)" ]
4 -> 0 [ label = "Error(Implicit)" ]
2 -> 4 [ label = "Normal" ]
5 -> 0 [ label = "Error(Implicit)" ]
1 -> 2 [ label = "Normal" ]
1 -> 3 [ label = "Normal" ]
3 -> 4 [ label = "Normal" ]
3 -> 5 [ label = "Normal" ]
2 -> 3 [ label = "Jump" ]
2 -> 4 [ label = "Normal" ]
4 -> 5 [ label = "Normal" ]
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,17 @@ bb1: {
}

bb2: {

condition
}

bb3: {

}

bb4: {

}

bb5: {
statement
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ digraph {
6 [ label = "DoWhileStatement" ]
7 [ label = "BlockStatement\nbreak" ]
8 [ label = "unreachable" ]
9 [ label = "" ]
9 [ label = "Condition(true)" ]
10 [ label = "" ]
11 [ label = "" ]
12 [ label = "" ]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ bb8: {
}

bb9: {

condition
}

bb10: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ digraph {
1 [ label = "" ]
2 [ label = "" ]
3 [ label = "ForStatement\nVariableDeclaration" ]
4 [ label = "" ]
4 [ label = "Condition(test)" ]
5 [ label = "" ]
6 [ label = "ExpressionStatement" ]
7 [ label = "ExpressionStatement" ]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ bb3: {
}

bb4: {

condition
}

bb5: {
Expand Down
Loading

0 comments on commit 5301909

Please sign in to comment.