From 50d0659967ede0f2a6120ac1ce386553b143a887 Mon Sep 17 00:00:00 2001 From: shruti2522 Date: Fri, 21 Aug 2026 00:32:39 +0000 Subject: [PATCH 1/8] fix(mempool3): increase SIZE_CLASSES to avoid buffer overruns on large objects --- oscars/src/alloc/mempool3/mod.rs | 25 +++++++------------------ oscars/src/alloc/mempool3/tests.rs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/oscars/src/alloc/mempool3/mod.rs b/oscars/src/alloc/mempool3/mod.rs index 90a8f23..286c12c 100644 --- a/oscars/src/alloc/mempool3/mod.rs +++ b/oscars/src/alloc/mempool3/mod.rs @@ -28,19 +28,21 @@ impl From for PoolAllocError { } } -const SIZE_CLASSES: &[usize] = &[16, 24, 32, 48, 64, 96, 128, 192, 256, 512, 1024, 2048]; +const SIZE_CLASSES: &[usize] = &[ + 16, 24, 32, 48, 64, 96, 128, 192, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, +]; #[inline(always)] fn size_class_index_for(size: usize) -> usize { // binary search over size classes let idx = SIZE_CLASSES.partition_point(|&sc| sc < size); - debug_assert!( + assert!( idx < SIZE_CLASSES.len(), "object size {size}B exceeds the largest size class ({}B); \ consider adding a larger class", SIZE_CLASSES.last().unwrap() ); - idx.min(SIZE_CLASSES.len() - 1) + idx } const DEFAULT_PAGE_SIZE: usize = 262_144; @@ -58,7 +60,7 @@ pub struct PoolAllocator<'alloc> { // cached index of the last pool used by free_slot pub(crate) free_cache: Cell, // per size class cached index of the last pool used by alloc_slot - pub(crate) alloc_cache: [Cell; 12], + pub(crate) alloc_cache: [Cell; 17], // empty slot pools kept alive to avoid OS reallocation on the next cycle pub(crate) recycled_pools: Vec, // maximum number of idle pages held across all size classes @@ -78,20 +80,7 @@ impl<'alloc> Default for PoolAllocator<'alloc> { slot_pools: Vec::new(), bump_pages: Vec::new(), free_cache: Cell::new(usize::MAX), - alloc_cache: [ - Cell::new(usize::MAX), - Cell::new(usize::MAX), - Cell::new(usize::MAX), - Cell::new(usize::MAX), - Cell::new(usize::MAX), - Cell::new(usize::MAX), - Cell::new(usize::MAX), - Cell::new(usize::MAX), - Cell::new(usize::MAX), - Cell::new(usize::MAX), - Cell::new(usize::MAX), - Cell::new(usize::MAX), - ], + alloc_cache: core::array::from_fn(|_| Cell::new(usize::MAX)), recycled_pools: Vec::new(), // keep two empty pages per size class to reduce OS overhead max_recycled: SIZE_CLASSES.len() * 2, diff --git a/oscars/src/alloc/mempool3/tests.rs b/oscars/src/alloc/mempool3/tests.rs index 1991dcc..2855fe2 100644 --- a/oscars/src/alloc/mempool3/tests.rs +++ b/oscars/src/alloc/mempool3/tests.rs @@ -234,3 +234,33 @@ fn max_recycled_cap_respected() { assert_eq!(allocator.recycled_pools.len(), 1); assert!(allocator.current_heap_size < heap_before); } + +#[test] +fn alloc_large_object_success() { + let mut allocator = PoolAllocator::default().with_page_size(16384); + // allocate a size that maps to a class > 2048 + // e.g. 4096 size class => size 3000 + struct LargeObject { + _data: [u8; 3000], + } + let a = allocator + .try_alloc(LargeObject { _data: [0; 3000] }) + .unwrap(); + assert_eq!(allocator.pools_len(), 1); + + // Test that the caching works for the new size classes + let _b = allocator + .try_alloc(LargeObject { _data: [0; 3000] }) + .unwrap(); + assert_eq!(allocator.pools_len(), 1); // should still fit in the same page +} + +#[test] +#[should_panic(expected = "object size")] +fn alloc_oversized_object_panics() { + let mut allocator = PoolAllocator::default().with_page_size(128 * 1024); + struct OversizedObject { + _data: [u8; 70000], + } + let _ = allocator.try_alloc(OversizedObject { _data: [0; 70000] }); +} From 44df52980584138e2942ddfa9edd3075f88f896e Mon Sep 17 00:00:00 2001 From: shruti2522 Date: Sat, 29 Aug 2026 06:44:08 +0000 Subject: [PATCH 2/8] feat: Add finalize_fn and root_count to GcBox for boa integration --- .../src/collectors/mark_sweep_branded/gc.rs | 17 +++++++++-- .../collectors/mark_sweep_branded/gc_box.rs | 10 +++++++ .../src/collectors/mark_sweep_branded/mod.rs | 28 +++++++++++++++++-- 3 files changed, 50 insertions(+), 5 deletions(-) diff --git a/oscars/src/collectors/mark_sweep_branded/gc.rs b/oscars/src/collectors/mark_sweep_branded/gc.rs index df5f18c..4a527e3 100644 --- a/oscars/src/collectors/mark_sweep_branded/gc.rs +++ b/oscars/src/collectors/mark_sweep_branded/gc.rs @@ -19,10 +19,23 @@ pub struct Gc<'gc, T: Trace + ?Sized + 'gc> { pub(crate) _marker: PhantomData<(&'gc T, *const ())>, } -impl<'gc, T: Trace + ?Sized + 'gc> Copy for Gc<'gc, T> {} impl<'gc, T: Trace + ?Sized + 'gc> Clone for Gc<'gc, T> { fn clone(&self) -> Self { - *self + let count = unsafe { &(*self.ptr.as_ptr().as_ptr()).0.root_count }; + count.set(count.get() + 1); + Self { + ptr: self.ptr, + _marker: core::marker::PhantomData, + } + } +} + +impl<'gc, T: Trace + ?Sized + 'gc> Drop for Gc<'gc, T> { + fn drop(&mut self) { + let count = unsafe { &(*self.ptr.as_ptr().as_ptr()).0.root_count }; + if count.get() > 0 { + count.set(count.get() - 1); + } } } diff --git a/oscars/src/collectors/mark_sweep_branded/gc_box.rs b/oscars/src/collectors/mark_sweep_branded/gc_box.rs index 8a2a0d2..8e53d6e 100644 --- a/oscars/src/collectors/mark_sweep_branded/gc_box.rs +++ b/oscars/src/collectors/mark_sweep_branded/gc_box.rs @@ -29,6 +29,8 @@ pub struct GcBox { pub(crate) color: Cell, /// Type-erased trace function. pub(crate) trace_fn: TraceFn, + pub(crate) finalize_fn: unsafe fn(NonNull), + pub(crate) root_count: Cell, /// Type-erased finalize and free fn pub(crate) drop_fn: DropFn, /// Allocation ID used to validate weak pointers. @@ -52,9 +54,17 @@ impl GcBox { /// /// Requires `T: Trace` for the `TypeId`. pub(crate) fn new(value: T, trace_fn: TraceFn, drop_fn: DropFn, alloc_id: usize) -> Self { + unsafe fn finalize_node(ptr: NonNull) { + let item_ptr = ptr.cast::>>(); + unsafe { + (*item_ptr.as_ptr()).0.value.run_finalizer(); + } + } Self { color: Cell::new(GcColor::White), trace_fn, + finalize_fn: finalize_node::, + root_count: Cell::new(1), drop_fn, alloc_id, type_id: typeid::of::(), diff --git a/oscars/src/collectors/mark_sweep_branded/mod.rs b/oscars/src/collectors/mark_sweep_branded/mod.rs index 3c026ed..a71f1be 100644 --- a/oscars/src/collectors/mark_sweep_branded/mod.rs +++ b/oscars/src/collectors/mark_sweep_branded/mod.rs @@ -45,6 +45,7 @@ pub struct Collector { pub(crate) sentinel: RootSentinel, pub(crate) generic_alloc_id: Cell, pub(crate) ephemerons: RefCell>, + pub(crate) is_sweeping: Cell, } impl Default for Collector { @@ -61,6 +62,7 @@ impl Collector { sentinel: RootSentinel::new(), generic_alloc_id: Cell::new(0), ephemerons: RefCell::new(Vec::new()), + is_sweeping: Cell::new(false), } } @@ -149,6 +151,10 @@ impl Collector { } /// Runs a collection cycle + pub fn is_sweeping(&self) -> bool { + self.is_sweeping.get() + } + pub fn collect(&self) { self.collect_with_roots(|_| {}) } @@ -162,6 +168,15 @@ impl Collector { trace_external(&mut tracer); + for ptr in self.pool.borrow().iter_live_slots() { + unsafe { + let gc_box = &(*ptr.cast::>>().as_ptr()).0; + if gc_box.root_count.get() > 0 { + (gc_box.trace_fn)(ptr, &mut tracer); + } + } + } + for link_ptr in self.sentinel.iter() { unsafe { // SAFETY: link_ptr points to the `link` field which is first in repr(C) RootNode. @@ -197,7 +212,7 @@ impl Collector { // Phase 3: sweep all slots. Collect unmarked ones, then invalidate and free them. use crate::alloc::mempool3::PoolItem; - let dead: Vec<(NonNull, DropFn)> = { + let dead: Vec<(NonNull, DropFn, unsafe fn(NonNull))> = { let pool = self.pool.borrow(); pool.iter_live_slots() .filter_map(|ptr| unsafe { @@ -206,14 +221,21 @@ impl Collector { gc_box.color.set(GcColor::White); None } else { - Some((ptr, gc_box.drop_fn)) + Some((ptr, gc_box.drop_fn, gc_box.finalize_fn)) } }) .collect() }; + + for (ptr, _, finalize_fn) in &dead { + unsafe { + (finalize_fn)(*ptr); + } + } + { let mut pool = self.pool.borrow_mut(); - for (ptr, drop_fn) in dead { + for (ptr, drop_fn, _) in dead { unsafe { (*ptr.cast::>>().as_ptr()).0.alloc_id = GcBox::<()>::FREED_ALLOC_ID; From 87cdbf2c4b83a0ccc6c929385efd97f1f50f8444 Mon Sep 17 00:00:00 2001 From: shruti2522 Date: Sat, 29 Aug 2026 11:13:04 +0000 Subject: [PATCH 3/8] fix: add repr(C) to GcBox to fix layout mismatch UB --- oscars/src/collectors/mark_sweep_branded/gc_box.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/oscars/src/collectors/mark_sweep_branded/gc_box.rs b/oscars/src/collectors/mark_sweep_branded/gc_box.rs index 8e53d6e..733d62c 100644 --- a/oscars/src/collectors/mark_sweep_branded/gc_box.rs +++ b/oscars/src/collectors/mark_sweep_branded/gc_box.rs @@ -24,6 +24,7 @@ pub(crate) enum GcColor { /// Heap wrapper for a garbage-collected value. /// /// Allocated via [`PoolAllocator`]. +#[repr(C)] pub struct GcBox { /// tricolor marking state, updated by the mark phase pub(crate) color: Cell, From 1c417743083d14a44d4c1176dd60e3fae2982d2d Mon Sep 17 00:00:00 2001 From: shruti2522 Date: Sat, 29 Aug 2026 11:26:16 +0000 Subject: [PATCH 4/8] fix: use mem::forget in cast_unchecked and into_raw to prevent root_count decrement and UAF --- oscars/src/collectors/mark_sweep_branded/gc.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/oscars/src/collectors/mark_sweep_branded/gc.rs b/oscars/src/collectors/mark_sweep_branded/gc.rs index 4a527e3..2fd14f3 100644 --- a/oscars/src/collectors/mark_sweep_branded/gc.rs +++ b/oscars/src/collectors/mark_sweep_branded/gc.rs @@ -97,10 +97,12 @@ impl<'gc, T: Trace + ?Sized + 'gc> Gc<'gc, T> { .ptr .as_ptr() .cast::>>(); - Gc { + let new_gc = Gc { ptr: unsafe { crate::alloc::mempool3::PoolPointer::from_raw(raw) }, _marker: PhantomData, - } + }; + core::mem::forget(self); + new_gc } /// Returns `true` if the inner value is of type `U`. @@ -118,7 +120,7 @@ impl<'gc, T: Trace + ?Sized + 'gc> Gc<'gc, T> { #[allow(private_interfaces)] pub fn into_raw(self) -> core::ptr::NonNull>> { let ptr = self.ptr.as_ptr(); - let _ = self; + core::mem::forget(self); ptr } From 05a218618bf527d2792a743d188d4c19f0f4316f Mon Sep 17 00:00:00 2001 From: shruti2522 Date: Sat, 29 Aug 2026 11:32:53 +0000 Subject: [PATCH 5/8] add assertions for NULL trace_fn --- oscars/src/collectors/mark_sweep_branded/mod.rs | 4 ++++ oscars/src/collectors/mark_sweep_branded/trace.rs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/oscars/src/collectors/mark_sweep_branded/mod.rs b/oscars/src/collectors/mark_sweep_branded/mod.rs index a71f1be..c363bba 100644 --- a/oscars/src/collectors/mark_sweep_branded/mod.rs +++ b/oscars/src/collectors/mark_sweep_branded/mod.rs @@ -172,6 +172,10 @@ impl Collector { unsafe { let gc_box = &(*ptr.cast::>>().as_ptr()).0; if gc_box.root_count.get() > 0 { + let trace_fn_ptr = gc_box.trace_fn as *const (); + if trace_fn_ptr.is_null() { + panic!("Collector::sweep: trace_fn is NULL for rooted object! alloc_id: {}, root_count: {}", gc_box.alloc_id, gc_box.root_count.get()); + } (gc_box.trace_fn)(ptr, &mut tracer); } } diff --git a/oscars/src/collectors/mark_sweep_branded/trace.rs b/oscars/src/collectors/mark_sweep_branded/trace.rs index 34dffac..99a7f23 100644 --- a/oscars/src/collectors/mark_sweep_branded/trace.rs +++ b/oscars/src/collectors/mark_sweep_branded/trace.rs @@ -96,6 +96,10 @@ impl<'a> Tracer<'a> { let gc_box = &(*gc.ptr.as_ptr().as_ptr()).0; if gc_box.color.get() == GcColor::White { gc_box.color.set(GcColor::Gray); + let trace_fn_ptr = gc_box.trace_fn as *const (); + if trace_fn_ptr.is_null() { + panic!("Tracer::mark: trace_fn is NULL! alloc_id: {}", gc_box.alloc_id); + } self.worklist .push((gc.ptr.as_ptr().cast::(), gc_box.trace_fn)); } From 13a3514748d4b57d099d1ebbc353d842f95a59f2 Mon Sep 17 00:00:00 2001 From: shruti2522 Date: Sat, 29 Aug 2026 12:50:44 +0000 Subject: [PATCH 6/8] fix: increment root_count in Gc::with_pointer to prevent UAF via WeakGc/Ephemeron/Root --- .../src/collectors/mark_sweep_branded/gc.rs | 2 + .../collectors/mark_sweep_branded/gc_box.rs | 2 +- patch_gc_with_pointer.py | 48 ++++ patch_oscars.py | 27 +++ patch_oscars_gc.py | 69 ++++++ patch_oscars_gc_full.py | 225 ++++++++++++++++++ 6 files changed, 372 insertions(+), 1 deletion(-) create mode 100644 patch_gc_with_pointer.py create mode 100644 patch_oscars.py create mode 100644 patch_oscars_gc.py create mode 100644 patch_oscars_gc_full.py diff --git a/oscars/src/collectors/mark_sweep_branded/gc.rs b/oscars/src/collectors/mark_sweep_branded/gc.rs index 2fd14f3..d7a9823 100644 --- a/oscars/src/collectors/mark_sweep_branded/gc.rs +++ b/oscars/src/collectors/mark_sweep_branded/gc.rs @@ -42,6 +42,8 @@ impl<'gc, T: Trace + ?Sized + 'gc> Drop for Gc<'gc, T> { impl<'gc, T: Trace + ?Sized + 'gc> Gc<'gc, T> { #[inline] pub(crate) fn with_pointer(ptr: PoolPointer<'static, GcBox>) -> Self { + let count = unsafe { &(*ptr.as_ptr().as_ptr()).0.root_count }; + count.set(count.get() + 1); Self { ptr, _marker: PhantomData, diff --git a/oscars/src/collectors/mark_sweep_branded/gc_box.rs b/oscars/src/collectors/mark_sweep_branded/gc_box.rs index 733d62c..76c1c5a 100644 --- a/oscars/src/collectors/mark_sweep_branded/gc_box.rs +++ b/oscars/src/collectors/mark_sweep_branded/gc_box.rs @@ -65,7 +65,7 @@ impl GcBox { color: Cell::new(GcColor::White), trace_fn, finalize_fn: finalize_node::, - root_count: Cell::new(1), + root_count: Cell::new(0), drop_fn, alloc_id, type_id: typeid::of::(), diff --git a/patch_gc_with_pointer.py b/patch_gc_with_pointer.py new file mode 100644 index 0000000..fc66ed1 --- /dev/null +++ b/patch_gc_with_pointer.py @@ -0,0 +1,48 @@ +import sys + +# 1. gc_box.rs: set initial root_count to 0 +with open("oscars/src/collectors/mark_sweep_branded/gc_box.rs", "r") as f: + content = f.read() + +content = content.replace("root_count: Cell::new(1),", "root_count: Cell::new(0),") +with open("oscars/src/collectors/mark_sweep_branded/gc_box.rs", "w") as f: + f.write(content) + + +# 2. gc.rs: make with_pointer increment root_count +with open("oscars/src/collectors/mark_sweep_branded/gc.rs", "r") as f: + content = f.read() + +target = """ pub(crate) fn with_pointer(ptr: PoolPointer<'static, GcBox>) -> Self { + Self { + ptr, + _marker: PhantomData, + } + }""" +replacement = """ pub(crate) fn with_pointer(ptr: PoolPointer<'static, GcBox>) -> Self { + let gc = Self { + ptr, + _marker: PhantomData, + }; + let count = unsafe { &(*gc.ptr.as_ptr().as_ptr()).0.root_count }; + count.set(count.get() + 1); + gc + }""" +content = content.replace(target, replacement) +with open("oscars/src/collectors/mark_sweep_branded/gc.rs", "w") as f: + f.write(content) + +# 3. Revert the manual root_count increment in weak.rs that I added earlier +with open("oscars/src/collectors/mark_sweep_branded/weak.rs", "r") as f: + content = f.read() + +target_weak = """ let gc = crate::collectors::mark_sweep_branded::Gc::with_pointer(self.ptr); + let count = &(*gc.ptr.as_ptr().as_ptr()).0.root_count; + count.set(count.get() + 1); + Some(gc)""" +replacement_weak = """ Some(crate::collectors::mark_sweep_branded::Gc::with_pointer(self.ptr))""" +content = content.replace(target_weak, replacement_weak) +with open("oscars/src/collectors/mark_sweep_branded/weak.rs", "w") as f: + f.write(content) + +print("Patched with_pointer to always increment root_count!") diff --git a/patch_oscars.py b/patch_oscars.py new file mode 100644 index 0000000..91698a4 --- /dev/null +++ b/patch_oscars.py @@ -0,0 +1,27 @@ +import re + +with open("oscars/src/collectors/mark_sweep_branded/mod.rs", "r") as f: + content = f.read() + +# Add collect_with_roots to MutationContext +target_mc_collect = """ pub fn collect(&self) { + self.collector.collect(); + }""" +replacement_mc_collect = """ pub fn collect(&self) { + self.collector.collect(); + } + + pub fn collect_with_roots(&self, trace_external: F) + where + F: FnOnce(&mut Tracer), + { + self.collector.collect_with_roots(trace_external); + }""" + +if target_mc_collect in content: + content = content.replace(target_mc_collect, replacement_mc_collect) + with open("oscars/src/collectors/mark_sweep_branded/mod.rs", "w") as f: + f.write(content) + print("Patched MutationContext") +else: + print("Could not find target_mc_collect") diff --git a/patch_oscars_gc.py b/patch_oscars_gc.py new file mode 100644 index 0000000..08ca567 --- /dev/null +++ b/patch_oscars_gc.py @@ -0,0 +1,69 @@ +import re + +with open("oscars/src/collectors/mark_sweep_branded/gc_box.rs", "r") as f: + content = f.read() + +# Add root_count to GcBox +target_gcbox = """ pub(crate) type_id: TypeId, + /// The user value. + pub(crate) value: T, +}""" +replacement_gcbox = """ pub(crate) type_id: TypeId, + pub(crate) root_count: Cell, + /// The user value. + pub(crate) value: T, +}""" +content = content.replace(target_gcbox, replacement_gcbox) + +target_new = """ alloc_id, + type_id: typeid::of::(), + value, + }""" +replacement_new = """ alloc_id, + type_id: typeid::of::(), + root_count: Cell::new(1), + value, + }""" +content = content.replace(target_new, replacement_new) + +with open("oscars/src/collectors/mark_sweep_branded/gc_box.rs", "w") as f: + f.write(content) + +with open("oscars/src/collectors/mark_sweep_branded/gc.rs", "r") as f: + content = f.read() + +# Remove Copy and change Clone, add Drop +target_copy = """impl<'gc, T: Trace + ?Sized + 'gc> Copy for Gc<'gc, T> {} +impl<'gc, T: Trace + ?Sized + 'gc> Clone for Gc<'gc, T> { + fn clone(&self) -> Self { + *self + } +}""" +replacement_copy = """impl<'gc, T: Trace + ?Sized + 'gc> Clone for Gc<'gc, T> { + fn clone(&self) -> Self { + let count = unsafe { &(*self.ptr.as_ptr().as_ptr()).0.root_count }; + count.set(count.get() + 1); + Self { + ptr: self.ptr, + _marker: core::marker::PhantomData, + } + } +} + +impl<'gc, T: Trace + ?Sized + 'gc> Drop for Gc<'gc, T> { + fn drop(&mut self) { + // SAFETY: The allocator does not unmap memory, so the block is still valid memory. + // Even if the object was collected and the block re-used, the `root_count` will just be corrupted. + // Wait, if it was collected, it means root_count WAS 0! So it won't be collected if we still have a Gc pointing to it! + // Because the Collector checks root_count during collection! + let count = unsafe { &(*self.ptr.as_ptr().as_ptr()).0.root_count }; + if count.get() > 0 { + count.set(count.get() - 1); + } + } +}""" +content = content.replace(target_copy, replacement_copy) + +with open("oscars/src/collectors/mark_sweep_branded/gc.rs", "w") as f: + f.write(content) +print("Patched Gc and GcBox") diff --git a/patch_oscars_gc_full.py b/patch_oscars_gc_full.py new file mode 100644 index 0000000..4fbce69 --- /dev/null +++ b/patch_oscars_gc_full.py @@ -0,0 +1,225 @@ +import re + +with open("oscars/src/collectors/mark_sweep_branded/gc_box.rs", "r") as f: + content = f.read() + +# Add root_count and finalize_fn to GcBox +target_gcbox = """ pub(crate) color: Cell, + /// Type-erased trace function. + pub(crate) trace_fn: TraceFn, + /// Type-erased finalize and free fn + pub(crate) drop_fn: DropFn,""" +replacement_gcbox = """ pub(crate) color: Cell, + /// Type-erased trace function. + pub(crate) trace_fn: TraceFn, + pub(crate) finalize_fn: unsafe fn(NonNull), + pub(crate) root_count: Cell, + /// Type-erased finalize and free fn + pub(crate) drop_fn: DropFn,""" +content = content.replace(target_gcbox, replacement_gcbox) + +target_new = """ pub(crate) fn new(value: T, trace_fn: TraceFn, drop_fn: DropFn, alloc_id: usize) -> Self { + Self { + color: Cell::new(GcColor::White), + trace_fn, + drop_fn, + alloc_id,""" +replacement_new = """ pub(crate) fn new(value: T, trace_fn: TraceFn, drop_fn: DropFn, alloc_id: usize) -> Self { + unsafe fn finalize_node(ptr: NonNull) { + let item_ptr = ptr.cast::>>(); + unsafe { + (*item_ptr.as_ptr()).0.value.run_finalizer(); + } + } + Self { + color: Cell::new(GcColor::White), + trace_fn, + finalize_fn: finalize_node::, + root_count: Cell::new(1), + drop_fn, + alloc_id,""" +content = content.replace(target_new, replacement_new) + +with open("oscars/src/collectors/mark_sweep_branded/gc_box.rs", "w") as f: + f.write(content) + +with open("oscars/src/collectors/mark_sweep_branded/gc.rs", "r") as f: + content = f.read() + +target_copy = """impl<'gc, T: Trace + ?Sized + 'gc> Copy for Gc<'gc, T> {} +impl<'gc, T: Trace + ?Sized + 'gc> Clone for Gc<'gc, T> { + fn clone(&self) -> Self { + *self + } +}""" +replacement_copy = """impl<'gc, T: Trace + ?Sized + 'gc> Clone for Gc<'gc, T> { + fn clone(&self) -> Self { + let count = unsafe { &(*self.ptr.as_ptr().as_ptr()).0.root_count }; + count.set(count.get() + 1); + Self { + ptr: self.ptr, + _marker: core::marker::PhantomData, + } + } +} + +impl<'gc, T: Trace + ?Sized + 'gc> Drop for Gc<'gc, T> { + fn drop(&mut self) { + let count = unsafe { &(*self.ptr.as_ptr().as_ptr()).0.root_count }; + if count.get() > 0 { + count.set(count.get() - 1); + } + } +}""" +content = content.replace(target_copy, replacement_copy) + +with open("oscars/src/collectors/mark_sweep_branded/gc.rs", "w") as f: + f.write(content) + +with open("oscars/src/collectors/mark_sweep_branded/mod.rs", "r") as f: + content = f.read() + +# Update drop_node to NOT call finalizer +target_drop = """pub(crate) unsafe fn drop_node( + pool: &mut PoolAllocator<'static>, + ptr: core::ptr::NonNull, +) { + let item_ptr = ptr.cast::>>(); + unsafe { + // Run finalizer + (*item_ptr.as_ptr()).0.value.run_finalizer(); + // Free item + pool.free(item_ptr); + } +}""" +replacement_drop = """pub(crate) unsafe fn drop_node( + pool: &mut PoolAllocator<'static>, + ptr: core::ptr::NonNull, +) { + let item_ptr = ptr.cast::>>(); + unsafe { + // Free item + pool.free(item_ptr); + } +}""" +content = content.replace(target_drop, replacement_drop) + +# Update sweep to run finalizers first, and trace root_count +target_sweep = """ let mut tracer = Tracer::new(); + + trace_external(&mut tracer); + + for link_ptr in self.sentinel.iter() {""" +replacement_sweep = """ let mut tracer = Tracer::new(); + + trace_external(&mut tracer); + + for ptr in self.pool.borrow().iter_live_slots() { + unsafe { + let gc_box = &(*ptr.cast::>>().as_ptr()).0; + if gc_box.root_count.get() > 0 { + (gc_box.trace_fn)(ptr, &mut tracer); + } + } + } + + for link_ptr in self.sentinel.iter() {""" +content = content.replace(target_sweep, replacement_sweep) + +target_dead = """ // Phase 3: sweep all slots. Collect unmarked ones, then invalidate and free them. + use crate::alloc::mempool3::PoolItem; + let dead: Vec<(NonNull, DropFn)> = { + let pool = self.pool.borrow(); + pool.iter_live_slots() + .filter_map(|ptr| unsafe { + let gc_box = &(*ptr.cast::>>().as_ptr()).0; + if gc_box.color.get() == GcColor::Black { + gc_box.color.set(GcColor::White); + None + } else { + Some((ptr, gc_box.drop_fn)) + } + }) + .collect() + }; + { + let mut pool = self.pool.borrow_mut(); + for (ptr, drop_fn) in dead { + unsafe { + (*ptr.cast::>>().as_ptr()).0.alloc_id = + GcBox::<()>::FREED_ALLOC_ID; + (drop_fn)(&mut pool, ptr); + } + } + }""" +replacement_dead = """ // Phase 3: sweep all slots. Collect unmarked ones, then invalidate and free them. + use crate::alloc::mempool3::PoolItem; + let dead: Vec<(NonNull, DropFn, unsafe fn(NonNull))> = { + let pool = self.pool.borrow(); + pool.iter_live_slots() + .filter_map(|ptr| unsafe { + let gc_box = &(*ptr.cast::>>().as_ptr()).0; + if gc_box.color.get() == GcColor::Black { + gc_box.color.set(GcColor::White); + None + } else { + Some((ptr, gc_box.drop_fn, gc_box.finalize_fn)) + } + }) + .collect() + }; + + for (ptr, _, finalize_fn) in &dead { + unsafe { + (finalize_fn)(*ptr); + } + } + + { + let mut pool = self.pool.borrow_mut(); + for (ptr, drop_fn, _) in dead { + unsafe { + (*ptr.cast::>>().as_ptr()).0.alloc_id = + GcBox::<()>::FREED_ALLOC_ID; + (drop_fn)(&mut pool, ptr); + } + } + }""" +content = content.replace(target_dead, replacement_dead) + +# Update Collector::drop +target_drop_coll = """ // Then iterate through pool and call drop_fn for all allocated items + for item in self.pool.borrow_mut().iter() { + unsafe { + let node = item.cast::>>(); + let drop_fn = (*node.as_ptr()).0.drop_fn; + (*node.as_ptr()).0.alloc_id = GcBox::<()>::FREED_ALLOC_ID; + (drop_fn)(&mut self.pool.borrow_mut(), item); + } + }""" +replacement_drop_coll = """ // Then iterate through pool and call drop_fn for all allocated items + // Wait, we need to run finalizers first here too! + let dead: Vec<(NonNull, DropFn, unsafe fn(NonNull))> = self.pool.borrow_mut().iter().map(|item| unsafe { + let node = item.cast::>>(); + let drop_fn = (*node.as_ptr()).0.drop_fn; + let finalize_fn = (*node.as_ptr()).0.finalize_fn; + (item, drop_fn, finalize_fn) + }).collect(); + for (item, _, finalize_fn) in &dead { + unsafe { + (finalize_fn)(*item); + } + } + for (item, drop_fn, _) in dead { + unsafe { + let node = item.cast::>>(); + (*node.as_ptr()).0.alloc_id = GcBox::<()>::FREED_ALLOC_ID; + (drop_fn)(&mut self.pool.borrow_mut(), item); + } + }""" +content = content.replace(target_drop_coll, replacement_drop_coll) + +with open("oscars/src/collectors/mark_sweep_branded/mod.rs", "w") as f: + f.write(content) + +print("Patched oscars full!") From 2af6e5ba0b290f1bdd0ebe6386813b31f42c288d Mon Sep 17 00:00:00 2001 From: shruti2522 Date: Sat, 29 Aug 2026 13:03:24 +0000 Subject: [PATCH 7/8] chore: remove temporary patch scripts --- patch_gc_with_pointer.py | 48 --------- patch_oscars.py | 27 ----- patch_oscars_gc.py | 69 ------------ patch_oscars_gc_full.py | 225 --------------------------------------- 4 files changed, 369 deletions(-) delete mode 100644 patch_gc_with_pointer.py delete mode 100644 patch_oscars.py delete mode 100644 patch_oscars_gc.py delete mode 100644 patch_oscars_gc_full.py diff --git a/patch_gc_with_pointer.py b/patch_gc_with_pointer.py deleted file mode 100644 index fc66ed1..0000000 --- a/patch_gc_with_pointer.py +++ /dev/null @@ -1,48 +0,0 @@ -import sys - -# 1. gc_box.rs: set initial root_count to 0 -with open("oscars/src/collectors/mark_sweep_branded/gc_box.rs", "r") as f: - content = f.read() - -content = content.replace("root_count: Cell::new(1),", "root_count: Cell::new(0),") -with open("oscars/src/collectors/mark_sweep_branded/gc_box.rs", "w") as f: - f.write(content) - - -# 2. gc.rs: make with_pointer increment root_count -with open("oscars/src/collectors/mark_sweep_branded/gc.rs", "r") as f: - content = f.read() - -target = """ pub(crate) fn with_pointer(ptr: PoolPointer<'static, GcBox>) -> Self { - Self { - ptr, - _marker: PhantomData, - } - }""" -replacement = """ pub(crate) fn with_pointer(ptr: PoolPointer<'static, GcBox>) -> Self { - let gc = Self { - ptr, - _marker: PhantomData, - }; - let count = unsafe { &(*gc.ptr.as_ptr().as_ptr()).0.root_count }; - count.set(count.get() + 1); - gc - }""" -content = content.replace(target, replacement) -with open("oscars/src/collectors/mark_sweep_branded/gc.rs", "w") as f: - f.write(content) - -# 3. Revert the manual root_count increment in weak.rs that I added earlier -with open("oscars/src/collectors/mark_sweep_branded/weak.rs", "r") as f: - content = f.read() - -target_weak = """ let gc = crate::collectors::mark_sweep_branded::Gc::with_pointer(self.ptr); - let count = &(*gc.ptr.as_ptr().as_ptr()).0.root_count; - count.set(count.get() + 1); - Some(gc)""" -replacement_weak = """ Some(crate::collectors::mark_sweep_branded::Gc::with_pointer(self.ptr))""" -content = content.replace(target_weak, replacement_weak) -with open("oscars/src/collectors/mark_sweep_branded/weak.rs", "w") as f: - f.write(content) - -print("Patched with_pointer to always increment root_count!") diff --git a/patch_oscars.py b/patch_oscars.py deleted file mode 100644 index 91698a4..0000000 --- a/patch_oscars.py +++ /dev/null @@ -1,27 +0,0 @@ -import re - -with open("oscars/src/collectors/mark_sweep_branded/mod.rs", "r") as f: - content = f.read() - -# Add collect_with_roots to MutationContext -target_mc_collect = """ pub fn collect(&self) { - self.collector.collect(); - }""" -replacement_mc_collect = """ pub fn collect(&self) { - self.collector.collect(); - } - - pub fn collect_with_roots(&self, trace_external: F) - where - F: FnOnce(&mut Tracer), - { - self.collector.collect_with_roots(trace_external); - }""" - -if target_mc_collect in content: - content = content.replace(target_mc_collect, replacement_mc_collect) - with open("oscars/src/collectors/mark_sweep_branded/mod.rs", "w") as f: - f.write(content) - print("Patched MutationContext") -else: - print("Could not find target_mc_collect") diff --git a/patch_oscars_gc.py b/patch_oscars_gc.py deleted file mode 100644 index 08ca567..0000000 --- a/patch_oscars_gc.py +++ /dev/null @@ -1,69 +0,0 @@ -import re - -with open("oscars/src/collectors/mark_sweep_branded/gc_box.rs", "r") as f: - content = f.read() - -# Add root_count to GcBox -target_gcbox = """ pub(crate) type_id: TypeId, - /// The user value. - pub(crate) value: T, -}""" -replacement_gcbox = """ pub(crate) type_id: TypeId, - pub(crate) root_count: Cell, - /// The user value. - pub(crate) value: T, -}""" -content = content.replace(target_gcbox, replacement_gcbox) - -target_new = """ alloc_id, - type_id: typeid::of::(), - value, - }""" -replacement_new = """ alloc_id, - type_id: typeid::of::(), - root_count: Cell::new(1), - value, - }""" -content = content.replace(target_new, replacement_new) - -with open("oscars/src/collectors/mark_sweep_branded/gc_box.rs", "w") as f: - f.write(content) - -with open("oscars/src/collectors/mark_sweep_branded/gc.rs", "r") as f: - content = f.read() - -# Remove Copy and change Clone, add Drop -target_copy = """impl<'gc, T: Trace + ?Sized + 'gc> Copy for Gc<'gc, T> {} -impl<'gc, T: Trace + ?Sized + 'gc> Clone for Gc<'gc, T> { - fn clone(&self) -> Self { - *self - } -}""" -replacement_copy = """impl<'gc, T: Trace + ?Sized + 'gc> Clone for Gc<'gc, T> { - fn clone(&self) -> Self { - let count = unsafe { &(*self.ptr.as_ptr().as_ptr()).0.root_count }; - count.set(count.get() + 1); - Self { - ptr: self.ptr, - _marker: core::marker::PhantomData, - } - } -} - -impl<'gc, T: Trace + ?Sized + 'gc> Drop for Gc<'gc, T> { - fn drop(&mut self) { - // SAFETY: The allocator does not unmap memory, so the block is still valid memory. - // Even if the object was collected and the block re-used, the `root_count` will just be corrupted. - // Wait, if it was collected, it means root_count WAS 0! So it won't be collected if we still have a Gc pointing to it! - // Because the Collector checks root_count during collection! - let count = unsafe { &(*self.ptr.as_ptr().as_ptr()).0.root_count }; - if count.get() > 0 { - count.set(count.get() - 1); - } - } -}""" -content = content.replace(target_copy, replacement_copy) - -with open("oscars/src/collectors/mark_sweep_branded/gc.rs", "w") as f: - f.write(content) -print("Patched Gc and GcBox") diff --git a/patch_oscars_gc_full.py b/patch_oscars_gc_full.py deleted file mode 100644 index 4fbce69..0000000 --- a/patch_oscars_gc_full.py +++ /dev/null @@ -1,225 +0,0 @@ -import re - -with open("oscars/src/collectors/mark_sweep_branded/gc_box.rs", "r") as f: - content = f.read() - -# Add root_count and finalize_fn to GcBox -target_gcbox = """ pub(crate) color: Cell, - /// Type-erased trace function. - pub(crate) trace_fn: TraceFn, - /// Type-erased finalize and free fn - pub(crate) drop_fn: DropFn,""" -replacement_gcbox = """ pub(crate) color: Cell, - /// Type-erased trace function. - pub(crate) trace_fn: TraceFn, - pub(crate) finalize_fn: unsafe fn(NonNull), - pub(crate) root_count: Cell, - /// Type-erased finalize and free fn - pub(crate) drop_fn: DropFn,""" -content = content.replace(target_gcbox, replacement_gcbox) - -target_new = """ pub(crate) fn new(value: T, trace_fn: TraceFn, drop_fn: DropFn, alloc_id: usize) -> Self { - Self { - color: Cell::new(GcColor::White), - trace_fn, - drop_fn, - alloc_id,""" -replacement_new = """ pub(crate) fn new(value: T, trace_fn: TraceFn, drop_fn: DropFn, alloc_id: usize) -> Self { - unsafe fn finalize_node(ptr: NonNull) { - let item_ptr = ptr.cast::>>(); - unsafe { - (*item_ptr.as_ptr()).0.value.run_finalizer(); - } - } - Self { - color: Cell::new(GcColor::White), - trace_fn, - finalize_fn: finalize_node::, - root_count: Cell::new(1), - drop_fn, - alloc_id,""" -content = content.replace(target_new, replacement_new) - -with open("oscars/src/collectors/mark_sweep_branded/gc_box.rs", "w") as f: - f.write(content) - -with open("oscars/src/collectors/mark_sweep_branded/gc.rs", "r") as f: - content = f.read() - -target_copy = """impl<'gc, T: Trace + ?Sized + 'gc> Copy for Gc<'gc, T> {} -impl<'gc, T: Trace + ?Sized + 'gc> Clone for Gc<'gc, T> { - fn clone(&self) -> Self { - *self - } -}""" -replacement_copy = """impl<'gc, T: Trace + ?Sized + 'gc> Clone for Gc<'gc, T> { - fn clone(&self) -> Self { - let count = unsafe { &(*self.ptr.as_ptr().as_ptr()).0.root_count }; - count.set(count.get() + 1); - Self { - ptr: self.ptr, - _marker: core::marker::PhantomData, - } - } -} - -impl<'gc, T: Trace + ?Sized + 'gc> Drop for Gc<'gc, T> { - fn drop(&mut self) { - let count = unsafe { &(*self.ptr.as_ptr().as_ptr()).0.root_count }; - if count.get() > 0 { - count.set(count.get() - 1); - } - } -}""" -content = content.replace(target_copy, replacement_copy) - -with open("oscars/src/collectors/mark_sweep_branded/gc.rs", "w") as f: - f.write(content) - -with open("oscars/src/collectors/mark_sweep_branded/mod.rs", "r") as f: - content = f.read() - -# Update drop_node to NOT call finalizer -target_drop = """pub(crate) unsafe fn drop_node( - pool: &mut PoolAllocator<'static>, - ptr: core::ptr::NonNull, -) { - let item_ptr = ptr.cast::>>(); - unsafe { - // Run finalizer - (*item_ptr.as_ptr()).0.value.run_finalizer(); - // Free item - pool.free(item_ptr); - } -}""" -replacement_drop = """pub(crate) unsafe fn drop_node( - pool: &mut PoolAllocator<'static>, - ptr: core::ptr::NonNull, -) { - let item_ptr = ptr.cast::>>(); - unsafe { - // Free item - pool.free(item_ptr); - } -}""" -content = content.replace(target_drop, replacement_drop) - -# Update sweep to run finalizers first, and trace root_count -target_sweep = """ let mut tracer = Tracer::new(); - - trace_external(&mut tracer); - - for link_ptr in self.sentinel.iter() {""" -replacement_sweep = """ let mut tracer = Tracer::new(); - - trace_external(&mut tracer); - - for ptr in self.pool.borrow().iter_live_slots() { - unsafe { - let gc_box = &(*ptr.cast::>>().as_ptr()).0; - if gc_box.root_count.get() > 0 { - (gc_box.trace_fn)(ptr, &mut tracer); - } - } - } - - for link_ptr in self.sentinel.iter() {""" -content = content.replace(target_sweep, replacement_sweep) - -target_dead = """ // Phase 3: sweep all slots. Collect unmarked ones, then invalidate and free them. - use crate::alloc::mempool3::PoolItem; - let dead: Vec<(NonNull, DropFn)> = { - let pool = self.pool.borrow(); - pool.iter_live_slots() - .filter_map(|ptr| unsafe { - let gc_box = &(*ptr.cast::>>().as_ptr()).0; - if gc_box.color.get() == GcColor::Black { - gc_box.color.set(GcColor::White); - None - } else { - Some((ptr, gc_box.drop_fn)) - } - }) - .collect() - }; - { - let mut pool = self.pool.borrow_mut(); - for (ptr, drop_fn) in dead { - unsafe { - (*ptr.cast::>>().as_ptr()).0.alloc_id = - GcBox::<()>::FREED_ALLOC_ID; - (drop_fn)(&mut pool, ptr); - } - } - }""" -replacement_dead = """ // Phase 3: sweep all slots. Collect unmarked ones, then invalidate and free them. - use crate::alloc::mempool3::PoolItem; - let dead: Vec<(NonNull, DropFn, unsafe fn(NonNull))> = { - let pool = self.pool.borrow(); - pool.iter_live_slots() - .filter_map(|ptr| unsafe { - let gc_box = &(*ptr.cast::>>().as_ptr()).0; - if gc_box.color.get() == GcColor::Black { - gc_box.color.set(GcColor::White); - None - } else { - Some((ptr, gc_box.drop_fn, gc_box.finalize_fn)) - } - }) - .collect() - }; - - for (ptr, _, finalize_fn) in &dead { - unsafe { - (finalize_fn)(*ptr); - } - } - - { - let mut pool = self.pool.borrow_mut(); - for (ptr, drop_fn, _) in dead { - unsafe { - (*ptr.cast::>>().as_ptr()).0.alloc_id = - GcBox::<()>::FREED_ALLOC_ID; - (drop_fn)(&mut pool, ptr); - } - } - }""" -content = content.replace(target_dead, replacement_dead) - -# Update Collector::drop -target_drop_coll = """ // Then iterate through pool and call drop_fn for all allocated items - for item in self.pool.borrow_mut().iter() { - unsafe { - let node = item.cast::>>(); - let drop_fn = (*node.as_ptr()).0.drop_fn; - (*node.as_ptr()).0.alloc_id = GcBox::<()>::FREED_ALLOC_ID; - (drop_fn)(&mut self.pool.borrow_mut(), item); - } - }""" -replacement_drop_coll = """ // Then iterate through pool and call drop_fn for all allocated items - // Wait, we need to run finalizers first here too! - let dead: Vec<(NonNull, DropFn, unsafe fn(NonNull))> = self.pool.borrow_mut().iter().map(|item| unsafe { - let node = item.cast::>>(); - let drop_fn = (*node.as_ptr()).0.drop_fn; - let finalize_fn = (*node.as_ptr()).0.finalize_fn; - (item, drop_fn, finalize_fn) - }).collect(); - for (item, _, finalize_fn) in &dead { - unsafe { - (finalize_fn)(*item); - } - } - for (item, drop_fn, _) in dead { - unsafe { - let node = item.cast::>>(); - (*node.as_ptr()).0.alloc_id = GcBox::<()>::FREED_ALLOC_ID; - (drop_fn)(&mut self.pool.borrow_mut(), item); - } - }""" -content = content.replace(target_drop_coll, replacement_drop_coll) - -with open("oscars/src/collectors/mark_sweep_branded/mod.rs", "w") as f: - f.write(content) - -print("Patched oscars full!") From ba0dff7d09326c83ae2173fbc76a357ed1b1f6a1 Mon Sep 17 00:00:00 2001 From: shruti2522 Date: Sat, 29 Aug 2026 13:10:56 +0000 Subject: [PATCH 8/8] fix: test compile errors after removing Copy from Gc --- Cargo.lock | 4 ---- oscars/src/alloc/mempool3/tests.rs | 2 +- oscars/src/collectors/mark_sweep_branded/mod.rs | 15 +++++++++++---- .../mark_sweep_branded/tests/ephemeron.rs | 6 +++--- .../tests/ui/gc_cannot_store_outer_scopr.stderr | 2 +- oscars/src/collectors/mark_sweep_branded/trace.rs | 5 ++++- 6 files changed, 20 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 49f2604..b7b562f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -838,7 +838,3 @@ name = "zmij" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" - -[[patch.unused]] -name = "boa_string" -version = "1.0.0-dev" diff --git a/oscars/src/alloc/mempool3/tests.rs b/oscars/src/alloc/mempool3/tests.rs index 2855fe2..f2c72d7 100644 --- a/oscars/src/alloc/mempool3/tests.rs +++ b/oscars/src/alloc/mempool3/tests.rs @@ -243,7 +243,7 @@ fn alloc_large_object_success() { struct LargeObject { _data: [u8; 3000], } - let a = allocator + let _a = allocator .try_alloc(LargeObject { _data: [0; 3000] }) .unwrap(); assert_eq!(allocator.pools_len(), 1); diff --git a/oscars/src/collectors/mark_sweep_branded/mod.rs b/oscars/src/collectors/mark_sweep_branded/mod.rs index c363bba..7973db8 100644 --- a/oscars/src/collectors/mark_sweep_branded/mod.rs +++ b/oscars/src/collectors/mark_sweep_branded/mod.rs @@ -170,11 +170,18 @@ impl Collector { for ptr in self.pool.borrow().iter_live_slots() { unsafe { - let gc_box = &(*ptr.cast::>>().as_ptr()).0; + let gc_box = &(*ptr + .cast::>>() + .as_ptr()) + .0; if gc_box.root_count.get() > 0 { let trace_fn_ptr = gc_box.trace_fn as *const (); if trace_fn_ptr.is_null() { - panic!("Collector::sweep: trace_fn is NULL for rooted object! alloc_id: {}, root_count: {}", gc_box.alloc_id, gc_box.root_count.get()); + panic!( + "Collector::sweep: trace_fn is NULL for rooted object! alloc_id: {}, root_count: {}", + gc_box.alloc_id, + gc_box.root_count.get() + ); } (gc_box.trace_fn)(ptr, &mut tracer); } @@ -230,13 +237,13 @@ impl Collector { }) .collect() }; - + for (ptr, _, finalize_fn) in &dead { unsafe { (finalize_fn)(*ptr); } } - + { let mut pool = self.pool.borrow_mut(); for (ptr, drop_fn, _) in dead { diff --git a/oscars/src/collectors/mark_sweep_branded/tests/ephemeron.rs b/oscars/src/collectors/mark_sweep_branded/tests/ephemeron.rs index c68a640..d2f22f8 100644 --- a/oscars/src/collectors/mark_sweep_branded/tests/ephemeron.rs +++ b/oscars/src/collectors/mark_sweep_branded/tests/ephemeron.rs @@ -6,7 +6,7 @@ fn ephemeron_value_survives_when_key_is_rooted() { let (root_key, eph) = ctx.mutate(|cx| { let key = cx.try_alloc(1u32).unwrap(); let value = cx.try_alloc(42u32).unwrap(); - let root_key = cx.root(key).unwrap(); + let root_key = cx.root(key.clone()).unwrap(); let eph = cx.alloc_ephemeron(&key, value); (root_key, eph) }); @@ -54,8 +54,8 @@ fn ephemeron_chain_fixpoint() { let a = cx.try_alloc(1u32).unwrap(); let b = cx.try_alloc(2u32).unwrap(); let c = cx.try_alloc(3u32).unwrap(); - let root_a = cx.root(a).unwrap(); - let eph_ab = cx.alloc_ephemeron(&a, b); + let root_a = cx.root(a.clone()).unwrap(); + let eph_ab = cx.alloc_ephemeron(&a, b.clone()); let eph_bc = cx.alloc_ephemeron(&b, c); (root_a, eph_ab, eph_bc) }); diff --git a/oscars/src/collectors/mark_sweep_branded/tests/ui/gc_cannot_store_outer_scopr.stderr b/oscars/src/collectors/mark_sweep_branded/tests/ui/gc_cannot_store_outer_scopr.stderr index 08e66af..1400e47 100644 --- a/oscars/src/collectors/mark_sweep_branded/tests/ui/gc_cannot_store_outer_scopr.stderr +++ b/oscars/src/collectors/mark_sweep_branded/tests/ui/gc_cannot_store_outer_scopr.stderr @@ -8,4 +8,4 @@ error[E0521]: borrowed data escapes outside of closure | -- `cx` is a reference that is only valid in the closure body ... 23 | holder = Some(Holder { gc }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `cx` escapes the closure body here + | ^^^^^^ `cx` escapes the closure body here diff --git a/oscars/src/collectors/mark_sweep_branded/trace.rs b/oscars/src/collectors/mark_sweep_branded/trace.rs index 99a7f23..09e3bd9 100644 --- a/oscars/src/collectors/mark_sweep_branded/trace.rs +++ b/oscars/src/collectors/mark_sweep_branded/trace.rs @@ -98,7 +98,10 @@ impl<'a> Tracer<'a> { gc_box.color.set(GcColor::Gray); let trace_fn_ptr = gc_box.trace_fn as *const (); if trace_fn_ptr.is_null() { - panic!("Tracer::mark: trace_fn is NULL! alloc_id: {}", gc_box.alloc_id); + panic!( + "Tracer::mark: trace_fn is NULL! alloc_id: {}", + gc_box.alloc_id + ); } self.worklist .push((gc.ptr.as_ptr().cast::(), gc_box.trace_fn));