mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
feat(allocator): introduce Vec::from_array_in (#7331)
Because we lack specialization in stable Rust, `Vec::from_iter_in` is unable to take advantage of the fact that `[T; N]` has a statically knowable size. Introduce `Vec::from_array_in` for this case, which should be able to create the `Vec` with a single static-sized memcpy, or may allow the compiler to see that it can construct the array directly in the arena, rather than construct on stack and then copy to the arena. Also add a corresponding `AstBuilder::vec_from_array` method, and use it in various places in codebase.
This commit is contained in:
parent
1938a1d6d9
commit
39afb48025
7 changed files with 50 additions and 7 deletions
|
|
@ -99,6 +99,13 @@ impl<'alloc, T: ?Sized> Box<'alloc, T> {
|
|||
pub(crate) const unsafe fn from_non_null(ptr: NonNull<T>) -> 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<T> {
|
||||
boxed.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'alloc, T: ?Sized> ops::Deref for Box<'alloc, T> {
|
||||
|
|
|
|||
|
|
@ -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<const N: usize>(array: [T; N], allocator: &'alloc Allocator) -> Self {
|
||||
let boxed = Box::new_in(array, allocator);
|
||||
let ptr = Box::into_non_null(boxed).as_ptr().cast::<T>();
|
||||
// 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.
|
||||
|
|
|
|||
|
|
@ -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<T, const N: usize>(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]
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
]),
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue