diff --git a/.github/workflows/miri.yml b/.github/workflows/miri.yml index 4a467b9b2..f9ad3855f 100644 --- a/.github/workflows/miri.yml +++ b/.github/workflows/miri.yml @@ -86,8 +86,7 @@ jobs: # working-directory: tokio env: - # todo: disable memory leaks ignore - MIRIFLAGS: -Zmiri-disable-isolation -Zmiri-strict-provenance -Zmiri-retag-fields -Zmiri-ignore-leaks + MIRIFLAGS: -Zmiri-disable-isolation -Zmiri-strict-provenance -Zmiri-retag-fields PROPTEST_CASES: 10 # Cache the global cargo directory, but NOT the local `target` directory which diff --git a/packages/core/src/bump_frame.rs b/packages/core/src/bump_frame.rs index bf630c605..6927bd8bc 100644 --- a/packages/core/src/bump_frame.rs +++ b/packages/core/src/bump_frame.rs @@ -1,5 +1,5 @@ use crate::nodes::RenderReturn; -use crate::{Attribute, AttributeValue}; +use crate::{Attribute, AttributeValue, VComponent}; use bumpalo::Bump; use std::cell::RefCell; use std::cell::{Cell, UnsafeCell}; @@ -7,7 +7,10 @@ use std::cell::{Cell, UnsafeCell}; pub(crate) struct BumpFrame { pub bump: UnsafeCell, pub node: Cell<*const RenderReturn<'static>>, + + // The bump allocator will not call the destructor of the objects it allocated. Attributes and props need to have there destructor called, so we keep a list of them to drop before the bump allocator is reset. pub(crate) attributes_to_drop_before_reset: RefCell>>, + pub(crate) props_to_drop_before_reset: RefCell>>, } impl BumpFrame { @@ -17,6 +20,7 @@ impl BumpFrame { bump: UnsafeCell::new(bump), node: Cell::new(std::ptr::null()), attributes_to_drop_before_reset: Default::default(), + props_to_drop_before_reset: Default::default(), } } @@ -41,6 +45,10 @@ impl BumpFrame { .push(attribute); } + /// Reset the bump allocator and drop all the attributes and props that were allocated in it. + /// + /// # Safety + /// The caller must insure that no reference to anything allocated in the bump allocator is available after this function is called. pub(crate) unsafe fn reset(&self) { let mut attributes = self.attributes_to_drop_before_reset.borrow_mut(); attributes.drain(..).for_each(|attribute| { @@ -49,9 +57,20 @@ impl BumpFrame { _ = l.take(); } }); + let mut props = self.props_to_drop_before_reset.borrow_mut(); + props.drain(..).for_each(|prop| { + let prop = unsafe { &*prop }; + _ = prop.props.borrow_mut().take(); + }); unsafe { let bump = &mut *self.bump.get(); bump.reset(); } } } + +impl Drop for BumpFrame { + fn drop(&mut self) { + unsafe { self.reset() } + } +} diff --git a/packages/core/src/scopes.rs b/packages/core/src/scopes.rs index 3fd2aac30..f0bbd776c 100644 --- a/packages/core/src/scopes.rs +++ b/packages/core/src/scopes.rs @@ -367,12 +367,17 @@ impl<'src> ScopeState { } let mut props = self.borrowed_props.borrow_mut(); + let mut drop_props = self + .previous_frame() + .props_to_drop_before_reset + .borrow_mut(); for node in element.dynamic_nodes { if let DynamicNode::Component(comp) = node { + let unbounded = unsafe { std::mem::transmute(comp as *const VComponent) }; if !comp.static_props { - let unbounded = unsafe { std::mem::transmute(comp as *const VComponent) }; props.push(unbounded); } + drop_props.push(unbounded); } }