Skip to content
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
74 changes: 74 additions & 0 deletions crates/oxc_data_structures/src/stack/non_empty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,22 @@ impl<T> NonEmptyStack<T> {
Self { cursor: start, start, end }
}

/// Get reference to first value on stack.
#[inline]
pub fn first(&self) -> &T {
// SAFETY: All methods ensure stack is never empty, so `start` always points to
// a valid initialized `T`. `start` is always aligned for `T`.
unsafe { self.start.as_ref() }
}

/// Get mutable reference to first value on stack.
#[inline]
pub fn first_mut(&mut self) -> &mut T {
// SAFETY: All methods ensure stack is never empty, so `start` always points to
// a valid initialized `T`. `start` is always aligned for `T`.
unsafe { self.start.as_mut() }
}

/// Get reference to last value on stack.
#[inline]
pub fn last(&self) -> &T {
Expand Down Expand Up @@ -545,6 +561,64 @@ mod tests {
stack.pop();
}

#[test]
fn first() {
let mut stack = NonEmptyStack::new(10u64);
assert_len_cap_last!(stack, 1, 4, &10);
assert_eq!(stack.first(), &10);

// Make stack grow
stack.push(20);
stack.push(30);
stack.push(40);
stack.push(50);
assert_len_cap_last!(stack, 5, 8, &50);
assert_eq!(stack.first(), &10);

// Shrink stack back to just 1 entry
stack.pop();
stack.pop();
stack.pop();
stack.pop();
assert_len_cap_last!(stack, 1, 8, &10);
assert_eq!(stack.first(), &10);
}

#[test]
fn first_mut() {
let mut stack = NonEmptyStack::new(10u64);
assert_len_cap_last!(stack, 1, 4, &10);
assert_eq!(stack.first_mut(), &mut 10);

*stack.first_mut() = 11;
assert_eq!(stack[0], 11);
assert_eq!(stack.first_mut(), &mut 11);

// Make stack grow
stack.push(20);
stack.push(30);
stack.push(40);
stack.push(50);
assert_len_cap_last!(stack, 5, 8, &50);
assert_eq!(stack.first_mut(), &mut 11);

*stack.first_mut() = 12;
assert_eq!(stack[0], 12);
assert_eq!(stack.first_mut(), &mut 12);

// Shrink stack back to just 1 entry
stack.pop();
stack.pop();
stack.pop();
stack.pop();
assert_len_cap_last!(stack, 1, 8, &12);
assert_eq!(stack.first_mut(), &mut 12);

*stack.first_mut() = 13;
assert_eq!(stack[0], 13);
assert_eq!(stack.first_mut(), &mut 13);
}

#[test]
fn last_mut() {
let mut stack = NonEmptyStack::new(10u64);
Expand Down
30 changes: 30 additions & 0 deletions crates/oxc_data_structures/src/stack/sparse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,36 @@ impl<T> SparseStack<T> {
}
}

/// Get reference to value of first entry on the stack.
#[inline]
pub fn first(&self) -> Option<&T> {
let has_value = *self.has_values.first();
if has_value {
debug_assert!(!self.values.is_empty());
// SAFETY: First `self.has_values` is only `true` if there's a corresponding value in `self.values`.
// This invariant is maintained in `push`, `pop`, `take_last`, `last_or_init`, and `last_mut_or_init`.
let value = unsafe { self.values.get_unchecked(0) };
Some(value)
} else {
None
}
}

/// Get mutable reference to value of first entry on the stack.
#[inline]
pub fn first_mut(&mut self) -> Option<&mut T> {
let has_value = *self.has_values.first();
if has_value {
debug_assert!(!self.values.is_empty());
// SAFETY: First `self.has_values` is only `true` if there's a corresponding value in `self.values`.
// This invariant is maintained in `push`, `pop`, `take_last`, `last_or_init`, and `last_mut_or_init`.
let value = unsafe { self.values.get_unchecked_mut(0) };
Some(value)
} else {
None
}
}

/// Get reference to value of last entry on the stack.
#[inline]
pub fn last(&self) -> Option<&T> {
Expand Down
6 changes: 6 additions & 0 deletions crates/oxc_data_structures/src/stack/standard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,12 @@ impl<T> Stack<T> {
Self { cursor: start, start, end }
}

// Note: There is no need to implement `first` and `first_mut` methods.
// `NonEmptyStack` can make those methods infallible, but `Stack` can't because `Stack` can be empty.
// `std`'s `first` and `first_mut` methods available via `Deref` / `DerefMut` to a `&[T]` / `&mut[T]`
// are just as efficient as a hand-written version.
// https://godbolt.org/z/rjb1dzob1

/// Get reference to last value on stack.
#[inline]
pub fn last(&self) -> Option<&T> {
Expand Down
10 changes: 5 additions & 5 deletions crates/oxc_transformer/src/common/arrow_function_converter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,18 +194,18 @@ impl<'a> Traverse<'a> for ArrowFunctionConverter<'a> {
);

debug_assert!(self.this_var_stack.len() == 1);
debug_assert!(self.this_var_stack.last().is_none());
debug_assert!(self.this_var_stack.first().is_none());
debug_assert!(self.arguments_var_stack.len() == 1);
debug_assert!(self.arguments_var_stack.last().is_none());
debug_assert!(self.arguments_var_stack.first().is_none());
debug_assert!(self.constructor_super_stack.len() == 1);
// TODO: This assertion currently failing because we don't handle `super` in arrow functions
// in class static properties correctly.
// e.g. `class C { static f = () => super.prop; }`
// debug_assert!(self.constructor_super_stack.last() == &false);
// debug_assert!(self.constructor_super_stack.first() == &false);
debug_assert!(self.super_methods_stack.len() == 1);
debug_assert!(self.super_methods_stack.last().is_empty());
debug_assert!(self.super_methods_stack.first().is_empty());
debug_assert!(self.super_needs_transform_stack.len() == 1);
debug_assert!(self.super_needs_transform_stack.last() == &false);
debug_assert!(self.super_needs_transform_stack.first() == &false);
}

fn enter_function(&mut self, func: &mut Function<'a>, ctx: &mut TraverseCtx<'a>) {
Expand Down
6 changes: 2 additions & 4 deletions crates/oxc_transformer/src/typescript/enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -574,9 +574,7 @@ impl IdentifierReferenceRename<'_, '_> {
// }
// }
// ```
//
// `NonEmptyStack` guarantees that the stack is not empty.
*self.scope_stack.first().unwrap() == symbol_scope_id
*self.scope_stack.first() == symbol_scope_id
// The resolved symbol is declared outside the enum,
// and we have checked that the name exists in previous_enum_members:
//
Expand All @@ -586,7 +584,7 @@ impl IdentifierReferenceRename<'_, '_> {
// enum Foo { B = A }
// ^ This should be renamed to Foo.A
// ```
|| !self.scope_stack.contains(&symbol_scope_id)
|| !self.scope_stack.contains(&symbol_scope_id)
}
}

Expand Down
Loading