Skip to content

Commit 39b0c9c

Browse files
committed
feat(data_structures): add first and first_mut methods to stack types
1 parent f6b6e70 commit 39b0c9c

File tree

5 files changed

+117
-9
lines changed

5 files changed

+117
-9
lines changed

crates/oxc_data_structures/src/stack/non_empty.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,22 @@ impl<T> NonEmptyStack<T> {
216216
Self { cursor: start, start, end }
217217
}
218218

219+
/// Get reference to first value on stack.
220+
#[inline]
221+
pub fn first(&self) -> &T {
222+
// SAFETY: All methods ensure stack is never empty, so `start` always points to
223+
// a valid initialized `T`. `start` is always aligned for `T`.
224+
unsafe { self.start.as_ref() }
225+
}
226+
227+
/// Get mutable reference to first value on stack.
228+
#[inline]
229+
pub fn first_mut(&mut self) -> &mut T {
230+
// SAFETY: All methods ensure stack is never empty, so `start` always points to
231+
// a valid initialized `T`. `start` is always aligned for `T`.
232+
unsafe { self.start.as_mut() }
233+
}
234+
219235
/// Get reference to last value on stack.
220236
#[inline]
221237
pub fn last(&self) -> &T {
@@ -545,6 +561,64 @@ mod tests {
545561
stack.pop();
546562
}
547563

564+
#[test]
565+
fn first() {
566+
let mut stack = NonEmptyStack::new(10u64);
567+
assert_len_cap_last!(stack, 1, 4, &10);
568+
assert_eq!(stack.first(), &10);
569+
570+
// Make stack grow
571+
stack.push(20);
572+
stack.push(30);
573+
stack.push(40);
574+
stack.push(50);
575+
assert_len_cap_last!(stack, 5, 8, &50);
576+
assert_eq!(stack.first(), &10);
577+
578+
// Shrink stack back to just 1 entry
579+
stack.pop();
580+
stack.pop();
581+
stack.pop();
582+
stack.pop();
583+
assert_len_cap_last!(stack, 1, 8, &10);
584+
assert_eq!(stack.first(), &10);
585+
}
586+
587+
#[test]
588+
fn first_mut() {
589+
let mut stack = NonEmptyStack::new(10u64);
590+
assert_len_cap_last!(stack, 1, 4, &10);
591+
assert_eq!(stack.first_mut(), &mut 10);
592+
593+
*stack.first_mut() = 11;
594+
assert_eq!(stack[0], 11);
595+
assert_eq!(stack.first_mut(), &mut 11);
596+
597+
// Make stack grow
598+
stack.push(20);
599+
stack.push(30);
600+
stack.push(40);
601+
stack.push(50);
602+
assert_len_cap_last!(stack, 5, 8, &50);
603+
assert_eq!(stack.first_mut(), &mut 11);
604+
605+
*stack.first_mut() = 12;
606+
assert_eq!(stack[0], 12);
607+
assert_eq!(stack.first_mut(), &mut 12);
608+
609+
// Shrink stack back to just 1 entry
610+
stack.pop();
611+
stack.pop();
612+
stack.pop();
613+
stack.pop();
614+
assert_len_cap_last!(stack, 1, 8, &12);
615+
assert_eq!(stack.first_mut(), &mut 12);
616+
617+
*stack.first_mut() = 13;
618+
assert_eq!(stack[0], 13);
619+
assert_eq!(stack.first_mut(), &mut 13);
620+
}
621+
548622
#[test]
549623
fn last_mut() {
550624
let mut stack = NonEmptyStack::new(10u64);

crates/oxc_data_structures/src/stack/sparse.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,36 @@ impl<T> SparseStack<T> {
124124
}
125125
}
126126

127+
/// Get reference to value of first entry on the stack.
128+
#[inline]
129+
pub fn first(&self) -> Option<&T> {
130+
let has_value = *self.has_values.first();
131+
if has_value {
132+
debug_assert!(!self.values.is_empty());
133+
// SAFETY: First `self.has_values` is only `true` if there's a corresponding value in `self.values`.
134+
// This invariant is maintained in `push`, `pop`, `take_last`, `last_or_init`, and `last_mut_or_init`.
135+
let value = unsafe { self.values.get_unchecked(0) };
136+
Some(value)
137+
} else {
138+
None
139+
}
140+
}
141+
142+
/// Get mutable reference to value of first entry on the stack.
143+
#[inline]
144+
pub fn first_mut(&mut self) -> Option<&mut T> {
145+
let has_value = *self.has_values.first();
146+
if has_value {
147+
debug_assert!(!self.values.is_empty());
148+
// SAFETY: First `self.has_values` is only `true` if there's a corresponding value in `self.values`.
149+
// This invariant is maintained in `push`, `pop`, `take_last`, `last_or_init`, and `last_mut_or_init`.
150+
let value = unsafe { self.values.get_unchecked_mut(0) };
151+
Some(value)
152+
} else {
153+
None
154+
}
155+
}
156+
127157
/// Get reference to value of last entry on the stack.
128158
#[inline]
129159
pub fn last(&self) -> Option<&T> {

crates/oxc_data_structures/src/stack/standard.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,12 @@ impl<T> Stack<T> {
184184
Self { cursor: start, start, end }
185185
}
186186

187+
// Note: There is no need to implement `first` and `first_mut` methods.
188+
// `NonEmptyStack` can make those methods infallible, but `Stack` can't because `Stack` can be empty.
189+
// `std`'s `first` and `first_mut` methods available via `Deref` / `DerefMut` to a `&[T]` / `&mut[T]`
190+
// are just as efficient as a hand-written version.
191+
// https://godbolt.org/z/rjb1dzob1
192+
187193
/// Get reference to last value on stack.
188194
#[inline]
189195
pub fn last(&self) -> Option<&T> {

crates/oxc_transformer/src/common/arrow_function_converter.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -194,18 +194,18 @@ impl<'a> Traverse<'a> for ArrowFunctionConverter<'a> {
194194
);
195195

196196
debug_assert!(self.this_var_stack.len() == 1);
197-
debug_assert!(self.this_var_stack.last().is_none());
197+
debug_assert!(self.this_var_stack.first().is_none());
198198
debug_assert!(self.arguments_var_stack.len() == 1);
199-
debug_assert!(self.arguments_var_stack.last().is_none());
199+
debug_assert!(self.arguments_var_stack.first().is_none());
200200
debug_assert!(self.constructor_super_stack.len() == 1);
201201
// TODO: This assertion currently failing because we don't handle `super` in arrow functions
202202
// in class static properties correctly.
203203
// e.g. `class C { static f = () => super.prop; }`
204-
// debug_assert!(self.constructor_super_stack.last() == &false);
204+
// debug_assert!(self.constructor_super_stack.first() == &false);
205205
debug_assert!(self.super_methods_stack.len() == 1);
206-
debug_assert!(self.super_methods_stack.last().is_empty());
206+
debug_assert!(self.super_methods_stack.first().is_empty());
207207
debug_assert!(self.super_needs_transform_stack.len() == 1);
208-
debug_assert!(self.super_needs_transform_stack.last() == &false);
208+
debug_assert!(self.super_needs_transform_stack.first() == &false);
209209
}
210210

211211
fn enter_function(&mut self, func: &mut Function<'a>, ctx: &mut TraverseCtx<'a>) {

crates/oxc_transformer/src/typescript/enum.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -574,9 +574,7 @@ impl IdentifierReferenceRename<'_, '_> {
574574
// }
575575
// }
576576
// ```
577-
//
578-
// `NonEmptyStack` guarantees that the stack is not empty.
579-
*self.scope_stack.first().unwrap() == symbol_scope_id
577+
*self.scope_stack.first() == symbol_scope_id
580578
// The resolved symbol is declared outside the enum,
581579
// and we have checked that the name exists in previous_enum_members:
582580
//
@@ -586,7 +584,7 @@ impl IdentifierReferenceRename<'_, '_> {
586584
// enum Foo { B = A }
587585
// ^ This should be renamed to Foo.A
588586
// ```
589-
|| !self.scope_stack.contains(&symbol_scope_id)
587+
|| !self.scope_stack.contains(&symbol_scope_id)
590588
}
591589
}
592590

0 commit comments

Comments
 (0)