diff --git a/crates/oxc_allocator/src/clone_in.rs b/crates/oxc_allocator/src/clone_in.rs new file mode 100644 index 0000000000000..6ec31c239d125 --- /dev/null +++ b/crates/oxc_allocator/src/clone_in.rs @@ -0,0 +1,91 @@ +use std::cell::Cell; + +use crate::{Allocator, Box, Vec}; + +/// A trait to explicitly clone an object into an arena allocator. +/// +/// As a convention `Cloned` associated type should always be the same as `Self`, +/// It'd only differ in the lifetime, Here's an example: +/// +/// ``` +/// impl<'old_alloc, 'new_alloc> CloneIn<'new_alloc> for Struct<'old_alloc> { +/// type Cloned = Struct<'new_alloc>; +/// fn clone_in(&self, alloc: &'new_alloc Allocator) -> Self::Cloned { +/// Struct { a: self.a.clone_in(alloc), b: self.b.clone_in(alloc) } +/// } +/// } +/// ``` +/// +/// Implementations of this trait on non-allocated items usually short-circuit to `Clone::clone`; +/// However, it **isn't** guaranteed. +/// +pub trait CloneIn<'new_alloc>: Sized { + type Cloned; + + fn clone_in(&self, alloc: &'new_alloc Allocator) -> Self::Cloned; +} + +impl<'alloc, T, C> CloneIn<'alloc> for Option +where + T: CloneIn<'alloc, Cloned = C>, +{ + type Cloned = Option; + fn clone_in(&self, alloc: &'alloc Allocator) -> Self::Cloned { + self.as_ref().map(|it| it.clone_in(alloc)) + } +} + +impl<'old_alloc, 'new_alloc, T, C> CloneIn<'new_alloc> for Box<'old_alloc, T> +where + T: CloneIn<'new_alloc, Cloned = C>, +{ + type Cloned = Box<'new_alloc, C>; + fn clone_in(&self, alloc: &'new_alloc Allocator) -> Self::Cloned { + Box::new_in(self.as_ref().clone_in(alloc), alloc) + } +} + +impl<'old_alloc, 'new_alloc, T, C> CloneIn<'new_alloc> for Vec<'old_alloc, T> +where + T: CloneIn<'new_alloc, Cloned = C>, +{ + type Cloned = Vec<'new_alloc, C>; + fn clone_in(&self, alloc: &'new_alloc Allocator) -> Self::Cloned { + Vec::from_iter_in(self.iter().map(|it| it.clone_in(alloc)), alloc) + } +} + +impl<'alloc, T: Copy> CloneIn<'alloc> for Cell { + type Cloned = Cell; + fn clone_in(&self, _: &'alloc Allocator) -> Self::Cloned { + Cell::new(self.get()) + } +} + +impl<'old_alloc, 'new_alloc> CloneIn<'new_alloc> for &'old_alloc str { + type Cloned = &'new_alloc str; + fn clone_in(&self, alloc: &'new_alloc Allocator) -> Self::Cloned { + alloc.alloc_str(self) + } +} + +macro_rules! impl_clone_in { + ($($t:ty)*) => { + $( + impl<'alloc> CloneIn<'alloc> for $t { + type Cloned = Self; + #[inline(always)] + fn clone_in(&self, _: &'alloc Allocator) -> Self { + *self + } + } + )* + } +} + +impl_clone_in! { + usize u8 u16 u32 u64 u128 + isize i8 i16 i32 i64 i128 + f32 f64 + bool char +} diff --git a/crates/oxc_allocator/src/lib.rs b/crates/oxc_allocator/src/lib.rs index a8707326c0c57..7ab4c8ff6d00e 100644 --- a/crates/oxc_allocator/src/lib.rs +++ b/crates/oxc_allocator/src/lib.rs @@ -4,11 +4,13 @@ use std::{ }; mod arena; +mod clone_in; mod convert; use bumpalo::Bump; pub use arena::{Box, String, Vec}; +pub use clone_in::CloneIn; pub use convert::{FromIn, IntoIn}; #[derive(Default)] diff --git a/crates/oxc_span/src/atom.rs b/crates/oxc_span/src/atom.rs index f81bd59a00f2c..0d3ff890b4c75 100644 --- a/crates/oxc_span/src/atom.rs +++ b/crates/oxc_span/src/atom.rs @@ -9,7 +9,7 @@ use compact_str::CompactString; use serde::{Serialize, Serializer}; use crate::Span; -use oxc_allocator::{Allocator, FromIn}; +use oxc_allocator::{Allocator, CloneIn, FromIn}; #[cfg(feature = "serialize")] #[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)] @@ -59,6 +59,14 @@ impl<'a> Atom<'a> { } } +impl<'old_alloc, 'new_alloc> CloneIn<'new_alloc> for Atom<'old_alloc> { + type Cloned = Atom<'new_alloc>; + + fn clone_in(&self, alloc: &'new_alloc Allocator) -> Self::Cloned { + Atom::from_in(self.as_str(), alloc) + } +} + impl<'a, 'b> FromIn<'a, &'b Atom<'a>> for Atom<'a> { fn from_in(s: &'b Atom<'a>, _: &'a Allocator) -> Self { Self::from(s.0) diff --git a/crates/oxc_span/src/source_type/mod.rs b/crates/oxc_span/src/source_type/mod.rs index 87b04e1eff843..1229dcee5d06e 100644 --- a/crates/oxc_span/src/source_type/mod.rs +++ b/crates/oxc_span/src/source_type/mod.rs @@ -1,6 +1,7 @@ use std::path::Path; mod types; +use oxc_allocator::{Allocator, CloneIn}; pub use types::*; #[derive(Debug)] @@ -17,6 +18,14 @@ impl Default for SourceType { } } +impl<'a> CloneIn<'a> for SourceType { + type Cloned = Self; + #[inline] + fn clone_in(&self, _: &'a Allocator) -> Self { + *self + } +} + /// Valid file extensions pub const VALID_EXTENSIONS: [&str; 8] = ["js", "mjs", "cjs", "jsx", "ts", "mts", "cts", "tsx"]; diff --git a/crates/oxc_span/src/span/mod.rs b/crates/oxc_span/src/span/mod.rs index 89b98b8aaa5d8..504dd916f0188 100644 --- a/crates/oxc_span/src/span/mod.rs +++ b/crates/oxc_span/src/span/mod.rs @@ -6,6 +6,7 @@ use std::{ use miette::{LabeledSpan, SourceOffset, SourceSpan}; mod types; +use oxc_allocator::{Allocator, CloneIn}; pub use types::Span; /// An Empty span useful for creating AST nodes. @@ -338,6 +339,14 @@ impl GetSpanMut for Span { } } +impl<'a> CloneIn<'a> for Span { + type Cloned = Self; + #[inline] + fn clone_in(&self, _: &'a Allocator) -> Self { + *self + } +} + #[cfg(test)] mod test { use super::Span;