Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf(transformer): nullish coalescing operator transform use SparseStack #5942

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions crates/oxc_transformer/src/es2020/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ impl<'a> ES2020<'a> {
}

impl<'a> Traverse<'a> for ES2020<'a> {
#[inline] // Inline because it's no-op in release mode
fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
if self.options.nullish_coalescing_operator {
self.nullish_coalescing_operator.exit_program(program, ctx);
}
}

fn enter_statements(
&mut self,
statements: &mut Vec<'a, Statement<'a>>,
Expand Down
26 changes: 16 additions & 10 deletions crates/oxc_transformer/src/es2020/nullish_coalescing_operator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,31 @@ use oxc_span::SPAN;
use oxc_syntax::operator::{AssignmentOperator, BinaryOperator, LogicalOperator};
use oxc_traverse::{Ancestor, Traverse, TraverseCtx};

use crate::context::Ctx;
use crate::{context::Ctx, helpers::stack::SparseStack};

pub struct NullishCoalescingOperator<'a> {
_ctx: Ctx<'a>,
var_declarations: std::vec::Vec<Vec<'a, VariableDeclarator<'a>>>,
var_declarations: SparseStack<Vec<'a, VariableDeclarator<'a>>>,
}

impl<'a> NullishCoalescingOperator<'a> {
pub fn new(ctx: Ctx<'a>) -> Self {
Self { _ctx: ctx, var_declarations: vec![] }
Self { _ctx: ctx, var_declarations: SparseStack::new() }
}
}

impl<'a> Traverse<'a> for NullishCoalescingOperator<'a> {
fn enter_statements(&mut self, _stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
self.var_declarations.push(ctx.ast.vec());
#[inline] // Inline because it's no-op in release mode
fn exit_program(&mut self, _program: &mut Program<'a>, _ctx: &mut TraverseCtx<'a>) {
debug_assert!(self.var_declarations.is_empty());
}

fn enter_statements(
&mut self,
_stmts: &mut Vec<'a, Statement<'a>>,
_ctx: &mut TraverseCtx<'a>,
) {
self.var_declarations.push(None);
}

fn exit_statements(
Expand All @@ -59,9 +68,7 @@ impl<'a> Traverse<'a> for NullishCoalescingOperator<'a> {
ctx: &mut TraverseCtx<'a>,
) {
if let Some(declarations) = self.var_declarations.pop() {
if declarations.is_empty() {
return;
}
debug_assert!(!declarations.is_empty());
let variable = ctx.ast.alloc_variable_declaration(
SPAN,
VariableDeclarationKind::Var,
Expand Down Expand Up @@ -151,8 +158,7 @@ impl<'a> Traverse<'a> for NullishCoalescingOperator<'a> {
} else {
let kind = VariableDeclarationKind::Var;
self.var_declarations
.last_mut()
.unwrap()
.get_mut_or_init(|| ctx.ast.vec())
.push(ctx.ast.variable_declarator(SPAN, kind, id, None, false));
}

Expand Down
26 changes: 22 additions & 4 deletions crates/oxc_transformer/src/helpers/stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ impl<T> SparseStack<T> {
if has_value {
debug_assert!(!self.values.is_empty());
// SAFETY: Last `self.has_values` is only `true` if there's a corresponding value in `self.values`.
// This invariant is maintained in `push`, `take`, and `get_or_init`.
// This invariant is maintained in `push`, `take`, `get_or_init`, and `get_mut_or_init`.
// We maintain it here too because we just popped from `self.has_values`, so that `true`
// has been consumed at the same time we consume its corresponding value from `self.values`.
let value = unsafe { self.values.pop().unwrap_unchecked() };
Expand All @@ -70,7 +70,7 @@ impl<T> SparseStack<T> {

debug_assert!(!self.values.is_empty());
// SAFETY: Last `self.has_values` is only `true` if there's a corresponding value in `self.values`.
// This invariant is maintained in `push`, `pop`, and `get_or_init`.
// This invariant is maintained in `push`, `pop`, `get_or_init`, and `get_mut_or_init`.
// We maintain it here too because we just set last `self.has_values` to `false`
// at the same time as we consume the corresponding value from `self.values`.
let value = unsafe { self.values.pop().unwrap_unchecked() };
Expand All @@ -94,12 +94,31 @@ impl<T> SparseStack<T> {

debug_assert!(!self.values.is_empty());
// SAFETY: Last `self.has_values` is only `true` if there's a corresponding value in `self.values`.
// This invariant is maintained in `push`, `pop`, and `take`.
// This invariant is maintained in `push`, `pop`, `take`, and `get_mut_or_init`.
// Here either last `self.has_values` was already `true`, or it's just been set to `true`
// and a value pushed to `self.values` above.
unsafe { self.values.last().unwrap_unchecked() }
}

/// Initialize the value for top entry on the stack, if it has no value already.
/// Return mutable reference to value.
///
/// # Panics
/// Panics if the stack is empty.
pub fn get_mut_or_init<I: FnOnce() -> T>(&mut self, init: I) -> &mut T {
let has_value = self.has_values.last_mut().unwrap();
if !*has_value {
*has_value = true;
self.values.push(init());
}

// SAFETY: Last `self.has_values` is only `true` if there's a corresponding value in `self.values`.
// This invariant is maintained in `push`, `pop`, `take`, and `get_or_init`.
// Here either last `self.has_values` was already `true`, or it's just been set to `true`
// and a value pushed to `self.values` above.
unsafe { self.values.last_mut().unwrap_unchecked() }
}

/// Get number of entries on the stack.
#[inline]
pub fn len(&self) -> usize {
Expand All @@ -108,7 +127,6 @@ impl<T> SparseStack<T> {

/// Returns `true` if stack is empty.
#[inline]
#[expect(dead_code)]
pub fn is_empty(&self) -> bool {
self.has_values.is_empty()
}
Expand Down
1 change: 1 addition & 0 deletions crates/oxc_transformer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ impl<'a> Traverse<'a> for Transformer<'a> {
fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
self.x1_react.exit_program(program, ctx);
self.x0_typescript.exit_program(program, ctx);
self.x2_es2020.exit_program(program, ctx);
self.x3_es2015.exit_program(program, ctx);
}

Expand Down