Skip to content

Commit 635e032

Browse files
committed
Introduce RootedFixedValueArray
Signed-off-by: Taym Haddadi <[email protected]>
1 parent 060888d commit 635e032

File tree

2 files changed

+237
-0
lines changed

2 files changed

+237
-0
lines changed

mozjs/src/gc/collections.rs

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use mozjs_sys::jsapi::JS;
55
use mozjs_sys::jsgc::GCMethods;
66
use mozjs_sys::jsval::JSVal;
77
use mozjs_sys::trace::Traceable;
8+
use std::marker::PhantomData;
89
use std::ops::{Deref, DerefMut};
910

1011
/// A vector of items to be rooted with `RootedVec`.
@@ -157,3 +158,219 @@ impl<T: Traceable + 'static> Drop for RootedTraceableBox<T> {
157158
}
158159
}
159160
}
161+
162+
/// Inline, fixed capacity buffer of JS values with GC barriers.
163+
/// Backed by `[Heap<JSVal>; N]`, and a manual `len`.
164+
pub struct FixedValueArray<const N: usize> {
165+
elems: [Heap<JSVal>; N],
166+
len: usize,
167+
}
168+
169+
unsafe impl<const N: usize> Traceable for FixedValueArray<N> {
170+
unsafe fn trace(&self, trc: *mut JSTracer) {
171+
for i in 0..self.len {
172+
self.elems[i].trace(trc);
173+
}
174+
}
175+
}
176+
177+
impl<const N: usize> FixedValueArray<N> {
178+
/// Create a new empty array, with all slots initialized to Undefined.
179+
pub fn new() -> Self {
180+
Self {
181+
elems: std::array::from_fn(|_| Heap::<JSVal>::default()),
182+
len: 0,
183+
}
184+
}
185+
186+
/// Current logical length.
187+
pub fn len(&self) -> usize {
188+
self.len
189+
}
190+
191+
/// Max capacity (const generic).
192+
pub fn capacity(&self) -> usize {
193+
N
194+
}
195+
196+
/// Push a JSVal into the next slot.
197+
/// Panics if you exceed capacity N.
198+
pub fn push(&mut self, v: JSVal) {
199+
assert!(self.len < N, "FixedValueArray capacity ({}) exceeded", N);
200+
self.elems[self.len].set(v);
201+
self.len += 1;
202+
}
203+
204+
/// Return a stable HandleValueArray that SpiderMonkey can consume.
205+
pub fn as_handle_value_array(&self) -> JS::HandleValueArray {
206+
JS::HandleValueArray {
207+
length_: self.len,
208+
elements_: self.elems.as_ptr() as *const JSVal,
209+
}
210+
}
211+
}
212+
213+
/// A rooted wrapper for a FixedValueArray<N>.
214+
pub struct RootedFixedValueArray<'a, const N: usize> {
215+
ptr: *mut FixedValueArray<N>,
216+
_phantom: PhantomData<&'a mut FixedValueArray<N>>,
217+
}
218+
219+
impl<'a, const N: usize> RootedFixedValueArray<'a, N> {
220+
/// Allocate a FixedValueArray<N>, register it in RootedTraceableSet so the GC
221+
pub fn new() -> Self {
222+
let boxed = Box::new(FixedValueArray::<N>::new());
223+
let raw = Box::into_raw(boxed);
224+
225+
unsafe {
226+
RootedTraceableSet::add(raw);
227+
}
228+
229+
RootedFixedValueArray {
230+
ptr: raw,
231+
_phantom: PhantomData,
232+
}
233+
}
234+
235+
/// Push a JSVal into the underlying fixed array.
236+
pub fn push(&mut self, v: JSVal) {
237+
unsafe { (&mut *self.ptr).push(v) }
238+
}
239+
240+
/// Produce a stable HandleValueArray view into the initialized prefix.
241+
pub fn as_handle_value_array(&self) -> JS::HandleValueArray {
242+
unsafe { (&*self.ptr).as_handle_value_array() }
243+
}
244+
245+
pub fn len(&self) -> usize {
246+
unsafe { (&*self.ptr).len() }
247+
}
248+
249+
pub fn capacity(&self) -> usize {
250+
unsafe { (&*self.ptr).capacity() }
251+
}
252+
}
253+
254+
impl<'a, const N: usize> Drop for RootedFixedValueArray<'a, N> {
255+
fn drop(&mut self) {
256+
unsafe {
257+
RootedTraceableSet::remove(self.ptr);
258+
let _ = Box::from_raw(self.ptr);
259+
}
260+
}
261+
}
262+
263+
/// A growable, rooted, GC traceable collection of JS values.
264+
/// Elements are stored in boxed Heap<JSVal> cells so each GC cell has a stable
265+
/// address even if the Vec reallocates.
266+
pub struct DynamicValueArray {
267+
elems: Vec<Box<Heap<JSVal>>>,
268+
}
269+
270+
unsafe impl Traceable for DynamicValueArray {
271+
unsafe fn trace(&self, trc: *mut JSTracer) {
272+
for heap_box in &self.elems {
273+
heap_box.trace(trc);
274+
}
275+
}
276+
}
277+
278+
impl DynamicValueArray {
279+
pub fn new() -> Self {
280+
DynamicValueArray { elems: Vec::new() }
281+
}
282+
283+
pub fn with_capacity(cap: usize) -> Self {
284+
DynamicValueArray {
285+
elems: Vec::with_capacity(cap),
286+
}
287+
}
288+
289+
pub fn push(&mut self, v: JSVal) {
290+
let cell = Heap::boxed(v);
291+
self.elems.push(cell);
292+
}
293+
294+
pub fn len(&self) -> usize {
295+
self.elems.len()
296+
}
297+
}
298+
299+
/// A rooted, wrapper for DynamicValueArray which also owns
300+
/// Vec<JSVal> used to present a HandleValueArray view to SpiderMonkey.
301+
pub struct RootedDynamicValueArray {
302+
ptr: *mut DynamicValueArray,
303+
scratch: Vec<JSVal>,
304+
}
305+
306+
unsafe impl Traceable for RootedDynamicValueArray {
307+
unsafe fn trace(&self, trc: *mut JSTracer) {
308+
(&*self.ptr).trace(trc)
309+
}
310+
}
311+
312+
impl RootedDynamicValueArray {
313+
pub fn new() -> Self {
314+
let boxed = Box::new(DynamicValueArray::new());
315+
let raw = Box::into_raw(boxed);
316+
317+
unsafe {
318+
RootedTraceableSet::add(raw);
319+
}
320+
321+
RootedDynamicValueArray {
322+
ptr: raw,
323+
scratch: Vec::new(),
324+
}
325+
}
326+
327+
pub fn with_capacity(cap: usize) -> Self {
328+
let boxed = Box::new(DynamicValueArray::with_capacity(cap));
329+
let raw = Box::into_raw(boxed);
330+
331+
unsafe {
332+
RootedTraceableSet::add(raw);
333+
}
334+
335+
RootedDynamicValueArray {
336+
ptr: raw,
337+
scratch: Vec::with_capacity(cap),
338+
}
339+
}
340+
341+
pub fn push(&mut self, v: JSVal) {
342+
unsafe {
343+
(&mut *self.ptr).push(v);
344+
}
345+
}
346+
347+
fn rebuild_scratch(&mut self) {
348+
let inner = unsafe { &*self.ptr };
349+
self.scratch.clear();
350+
self.scratch.reserve(inner.len());
351+
for heap_box in &inner.elems {
352+
self.scratch.push(heap_box.get());
353+
}
354+
}
355+
356+
pub fn as_handle_value_array(&mut self) -> JS::HandleValueArray {
357+
self.rebuild_scratch();
358+
JS::HandleValueArray {
359+
length_: self.scratch.len(),
360+
elements_: self.scratch.as_ptr(),
361+
}
362+
}
363+
364+
pub fn len(&self) -> usize {
365+
unsafe { (&*self.ptr).len() }
366+
}
367+
}
368+
369+
impl Drop for RootedDynamicValueArray {
370+
fn drop(&mut self) {
371+
unsafe {
372+
RootedTraceableSet::remove(self.ptr);
373+
let _ = Box::from_raw(self.ptr);
374+
}
375+
}
376+
}

mozjs/src/gc/macros.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,23 @@ macro_rules! auto_root {
4747
let $($var)+: $crate::rust::CustomAutoRootedGuard<$type> = __root.root($cx);
4848
};
4949
}
50+
51+
#[macro_export]
52+
macro_rules! rooted_fixed_array {
53+
(let mut $name:ident : $cap:expr) => {
54+
let mut $name = $crate::gc::RootedFixedValueArray::<'_, { $cap }>::new();
55+
};
56+
(let $name:ident : $cap:expr) => {
57+
let $name = $crate::gc::RootedFixedValueArray::<'_, { $cap }>::new();
58+
};
59+
}
60+
61+
#[macro_export]
62+
macro_rules! rooted_dynamic_array {
63+
(let mut $name:ident) => {
64+
let mut $name = $crate::gc::RootedDynamicValueArray::new();
65+
};
66+
(let mut $name:ident : $cap:expr) => {
67+
let mut $name = $crate::gc::RootedDynamicValueArray::with_capacity($cap);
68+
};
69+
}

0 commit comments

Comments
 (0)