mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
feat(semantic/cfg): add iteration instructions. (#3566)
This commit is contained in:
parent
defef057e1
commit
f2dfd667df
11 changed files with 77 additions and 43 deletions
|
|
@ -275,6 +275,7 @@ impl GetterReturn {
|
|||
// Ignore irrelevant elements.
|
||||
| InstructionKind::Break(_)
|
||||
| InstructionKind::Continue(_)
|
||||
| InstructionKind::Iteration(_)
|
||||
| InstructionKind::Statement => {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,6 +126,7 @@ fn contains_return_statement<'a>(node: &AstNode<'a>, ctx: &LintContext<'a>) -> b
|
|||
InstructionKind::Return(ReturnInstructionKind::ImplicitUndefined)
|
||||
| InstructionKind::Break(_)
|
||||
| InstructionKind::Continue(_)
|
||||
| InstructionKind::Iteration(_)
|
||||
| InstructionKind::Statement => {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ use crate::{
|
|||
checker::{EarlyErrorJavaScript, EarlyErrorTypeScript},
|
||||
class::ClassTableBuilder,
|
||||
control_flow::{
|
||||
ControlFlowGraphBuilder, CtxCursor, CtxFlags, EdgeType, ErrorEdgeKind, Register,
|
||||
ReturnInstructionKind,
|
||||
ControlFlowGraphBuilder, CtxCursor, CtxFlags, EdgeType, ErrorEdgeKind,
|
||||
IterationInstructionKind, Register, ReturnInstructionKind,
|
||||
},
|
||||
diagnostics::redeclaration,
|
||||
jsdoc::JSDocBuilder,
|
||||
|
|
@ -71,6 +71,8 @@ pub struct SemanticBuilder<'a> {
|
|||
pub cfg: ControlFlowGraphBuilder<'a>,
|
||||
|
||||
pub class_table_builder: ClassTableBuilder,
|
||||
|
||||
ast_nodes_records: Vec<Vec<AstNodeId>>,
|
||||
}
|
||||
|
||||
pub struct SemanticBuilderReturn<'a> {
|
||||
|
|
@ -105,6 +107,7 @@ impl<'a> SemanticBuilder<'a> {
|
|||
check_syntax_error: false,
|
||||
cfg: ControlFlowGraphBuilder::default(),
|
||||
class_table_builder: ClassTableBuilder::new(),
|
||||
ast_nodes_records: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -207,6 +210,7 @@ impl<'a> SemanticBuilder<'a> {
|
|||
} else {
|
||||
self.nodes.add_node(ast_node, Some(self.current_node_id))
|
||||
};
|
||||
self.record_ast_node();
|
||||
}
|
||||
|
||||
fn pop_ast_node(&mut self) {
|
||||
|
|
@ -215,6 +219,20 @@ impl<'a> SemanticBuilder<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn record_ast_nodes(&mut self) {
|
||||
self.ast_nodes_records.push(Vec::new());
|
||||
}
|
||||
|
||||
fn retrieve_recorded_ast_nodes(&mut self) -> Vec<AstNodeId> {
|
||||
self.ast_nodes_records.pop().expect("there is no ast nodes record to stop.")
|
||||
}
|
||||
|
||||
fn record_ast_node(&mut self) {
|
||||
if let Some(records) = self.ast_nodes_records.last_mut() {
|
||||
records.push(self.current_node_id);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current_scope_flags(&self) -> ScopeFlags {
|
||||
self.scope.get_flags(self.current_scope_id)
|
||||
}
|
||||
|
|
@ -741,7 +759,7 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
|
|||
let after_body_graph_ix = self.cfg.current_node_ix;
|
||||
let after_for_stmt = self.cfg.new_basic_block_normal();
|
||||
self.cfg.add_edge(before_for_graph_ix, test_graph_ix, EdgeType::Normal);
|
||||
self.cfg.add_edge(after_test_graph_ix, before_body_graph_ix, EdgeType::Normal);
|
||||
self.cfg.add_edge(after_test_graph_ix, before_body_graph_ix, EdgeType::Jump);
|
||||
self.cfg.add_edge(after_body_graph_ix, update_graph_ix, EdgeType::Backedge);
|
||||
self.cfg.add_edge(update_graph_ix, test_graph_ix, EdgeType::Backedge);
|
||||
self.cfg.add_edge(after_test_graph_ix, after_for_stmt, EdgeType::Normal);
|
||||
|
|
@ -749,7 +767,7 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
|
|||
self.cfg
|
||||
.ctx(None)
|
||||
.mark_break(after_for_stmt)
|
||||
.mark_continue(test_graph_ix)
|
||||
.mark_continue(update_graph_ix)
|
||||
.resolve_with_upper_label();
|
||||
/* cfg */
|
||||
|
||||
|
|
@ -782,6 +800,7 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
|
|||
stmt.scope_id.set(Some(self.current_scope_id));
|
||||
}
|
||||
self.enter_node(kind);
|
||||
|
||||
self.visit_for_statement_left(&stmt.left);
|
||||
|
||||
/* cfg */
|
||||
|
|
@ -789,12 +808,14 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
|
|||
let start_prepare_cond_graph_ix = self.cfg.new_basic_block_normal();
|
||||
/* cfg */
|
||||
|
||||
self.record_ast_nodes();
|
||||
self.visit_expression(&stmt.right);
|
||||
let right_node = self.retrieve_recorded_ast_nodes().into_iter().next();
|
||||
|
||||
/* cfg */
|
||||
let end_of_prepare_cond_graph_ix = self.cfg.current_node_ix;
|
||||
// this basic block is always empty since there's no update condition in a for-in loop.
|
||||
let basic_block_with_backedge_graph_ix = self.cfg.new_basic_block_normal();
|
||||
let iteration_graph_ix = self.cfg.new_basic_block_normal();
|
||||
self.cfg.append_iteration(right_node, IterationInstructionKind::In);
|
||||
let body_graph_ix = self.cfg.new_basic_block_normal();
|
||||
|
||||
self.cfg.ctx(None).default().allow_break().allow_continue();
|
||||
|
|
@ -808,28 +829,20 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
|
|||
// connect before for statement to the iterable expression
|
||||
self.cfg.add_edge(before_for_stmt_graph_ix, start_prepare_cond_graph_ix, EdgeType::Normal);
|
||||
// connect the end of the iterable expression to the basic block with back edge
|
||||
self.cfg.add_edge(
|
||||
end_of_prepare_cond_graph_ix,
|
||||
basic_block_with_backedge_graph_ix,
|
||||
EdgeType::Normal,
|
||||
);
|
||||
self.cfg.add_edge(end_of_prepare_cond_graph_ix, iteration_graph_ix, EdgeType::Normal);
|
||||
// connect the basic block with back edge to the start of the body
|
||||
self.cfg.add_edge(basic_block_with_backedge_graph_ix, body_graph_ix, EdgeType::Normal);
|
||||
self.cfg.add_edge(iteration_graph_ix, body_graph_ix, EdgeType::Jump);
|
||||
// connect the end of the body back to the basic block
|
||||
// with back edge for the next iteration
|
||||
self.cfg.add_edge(
|
||||
end_of_body_graph_ix,
|
||||
basic_block_with_backedge_graph_ix,
|
||||
EdgeType::Backedge,
|
||||
);
|
||||
self.cfg.add_edge(end_of_body_graph_ix, iteration_graph_ix, EdgeType::Backedge);
|
||||
// connect the basic block with back edge to the basic block after the for loop
|
||||
// for when there are no more iterations left in the iterable
|
||||
self.cfg.add_edge(basic_block_with_backedge_graph_ix, after_for_graph_ix, EdgeType::Normal);
|
||||
self.cfg.add_edge(iteration_graph_ix, after_for_graph_ix, EdgeType::Normal);
|
||||
|
||||
self.cfg
|
||||
.ctx(None)
|
||||
.mark_break(after_for_graph_ix)
|
||||
.mark_continue(basic_block_with_backedge_graph_ix)
|
||||
.mark_continue(iteration_graph_ix)
|
||||
.resolve_with_upper_label();
|
||||
/* cfg */
|
||||
|
||||
|
|
@ -847,6 +860,7 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
|
|||
stmt.scope_id.set(Some(self.current_scope_id));
|
||||
}
|
||||
self.enter_node(kind);
|
||||
|
||||
self.visit_for_statement_left(&stmt.left);
|
||||
|
||||
/* cfg */
|
||||
|
|
@ -854,12 +868,14 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
|
|||
let start_prepare_cond_graph_ix = self.cfg.new_basic_block_normal();
|
||||
/* cfg */
|
||||
|
||||
self.record_ast_nodes();
|
||||
self.visit_expression(&stmt.right);
|
||||
let right_node = self.retrieve_recorded_ast_nodes().into_iter().next();
|
||||
|
||||
/* cfg */
|
||||
let end_of_prepare_cond_graph_ix = self.cfg.current_node_ix;
|
||||
// this basic block is always empty since there's no update condition in a for-of loop.
|
||||
let basic_block_with_backedge_graph_ix = self.cfg.new_basic_block_normal();
|
||||
let iteration_graph_ix = self.cfg.new_basic_block_normal();
|
||||
self.cfg.append_iteration(right_node, IterationInstructionKind::Of);
|
||||
let body_graph_ix = self.cfg.new_basic_block_normal();
|
||||
|
||||
self.cfg.ctx(None).default().allow_break().allow_continue();
|
||||
|
|
@ -873,28 +889,20 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
|
|||
// connect before for statement to the iterable expression
|
||||
self.cfg.add_edge(before_for_stmt_graph_ix, start_prepare_cond_graph_ix, EdgeType::Normal);
|
||||
// connect the end of the iterable expression to the basic block with back edge
|
||||
self.cfg.add_edge(
|
||||
end_of_prepare_cond_graph_ix,
|
||||
basic_block_with_backedge_graph_ix,
|
||||
EdgeType::Normal,
|
||||
);
|
||||
self.cfg.add_edge(end_of_prepare_cond_graph_ix, iteration_graph_ix, EdgeType::Normal);
|
||||
// connect the basic block with back edge to the start of the body
|
||||
self.cfg.add_edge(basic_block_with_backedge_graph_ix, body_graph_ix, EdgeType::Normal);
|
||||
self.cfg.add_edge(iteration_graph_ix, body_graph_ix, EdgeType::Jump);
|
||||
// connect the end of the body back to the basic block
|
||||
// with back edge for the next iteration
|
||||
self.cfg.add_edge(
|
||||
end_of_body_graph_ix,
|
||||
basic_block_with_backedge_graph_ix,
|
||||
EdgeType::Backedge,
|
||||
);
|
||||
self.cfg.add_edge(end_of_body_graph_ix, iteration_graph_ix, EdgeType::Backedge);
|
||||
// connect the basic block with back edge to the basic block after the for loop
|
||||
// for when there are no more iterations left in the iterable
|
||||
self.cfg.add_edge(basic_block_with_backedge_graph_ix, after_for_graph_ix, EdgeType::Normal);
|
||||
self.cfg.add_edge(iteration_graph_ix, after_for_graph_ix, EdgeType::Normal);
|
||||
|
||||
self.cfg
|
||||
.ctx(None)
|
||||
.mark_break(after_for_graph_ix)
|
||||
.mark_continue(basic_block_with_backedge_graph_ix)
|
||||
.mark_continue(iteration_graph_ix)
|
||||
.resolve_with_upper_label();
|
||||
/* cfg */
|
||||
|
||||
|
|
@ -1281,7 +1289,7 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
|
|||
let after_while_graph_ix = self.cfg.new_basic_block_normal();
|
||||
|
||||
self.cfg.add_edge(before_while_stmt_graph_ix, condition_graph_ix, EdgeType::Normal);
|
||||
self.cfg.add_edge(condition_graph_ix, body_graph_ix, EdgeType::Normal);
|
||||
self.cfg.add_edge(condition_graph_ix, body_graph_ix, EdgeType::Jump);
|
||||
self.cfg.add_edge(after_body_graph_ix, condition_graph_ix, EdgeType::Backedge);
|
||||
self.cfg.add_edge(condition_graph_ix, after_while_graph_ix, EdgeType::Normal);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ pub use context::{CtxCursor, CtxFlags};
|
|||
|
||||
use super::{
|
||||
AstNodeId, BasicBlock, BasicBlockId, CompactStr, ControlFlowGraph, EdgeType, ErrorEdgeKind,
|
||||
Graph, Instruction, InstructionKind, LabeledInstruction, PreservedExpressionState, Register,
|
||||
Graph, Instruction, InstructionKind, IterationInstructionKind, LabeledInstruction,
|
||||
PreservedExpressionState, Register,
|
||||
};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
|
|
@ -144,6 +145,10 @@ impl<'a> ControlFlowGraphBuilder<'a> {
|
|||
);
|
||||
}
|
||||
|
||||
pub fn append_iteration(&mut self, node: Option<AstNodeId>, kind: IterationInstructionKind) {
|
||||
self.push_instruction(InstructionKind::Iteration(kind), node);
|
||||
}
|
||||
|
||||
pub fn append_throw(&mut self, node: AstNodeId) {
|
||||
self.push_instruction(InstructionKind::Throw, Some(node));
|
||||
self.append_unreachable();
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ use crate::{
|
|||
LabeledInstruction, ReturnInstructionKind,
|
||||
};
|
||||
|
||||
use super::IterationInstructionKind;
|
||||
|
||||
pub trait DisplayDot {
|
||||
fn display_dot(&self) -> String;
|
||||
}
|
||||
|
|
@ -78,6 +80,8 @@ impl DisplayDot for Instruction {
|
|||
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>"
|
||||
}
|
||||
|
|
@ -132,6 +136,15 @@ impl DebugDot for Instruction {
|
|||
}
|
||||
InstructionKind::Unreachable => "unreachable".to_string(),
|
||||
InstructionKind::Throw => "throw".to_string(),
|
||||
InstructionKind::Iteration(ref kind) => {
|
||||
format!(
|
||||
"Iteration({} {} {})",
|
||||
self.node_id.map_or("None".to_string(), |id| ctx.debug_ast_kind(id)),
|
||||
if matches!(kind, IterationInstructionKind::Of) { "of" } else { "in" },
|
||||
// TODO: at this point we can't evaluate this note. needs access to the graph information.
|
||||
"expr"
|
||||
)
|
||||
}
|
||||
InstructionKind::Break(LabeledInstruction::Labeled) => {
|
||||
let Some(AstKind::BreakStatement(BreakStatement { label: Some(label), .. })) =
|
||||
self.node_id.map(|id| ctx.0.get_node(id)).map(AstNode::kind)
|
||||
|
|
|
|||
|
|
@ -148,8 +148,8 @@ pub enum InstructionKind {
|
|||
Break(LabeledInstruction),
|
||||
Continue(LabeledInstruction),
|
||||
Throw,
|
||||
Iteration(IterationInstructionKind),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ReturnInstructionKind {
|
||||
ImplicitUndefined,
|
||||
|
|
@ -162,6 +162,12 @@ pub enum LabeledInstruction {
|
|||
Unlabeled,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum IterationInstructionKind {
|
||||
Of,
|
||||
In,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum EdgeType {
|
||||
/// Conditional jumps
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ digraph {
|
|||
6 -> 2 [ label = "Error(Implicit)" ]
|
||||
7 -> 2 [ label = "Error(Implicit)" ]
|
||||
3 -> 4 [ label = "Normal" ]
|
||||
4 -> 6 [ label = "Normal" ]
|
||||
4 -> 6 [ label = "Jump" ]
|
||||
6 -> 5 [ label = "Backedge" ]
|
||||
5 -> 4 [ label = "Backedge" ]
|
||||
4 -> 7 [ label = "Normal" ]
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ digraph {
|
|||
2 [ label = "" ]
|
||||
3 [ label = "ForInStatement\nVariableDeclaration" ]
|
||||
4 [ label = "" ]
|
||||
5 [ label = "" ]
|
||||
5 [ label = "Iteration(IdentifierReference(array) in expr)" ]
|
||||
6 [ label = "BlockStatement\nIfStatement" ]
|
||||
7 [ label = "BlockStatement\nExpressionStatement\nbreak" ]
|
||||
8 [ label = "unreachable" ]
|
||||
|
|
@ -45,7 +45,7 @@ digraph {
|
|||
14 -> 2 [ label = "Error(Implicit)" ]
|
||||
3 -> 4 [ label = "Normal" ]
|
||||
4 -> 5 [ label = "Normal" ]
|
||||
5 -> 6 [ label = "Normal" ]
|
||||
5 -> 6 [ label = "Jump" ]
|
||||
13 -> 5 [ label = "Backedge" ]
|
||||
5 -> 14 [ label = "Normal" ]
|
||||
7 -> 14 [ label = "Jump" ]
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ bb4: {
|
|||
}
|
||||
|
||||
bb5: {
|
||||
|
||||
iteration <in>
|
||||
}
|
||||
|
||||
bb6: {
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ digraph {
|
|||
3 -> 9 [ label = "Normal" ]
|
||||
10 -> 0 [ label = "Error(Implicit)" ]
|
||||
1 -> 2 [ label = "Normal" ]
|
||||
2 -> 3 [ label = "Normal" ]
|
||||
2 -> 3 [ label = "Jump" ]
|
||||
9 -> 2 [ label = "Backedge" ]
|
||||
2 -> 10 [ label = "Normal" ]
|
||||
5 -> 2 [ label = "Jump" ]
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ digraph {
|
|||
7 -> 8 [ label = "Unreachable" , style = "dotted" ]
|
||||
9 -> 2 [ label = "Error(Implicit)" ]
|
||||
3 -> 4 [ label = "Normal" ]
|
||||
4 -> 7 [ label = "Normal" ]
|
||||
4 -> 7 [ label = "Jump" ]
|
||||
8 -> 4 [ label = "Backedge" ]
|
||||
4 -> 9 [ label = "Normal" ]
|
||||
10 -> 2 [ label = "Error(Implicit)" ]
|
||||
|
|
|
|||
Loading…
Reference in a new issue