diff --git a/crates/oxc_allocator/src/boxed.rs b/crates/oxc_allocator/src/boxed.rs index 3271631cb1c12..a91e7337f5160 100644 --- a/crates/oxc_allocator/src/boxed.rs +++ b/crates/oxc_allocator/src/boxed.rs @@ -99,6 +99,13 @@ impl<'alloc, T: ?Sized> Box<'alloc, T> { pub(crate) const unsafe fn from_non_null(ptr: NonNull) -> Self { Self(ptr, PhantomData) } + + /// Consume a [`Box`] and return a [`NonNull`] pointer to its contents. + #[inline] + #[expect(clippy::needless_pass_by_value)] + pub fn into_non_null(boxed: Self) -> NonNull { + boxed.0 + } } impl<'alloc, T: ?Sized> ops::Deref for Box<'alloc, T> { diff --git a/crates/oxc_allocator/src/vec.rs b/crates/oxc_allocator/src/vec.rs index 471e47e47fc1c..057333aae5c36 100644 --- a/crates/oxc_allocator/src/vec.rs +++ b/crates/oxc_allocator/src/vec.rs @@ -117,6 +117,34 @@ impl<'alloc, T> Vec<'alloc, T> { Self(vec) } + /// Create a new [`Vec`] from a fixed-size array, allocated in the given `allocator`. + /// + /// This is preferable to `from_iter_in` where source is an array, as size is statically known, + /// and compiler is more likely to construct the values directly in arena, rather than constructing + /// on stack and then copying to arena. + /// + /// # Examples + /// + /// ``` + /// use oxc_allocator::{Allocator, Vec}; + /// + /// let allocator = Allocator::default(); + /// + /// let array: [u32; 4] = [1, 2, 3, 4]; + /// let vec = Vec::from_array_in(array, &allocator); + /// ``` + #[inline] + pub fn from_array_in(array: [T; N], allocator: &'alloc Allocator) -> Self { + let boxed = Box::new_in(array, allocator); + let ptr = Box::into_non_null(boxed).as_ptr().cast::(); + // SAFETY: `ptr` has correct alignment - it was just allocated as `[T; N]`. + // `ptr` was allocated with correct size for `[T; N]`. + // `len` and `capacity` are both `N`. + // Allocated size cannot be larger than `isize::MAX`, or `Box::new_in` would have failed. + let vec = unsafe { vec::Vec::from_raw_parts_in(ptr, N, N, &**allocator) }; + Self(ManuallyDrop::new(vec)) + } + /// Converts the vector into [`Box<[T]>`][owned slice]. /// /// Any excess capacity the vector has will not be included in the slice. diff --git a/crates/oxc_ast/src/ast_builder_impl.rs b/crates/oxc_ast/src/ast_builder_impl.rs index 9c5c4ccea58b0..80937d8333c7d 100644 --- a/crates/oxc_ast/src/ast_builder_impl.rs +++ b/crates/oxc_ast/src/ast_builder_impl.rs @@ -67,6 +67,16 @@ impl<'a> AstBuilder<'a> { Vec::from_iter_in(iter, self.allocator) } + /// Create [`Vec`] from a fixed-size array. + /// + /// This is preferable to `vec_from_iter` where source is an array, as size is statically known, + /// and compiler is more likely to construct the values directly in arena, rather than constructing + /// on stack and then copying to arena. + #[inline] + pub fn vec_from_array(self, array: [T; N]) -> Vec<'a, T> { + Vec::from_array_in(array, self.allocator) + } + /// Move a string slice into the memory arena, returning a reference to the slice /// in the heap. #[inline] diff --git a/crates/oxc_isolated_declarations/src/function.rs b/crates/oxc_isolated_declarations/src/function.rs index bfcf00beced15..380098817f38d 100644 --- a/crates/oxc_isolated_declarations/src/function.rs +++ b/crates/oxc_isolated_declarations/src/function.rs @@ -99,7 +99,7 @@ impl<'a> IsolatedDeclarations<'a> { SPAN, self.ast.ts_type_union_type( SPAN, - self.ast.vec_from_iter([ + self.ast.vec_from_array([ ts_type, self.ast.ts_type_undefined_keyword(SPAN), ]), diff --git a/crates/oxc_isolated_declarations/src/return_type.rs b/crates/oxc_isolated_declarations/src/return_type.rs index e785245fb7fd4..55a6ca5154383 100644 --- a/crates/oxc_isolated_declarations/src/return_type.rs +++ b/crates/oxc_isolated_declarations/src/return_type.rs @@ -113,7 +113,7 @@ impl<'a> FunctionReturnType<'a> { let types = transformer .ast - .vec_from_iter([expr_type, transformer.ast.ts_type_undefined_keyword(SPAN)]); + .vec_from_array([expr_type, transformer.ast.ts_type_undefined_keyword(SPAN)]); expr_type = transformer.ast.ts_type_union_type(SPAN, types); } Some(expr_type) diff --git a/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs b/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs index 282ddbe102f83..2a547f25d4d79 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs @@ -156,9 +156,7 @@ impl<'a, 'b> PeepholeFoldConstants { // or: false_with_sideeffects && foo() => false_with_sideeffects, foo() let left = ctx.ast.move_expression(&mut logical_expr.left); let right = ctx.ast.move_expression(&mut logical_expr.right); - let mut vec = ctx.ast.vec_with_capacity(2); - vec.push(left); - vec.push(right); + let vec = ctx.ast.vec_from_array([left, right]); let sequence_expr = ctx.ast.expression_sequence(logical_expr.span, vec); return Some(sequence_expr); } else if let Expression::LogicalExpression(left_child) = &mut logical_expr.left { @@ -201,7 +199,7 @@ impl<'a, 'b> PeepholeFoldConstants { ValueType::Null | ValueType::Undefined => { Some(if left.may_have_side_effects() { // e.g. `(a(), null) ?? 1` => `(a(), null, 1)` - let expressions = ctx.ast.vec_from_iter([ + let expressions = ctx.ast.vec_from_array([ ctx.ast.move_expression(&mut logical_expr.left), ctx.ast.move_expression(&mut logical_expr.right), ]); diff --git a/crates/oxc_transformer/src/es2016/exponentiation_operator.rs b/crates/oxc_transformer/src/es2016/exponentiation_operator.rs index 38254c188783d..ea9c73caa44c8 100644 --- a/crates/oxc_transformer/src/es2016/exponentiation_operator.rs +++ b/crates/oxc_transformer/src/es2016/exponentiation_operator.rs @@ -545,7 +545,7 @@ impl<'a, 'ctx> ExponentiationOperator<'a, 'ctx> { let property = ctx.ast.identifier_name(SPAN, "pow"); let callee = Expression::from(ctx.ast.member_expression_static(SPAN, object, property, false)); - let arguments = ctx.ast.vec_from_iter([Argument::from(left), Argument::from(right)]); + let arguments = ctx.ast.vec_from_array([Argument::from(left), Argument::from(right)]); ctx.ast.expression_call(SPAN, callee, NONE, arguments, false) }