Skip to content

Commit 2ccf725

Browse files
committed
perf(semantic): remove function stack (#12676)
Only purpose of `function_stack` in `SemanticBuilder` is to track the current function, in order to set `NodeFlags::HasYield` on it if it contains `yield`. #12630 moved enter and exit logic into `visit_function`. So now we don't need a `Stack` to track the current function, we can use vars on the actual stack. Also, it's unnecessary to update current function when entering/exiting arrow functions, as they can't contain `yield`.
1 parent 5f50bc3 commit 2ccf725

File tree

2 files changed

+19
-16
lines changed

2 files changed

+19
-16
lines changed

crates/oxc_semantic/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ oxc_allocator = { workspace = true }
2323
oxc_ast = { workspace = true }
2424
oxc_ast_visit = { workspace = true }
2525
oxc_cfg = { workspace = true }
26-
oxc_data_structures = { workspace = true, features = ["assert_unchecked", "stack"] }
26+
oxc_data_structures = { workspace = true, features = ["assert_unchecked"] }
2727
oxc_diagnostics = { workspace = true }
2828
oxc_ecmascript = { workspace = true }
2929
oxc_index = { workspace = true }

crates/oxc_semantic/src/builder.rs

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ use oxc_cfg::{
1414
ControlFlowGraphBuilder, CtxCursor, CtxFlags, EdgeType, ErrorEdgeKind, InstructionKind,
1515
IterationInstructionKind, ReturnInstructionKind,
1616
};
17-
use oxc_data_structures::stack::Stack;
1817
use oxc_diagnostics::OxcDiagnostic;
1918
use oxc_span::{Atom, SourceType, Span};
2019
use oxc_syntax::{
@@ -66,8 +65,9 @@ pub struct SemanticBuilder<'a> {
6665
pub(crate) current_node_id: NodeId,
6766
pub(crate) current_node_flags: NodeFlags,
6867
pub(crate) current_scope_id: ScopeId,
69-
/// Stores current `AstKind::Function` and `AstKind::ArrowFunctionExpression` during AST visit
70-
pub(crate) function_stack: Stack<NodeId>,
68+
/// `NodeId` of current `Function` (not including arrow functions).
69+
/// When not in a function, is `NodeId` of `Program`.
70+
pub(crate) current_function_node_id: NodeId,
7171
pub(crate) module_instance_state_cache: FxHashMap<Address, ModuleInstanceState>,
7272
current_reference_flags: ReferenceFlags,
7373
pub(crate) hoisting_variables: FxHashMap<ScopeId, FxHashMap<Atom<'a>, SymbolId>>,
@@ -121,7 +121,7 @@ impl<'a> SemanticBuilder<'a> {
121121
current_node_flags: NodeFlags::empty(),
122122
current_reference_flags: ReferenceFlags::empty(),
123123
current_scope_id,
124-
function_stack: Stack::with_capacity(16),
124+
current_function_node_id: NodeId::ROOT,
125125
module_instance_state_cache: FxHashMap::default(),
126126
nodes: AstNodes::default(),
127127
hoisting_variables: FxHashMap::default(),
@@ -357,12 +357,6 @@ impl<'a> SemanticBuilder<'a> {
357357
self.current_scope_flags().is_strict_mode()
358358
}
359359

360-
pub(crate) fn set_function_node_flags(&mut self, flags: NodeFlags) {
361-
if let Some(current_function) = self.function_stack.last() {
362-
*self.nodes.get_node_mut(*current_function).flags_mut() |= flags;
363-
}
364-
}
365-
366360
/// Declares a `Symbol` for the node, adds it to symbol table, and binds it to the scope.
367361
///
368362
/// includes: the `SymbolFlags` that node has in addition to its declaration type (eg: export, ambient, etc.)
@@ -654,6 +648,9 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
654648
// as scope depth must remain >= 1.
655649

656650
self.leave_node(kind);
651+
652+
// Check `current_function_node_id` has been reset to as it was at start
653+
debug_assert!(self.current_function_node_id == NodeId::ROOT);
657654
}
658655

659656
fn visit_break_statement(&mut self, stmt: &BreakStatement<'a>) {
@@ -1613,10 +1610,14 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
16131610

16141611
let kind = AstKind::Function(self.alloc(func));
16151612
self.enter_node(kind);
1616-
self.function_stack.push(self.current_node_id);
1613+
1614+
let parent_function_node_id = self.current_function_node_id;
1615+
self.current_function_node_id = self.current_node_id;
1616+
16171617
if func.is_declaration() {
16181618
func.bind(self);
16191619
}
1620+
16201621
self.enter_scope(
16211622
{
16221623
let mut flags = flags;
@@ -1693,7 +1694,8 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
16931694

16941695
self.leave_scope();
16951696
self.leave_node(kind);
1696-
self.function_stack.pop();
1697+
1698+
self.current_function_node_id = parent_function_node_id;
16971699
}
16981700

16991701
fn visit_arrow_function_expression(&mut self, expr: &ArrowFunctionExpression<'a>) {
@@ -1712,7 +1714,6 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
17121714

17131715
let kind = AstKind::ArrowFunctionExpression(self.alloc(expr));
17141716
self.enter_node(kind);
1715-
self.function_stack.push(self.current_node_id);
17161717
self.enter_scope(
17171718
{
17181719
let mut flags = ScopeFlags::Function | ScopeFlags::Arrow;
@@ -1775,7 +1776,6 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
17751776
/* cfg */
17761777

17771778
self.leave_node(kind);
1778-
self.function_stack.pop();
17791779
self.leave_scope();
17801780
}
17811781

@@ -2039,7 +2039,10 @@ impl<'a> SemanticBuilder<'a> {
20392039
}
20402040
}
20412041
AstKind::YieldExpression(_) => {
2042-
self.set_function_node_flags(NodeFlags::HasYield);
2042+
// If not in a function, `current_function_node_id` is `NodeId` of `Program`.
2043+
// But it shouldn't be possible for `yield` to at top level - that's a parse error.
2044+
*self.nodes.get_node_mut(self.current_function_node_id).flags_mut() |=
2045+
NodeFlags::HasYield;
20432046
}
20442047
AstKind::CallExpression(call_expr) => {
20452048
if !call_expr.optional && call_expr.callee.is_specific_id("eval") {

0 commit comments

Comments
 (0)