Selaa lähdekoodia

wip: remove bumpframe

Jonathan Kelley 3 vuotta sitten
vanhempi
commit
e4cda7c2cb

+ 6 - 12
packages/core/src/diff.rs

@@ -125,7 +125,7 @@ impl<'bump> DiffState<'bump> {
 impl<'bump> ScopeArena {
     pub fn diff_scope(&'bump self, state: &mut DiffState<'bump>, id: &ScopeId) {
         if let Some(component) = self.get_scope(&id) {
-            let (old, new) = (component.frames.wip_head(), component.frames.fin_head());
+            let (old, new) = (component.wip_head(), component.fin_head());
             state.stack.push(DiffInstruction::Diff { new, old });
             self.work(state, || false);
         }
@@ -392,9 +392,7 @@ impl<'bump> ScopeArena {
 
         if !vcomponent.can_memoize {
             let cur_scope = self.get_scope(&parent_idx).unwrap();
-            let extended = vcomponent as *const VComponent;
-            let extended: *const VComponent<'static> = unsafe { std::mem::transmute(extended) };
-
+            let extended = unsafe { std::mem::transmute(vcomponent) };
             cur_scope.items.get_mut().borrowed_props.push(extended);
         }
 
@@ -1296,12 +1294,8 @@ impl<'bump> ScopeArena {
         listener: &'bump Listener<'bump>,
         scope: &ScopeState,
     ) {
-        let long_listener: &'bump Listener<'static> = unsafe { std::mem::transmute(listener) };
-        scope
-            .items
-            .borrow_mut()
-            .listeners
-            .push(long_listener as *const _)
+        let long_listener = unsafe { std::mem::transmute(listener) };
+        scope.items.borrow_mut().listeners.push(long_listener)
     }
 
     fn attach_suspended_node_to_scope(
@@ -1315,12 +1309,12 @@ impl<'bump> ScopeArena {
             .and_then(|id| self.get_scope(&id))
         {
             // safety: this lifetime is managed by the logic on scope
-            let extended: &VSuspended<'static> = unsafe { std::mem::transmute(suspended) };
+            let extended = unsafe { std::mem::transmute(suspended) };
             scope
                 .items
                 .borrow_mut()
                 .suspended_nodes
-                .insert(suspended.task_id, extended as *const _);
+                .insert(suspended.task_id, extended);
         }
     }
 }

+ 0 - 2
packages/core/src/lib.rs

@@ -12,7 +12,6 @@ Navigating this crate:
 
 Some utilities
 */
-pub(crate) mod bumpframe;
 pub(crate) mod component;
 pub(crate) mod diff;
 pub(crate) mod diff_stack;
@@ -31,7 +30,6 @@ pub(crate) mod virtual_dom;
 pub mod debug_dom;
 
 pub(crate) mod innerlude {
-    pub(crate) use crate::bumpframe::*;
     pub use crate::component::*;
     pub(crate) use crate::diff::*;
     pub use crate::diff_stack::*;

+ 4 - 6
packages/core/src/nodes.rs

@@ -21,9 +21,8 @@ use std::{
 /// It is used during the diffing/rendering process as a runtime key into an existing set of nodes. The "render" key
 /// is essentially a unique key to guarantee safe usage of the Node.
 pub struct NodeLink {
-    frame_id: u32,
-    gen_id: u32,
-    scope_id: ScopeId,
+    pub(crate) gen_id: u32,
+    pub(crate) scope_id: ScopeId,
 }
 
 /// A composable "VirtualNode" to declare a User Interface in the Dioxus VirtualDOM.
@@ -197,7 +196,6 @@ impl<'src> VNode<'src> {
                 key: f.key,
             }),
             VNode::Linked(c) => VNode::Linked(NodeLink {
-                frame_id: c.frame_id,
                 gen_id: c.gen_id,
                 scope_id: c.scope_id,
             }),
@@ -222,8 +220,8 @@ impl Debug for VNode<'_> {
             VNode::Component(comp) => write!(s, "VComponent {{ fc: {:?}}}", comp.user_fc),
             VNode::Linked(c) => write!(
                 s,
-                "VCached {{ frame_id: {}, gen_id: {}, scope_id: {:?} }}",
-                c.frame_id, c.gen_id, c.scope_id
+                "VCached {{ gen_id: {}, scope_id: {:?} }}",
+                c.gen_id, c.scope_id
             ),
         }
     }

+ 27 - 43
packages/core/src/bumpframe.rs → packages/core/src/old/bumpframe.rs

@@ -3,67 +3,51 @@ use bumpalo::Bump;
 use std::cell::Cell;
 
 pub(crate) struct ActiveFrame {
-    // We use a "generation" for users of contents in the bump frames to ensure their data isn't broken
-    pub generation: Cell<usize>,
+    pub cur_generation: Cell<usize>,
 
     // The double-buffering situation that we will use
     pub frames: [BumpFrame; 2],
 }
 
-pub(crate) struct BumpFrame {
-    pub bump: Bump,
-    pub(crate) head_node: VNode<'static>,
-
-    #[cfg(test)]
-    // used internally for debugging
-    _name: &'static str,
-}
-
 impl ActiveFrame {
     pub fn new() -> Self {
         let b1 = Bump::new();
         let b2 = Bump::new();
 
         let frame_a = BumpFrame {
-            head_node: VNode::Fragment(VFragment {
-                key: None,
-                children: &[],
-            }),
             bump: b1,
-
-            #[cfg(test)]
-            _name: "wip",
+            generation: 0.into(),
         };
         let frame_b = BumpFrame {
-            head_node: VNode::Fragment(VFragment {
-                key: None,
-                children: &[],
-            }),
             bump: b2,
-
-            #[cfg(test)]
-            _name: "fin",
+            generation: 0.into(),
         };
+
         Self {
-            generation: 0.into(),
             frames: [frame_a, frame_b],
+            cur_generation: 0.into(),
         }
     }
 
-    pub unsafe fn reset_wip_frame(&mut self) {
-        self.wip_frame_mut().bump.reset()
+    pub unsafe fn reset_wip_frame(&self) {
+        // todo: unsafecell or something
+        let bump = self.wip_frame() as *const _ as *mut BumpFrame;
+        let g = &mut *bump;
+        g.bump.reset();
+
+        // self.wip_frame_mut().bump.reset()
     }
 
     /// The "work in progress frame" represents the frame that is currently being worked on.
     pub fn wip_frame(&self) -> &BumpFrame {
-        match self.generation.get() & 1 == 0 {
+        match self.cur_generation.get() & 1 == 0 {
             true => &self.frames[0],
             false => &self.frames[1],
         }
     }
 
     pub fn wip_frame_mut(&mut self) -> &mut BumpFrame {
-        match self.generation.get() & 1 == 0 {
+        match self.cur_generation.get() & 1 == 0 {
             true => &mut self.frames[0],
             false => &mut self.frames[1],
         }
@@ -71,26 +55,26 @@ impl ActiveFrame {
 
     /// The finished frame represents the frame that has been "finished" and cannot be modified again
     pub fn finished_frame(&self) -> &BumpFrame {
-        match self.generation.get() & 1 == 1 {
+        match self.cur_generation.get() & 1 == 1 {
             true => &self.frames[0],
             false => &self.frames[1],
         }
     }
 
-    /// Give out our self-referential item with our own borrowed lifetime
-    pub fn fin_head<'b>(&'b self) -> &'b VNode<'b> {
-        let cur_head = &self.finished_frame().head_node;
-        unsafe { std::mem::transmute::<&VNode<'static>, &VNode<'b>>(cur_head) }
-    }
+    // /// Give out our self-referential item with our own borrowed lifetime
+    // pub fn fin_head<'b>(&'b self) -> &'b VNode<'b> {
+    //     let cur_head = &self.finished_frame().head_node;
+    //     unsafe { std::mem::transmute::<&VNode<'static>, &VNode<'b>>(cur_head) }
+    // }
 
-    /// Give out our self-referential item with our own borrowed lifetime
-    pub fn wip_head<'b>(&'b self) -> &'b VNode<'b> {
-        let cur_head = &self.wip_frame().head_node;
-        unsafe { std::mem::transmute::<&VNode<'static>, &VNode<'b>>(cur_head) }
-    }
+    // /// Give out our self-referential item with our own borrowed lifetime
+    // pub fn wip_head<'b>(&'b self) -> &'b VNode<'b> {
+    //     let cur_head = &self.wip_frame().head_node;
+    //     unsafe { std::mem::transmute::<&VNode<'static>, &VNode<'b>>(cur_head) }
+    // }
 
     pub fn cycle_frame(&mut self) {
-        self.generation.set(self.generation.get() + 1);
+        self.cur_generation.set(self.cur_generation.get() + 1);
     }
 }
 
@@ -117,6 +101,6 @@ mod tests {
             assert_eq!(fin._name, "wip");
             frames.cycle_frame();
         }
-        assert_eq!(frames.generation.get(), 10);
+        assert_eq!(frames.cur_generation.get(), 10);
     }
 }

+ 134 - 140
packages/core/src/scope.rs

@@ -47,6 +47,8 @@ pub struct ScopeState {
     // safety:
     //
     // pointers to scopes are *always* valid since they are bump allocated and never freed until this scope is also freed
+    // this is just a bit of a hack to not need an Rc to the ScopeArena.
+    // todo: replace this will ScopeId and provide a connection to scope arena directly
     pub(crate) parent_scope: Option<*mut ScopeState>,
 
     pub(crate) our_arena_idx: ScopeId,
@@ -57,11 +59,14 @@ pub struct ScopeState {
 
     pub(crate) is_subtree_root: Cell<bool>,
 
-    // Nodes
-    pub(crate) frames: ActiveFrame,
+    // The double-buffering situation that we will use
+    pub(crate) frames: [Bump; 2],
 
     pub(crate) vcomp: *const VComponent<'static>,
 
+    pub(crate) old_root: RefCell<Option<NodeLink>>,
+    pub(crate) new_root: RefCell<Option<NodeLink>>,
+
     /*
     we care about:
     - listeners (and how to call them when an event is triggered)
@@ -81,136 +86,20 @@ pub struct ScopeState {
 
 pub struct SelfReferentialItems<'a> {
     // nodes stored by "cx.render"
-    pub(crate) cached_nodes: Vec<VNode<'a>>,
+    pub(crate) cached_nodes_old: Vec<VNode<'a>>,
+    pub(crate) cached_nodes_new: Vec<VNode<'a>>,
 
-    pub(crate) generation: u32,
+    pub(crate) generation: Cell<u32>,
 
-    pub(crate) listeners: Vec<*const Listener<'a>>,
-    pub(crate) borrowed_props: Vec<*const VComponent<'a>>,
-    pub(crate) suspended_nodes: FxHashMap<u64, *const VSuspended<'a>>,
+    pub(crate) listeners: Vec<&'a Listener<'a>>,
+    pub(crate) borrowed_props: Vec<&'a VComponent<'a>>,
+    pub(crate) suspended_nodes: FxHashMap<u64, &'a VSuspended<'a>>,
     pub(crate) tasks: Vec<BumpBox<'a, dyn Future<Output = ()>>>,
     pub(crate) pending_effects: Vec<BumpBox<'a, dyn FnMut()>>,
 }
 
-pub struct ScopeVcomp {
-    // important things
-}
-
+// Public methods exposed to libraries and components
 impl ScopeState {
-    /// This method cleans up any references to data held within our hook list. This prevents mutable aliasing from
-    /// causing UB in our tree.
-    ///
-    /// This works by cleaning up our references from the bottom of the tree to the top. The directed graph of components
-    /// essentially forms a dependency tree that we can traverse from the bottom to the top. As we traverse, we remove
-    /// any possible references to the data in the hook list.
-    ///
-    /// References to hook data can only be stored in listeners and component props. During diffing, we make sure to log
-    /// all listeners and borrowed props so we can clear them here.
-    ///
-    /// This also makes sure that drop order is consistent and predictable. All resources that rely on being dropped will
-    /// be dropped.
-    pub(crate) fn ensure_drop_safety(&self) {
-        // make sure we drop all borrowed props manually to guarantee that their drop implementation is called before we
-        // run the hooks (which hold an &mut Reference)
-        // right now, we don't drop
-        self.items
-            .borrow_mut()
-            .borrowed_props
-            .drain(..)
-            .map(|li| unsafe { &*li })
-            .for_each(|comp| {
-                // First drop the component's undropped references
-                let scope_id = comp
-                    .associated_scope
-                    .get()
-                    .expect("VComponents should be associated with a valid Scope");
-
-                todo!("move this onto virtualdom");
-                // let scope = unsafe { &mut *scope_id };
-
-                // scope.ensure_drop_safety();
-
-                todo!("drop the component's props");
-                // let mut drop_props = comp.drop_props.borrow_mut().take().unwrap();
-                // drop_props();
-            });
-
-        // Now that all the references are gone, we can safely drop our own references in our listeners.
-        self.items
-            .borrow_mut()
-            .listeners
-            .drain(..)
-            .map(|li| unsafe { &*li })
-            .for_each(|listener| drop(listener.callback.borrow_mut().take()));
-    }
-
-    /// A safe wrapper around calling listeners
-    pub(crate) fn call_listener(&self, event: UserEvent, element: ElementId) {
-        let listners = &mut self.items.borrow_mut().listeners;
-
-        let raw_listener = listners.iter().find(|lis| {
-            let search = unsafe { &***lis };
-            if search.event == event.name {
-                let search_id = search.mounted_node.get();
-                search_id.map(|f| f == element).unwrap_or(false)
-            } else {
-                false
-            }
-        });
-
-        if let Some(raw_listener) = raw_listener {
-            let listener = unsafe { &**raw_listener };
-            let mut cb = listener.callback.borrow_mut();
-            if let Some(cb) = cb.as_mut() {
-                (cb)(event.event);
-            }
-        } else {
-            log::warn!("An event was triggered but there was no listener to handle it");
-        }
-    }
-
-    // General strategy here is to load up the appropriate suspended task and then run it.
-    // Suspended nodes cannot be called repeatedly.
-    pub(crate) fn call_suspended_node<'a>(&'a mut self, task_id: u64) {
-        let mut nodes = &mut self.items.get_mut().suspended_nodes;
-
-        if let Some(suspended) = nodes.remove(&task_id) {
-            let sus: &'a VSuspended<'static> = unsafe { &*suspended };
-            let sus: &'a VSuspended<'a> = unsafe { std::mem::transmute(sus) };
-            let mut boxed = sus.callback.borrow_mut().take().unwrap();
-            let new_node: Element = boxed();
-        }
-    }
-
-    // run the list of effects
-    pub(crate) fn run_effects(&mut self) {
-        // pub(crate) fn run_effects(&mut self, pool: &ResourcePool) {
-        for mut effect in self.items.get_mut().pending_effects.drain(..) {
-            effect();
-        }
-    }
-
-    pub(crate) fn new_subtree(&self) -> Option<u32> {
-        todo!()
-        // if self.is_subtree_root.get() {
-        //     None
-        // } else {
-        //     let cur = self.shared.cur_subtree.get();
-        //     self.shared.cur_subtree.set(cur + 1);
-        //     Some(cur)
-        // }
-    }
-
-    pub(crate) fn update_vcomp(&self, vcomp: &VComponent) {
-        let f: *const _ = vcomp;
-        todo!()
-        // self.vcomp = unsafe { std::mem::transmute(f) };
-    }
-
-    pub(crate) fn load_vcomp<'a>(&'a mut self) -> &'a VComponent<'a> {
-        unsafe { std::mem::transmute(&*self.vcomp) }
-    }
-
     /// Get the root VNode for this Scope.
     ///
     /// This VNode is the "entrypoint" VNode. If the component renders multiple nodes, then this VNode will be a fragment.
@@ -227,7 +116,7 @@ impl ScopeState {
     /// }
     /// ```
     pub fn root_node(&self) -> &VNode {
-        self.frames.fin_head()
+        self.fin_head()
     }
 
     /// Get the subtree ID that this scope belongs to.
@@ -316,7 +205,7 @@ impl ScopeState {
         let chan = self.sender.clone();
         let id = self.scope_id();
         Rc::new(move || {
-            chan.unbounded_send(SchedulerMsg::Immediate(id));
+            let _ = chan.unbounded_send(SchedulerMsg::Immediate(id));
         })
     }
 
@@ -328,7 +217,7 @@ impl ScopeState {
     pub fn schedule_update_any(&self) -> Rc<dyn Fn(ScopeId)> {
         let chan = self.sender.clone();
         Rc::new(move |id| {
-            chan.unbounded_send(SchedulerMsg::Immediate(id));
+            let _ = chan.unbounded_send(SchedulerMsg::Immediate(id));
         })
     }
 
@@ -343,17 +232,14 @@ impl ScopeState {
     ///
     /// `ScopeId` is not unique for the lifetime of the VirtualDom - a ScopeId will be reused if a component is unmounted.
     pub fn needs_update_any(&self, id: ScopeId) {
-        self.sender
-            .unbounded_send(SchedulerMsg::Immediate(id))
-            .unwrap();
+        let _ = self.sender.unbounded_send(SchedulerMsg::Immediate(id));
     }
 
     /// Get the [`ScopeId`] of a mounted component.
     ///
     /// `ScopeId` is not unique for the lifetime of the VirtualDom - a ScopeId will be reused if a component is unmounted.
     pub fn bump(&self) -> &Bump {
-        let bump = &self.frames.wip_frame().bump;
-        bump
+        &self.wip_frame()
     }
 
     /// Take a lazy VNode structure and actually build it with the context of the VDom's efficient VNode allocator.
@@ -363,20 +249,28 @@ impl ScopeState {
     /// ## Example
     ///
     /// ```ignore
-    /// fn Component(cx: Context<()>) -> VNode {
+    /// fn Component(cx: Scope, props: &Props) -> Element {
     ///     // Lazy assemble the VNode tree
-    ///     let lazy_tree = html! {<div> "Hello World" </div>};
+    ///     let lazy_nodes = rsx!("hello world");
     ///
     ///     // Actually build the tree and allocate it
     ///     cx.render(lazy_tree)
     /// }
     ///```
     pub fn render<'src>(&'src self, lazy_nodes: Option<LazyNodes<'src, '_>>) -> Option<NodeLink> {
-        todo!()
-        // ) -> Option<VNode<'src>> {
-        // let bump = &self.frames.wip_frame().bump;
-        // let factory = NodeFactory { bump };
-        // lazy_nodes.map(|f| f.call(factory))
+        let bump = &self.wip_frame();
+        let factory = NodeFactory { bump };
+        let node = lazy_nodes.map(|f| f.call(factory))?;
+
+        self.items
+            .borrow_mut()
+            .cached_nodes_old
+            .push(unsafe { std::mem::transmute(node) });
+
+        Some(NodeLink {
+            gen_id: self.items.borrow().generation.get(),
+            scope_id: self.our_arena_idx,
+        })
     }
 
     /// Push an effect to be ran after the component has been successfully mounted to the dom
@@ -544,3 +438,103 @@ Consult the `rules of hooks` to understand how to use hooks properly.
 You likely used the hook in a conditional. Hooks rely on consistent ordering between renders.
 Functions prefixed with "use" should never be called conditionally.
 "###;
+
+// Important internal methods
+impl ScopeState {
+    /// Give out our self-referential item with our own borrowed lifetime
+    pub(crate) fn fin_head<'b>(&'b self) -> &'b VNode<'b> {
+        todo!()
+        // let cur_head = &self.finished_frame().head_node;
+        // unsafe { std::mem::transmute::<&VNode<'static>, &VNode<'b>>(cur_head) }
+    }
+
+    /// Give out our self-referential item with our own borrowed lifetime
+    pub(crate) fn wip_head<'b>(&'b self) -> &'b VNode<'b> {
+        todo!()
+        // let cur_head = &self.wip_frame().head_node;
+        // unsafe { std::mem::transmute::<&VNode<'static>, &VNode<'b>>(cur_head) }
+    }
+
+    /// The "work in progress frame" represents the frame that is currently being worked on.
+    pub(crate) fn wip_frame(&self) -> &Bump {
+        todo!()
+        // match self.cur_generation.get() & 1 == 0 {
+        //     true => &self.frames[0],
+        //     false => &self.frames[1],
+        // }
+    }
+
+    pub unsafe fn reset_wip_frame(&self) {
+        // todo: unsafecell or something
+        let bump = self.wip_frame() as *const _ as *mut Bump;
+        let g = &mut *bump;
+        g.reset();
+
+        // self.wip_frame_mut().bump.reset()
+    }
+
+    /// A safe wrapper around calling listeners
+    pub(crate) fn call_listener(&self, event: UserEvent, element: ElementId) {
+        let listners = &mut self.items.borrow_mut().listeners;
+
+        let listener = listners.iter().find(|lis| {
+            let search = lis;
+            if search.event == event.name {
+                let search_id = search.mounted_node.get();
+                search_id.map(|f| f == element).unwrap_or(false)
+            } else {
+                false
+            }
+        });
+
+        if let Some(listener) = listener {
+            let mut cb = listener.callback.borrow_mut();
+            if let Some(cb) = cb.as_mut() {
+                (cb)(event.event);
+            }
+        } else {
+            log::warn!("An event was triggered but there was no listener to handle it");
+        }
+    }
+
+    // General strategy here is to load up the appropriate suspended task and then run it.
+    // Suspended nodes cannot be called repeatedly.
+    pub(crate) fn call_suspended_node<'a>(&'a mut self, task_id: u64) {
+        let mut nodes = &mut self.items.get_mut().suspended_nodes;
+
+        if let Some(suspended) = nodes.remove(&task_id) {
+            let sus: &'a VSuspended<'static> = unsafe { &*suspended };
+            let sus: &'a VSuspended<'a> = unsafe { std::mem::transmute(sus) };
+            let mut boxed = sus.callback.borrow_mut().take().unwrap();
+            let new_node: Element = boxed();
+        }
+    }
+
+    // run the list of effects
+    pub(crate) fn run_effects(&mut self) {
+        for mut effect in self.items.get_mut().pending_effects.drain(..) {
+            effect();
+        }
+    }
+
+    pub(crate) fn new_subtree(&self) -> Option<u32> {
+        todo!()
+        // if self.is_subtree_root.get() {
+        //     None
+        // } else {
+        //     let cur = self.shared.cur_subtree.get();
+        //     self.shared.cur_subtree.set(cur + 1);
+        //     Some(cur)
+        // }
+    }
+
+    pub(crate) fn update_vcomp(&self, vcomp: &VComponent) {
+        let f: *const _ = vcomp;
+        todo!()
+        // self.vcomp = unsafe { std::mem::transmute(f) };
+    }
+
+    pub(crate) fn load_vcomp<'a>(&'a mut self) -> &'a VComponent<'a> {
+        unsafe { std::mem::transmute(&*self.vcomp) }
+    }
+}

+ 56 - 3
packages/core/src/scopearena.rs

@@ -65,7 +65,7 @@ impl ScopeArena {
                 height,
                 subtree: Cell::new(subtree),
                 is_subtree_root: Cell::new(false),
-                frames: ActiveFrame::new(),
+                frames: [Bump::default(), Bump::default()],
                 vcomp,
 
                 hooks: Default::default(),
@@ -77,9 +77,12 @@ impl ScopeArena {
                     suspended_nodes: Default::default(),
                     tasks: Default::default(),
                     pending_effects: Default::default(),
-                    cached_nodes: Default::default(),
+                    cached_nodes_old: Default::default(),
                     generation: Default::default(),
+                    cached_nodes_new: todo!(),
                 }),
+                old_root: todo!(),
+                new_root: todo!(),
             };
 
             let stable = self.bump.alloc(new_scope);
@@ -101,5 +104,55 @@ impl ScopeArena {
         todo!()
     }
 
-    // scopes never get dropepd
+    // These methods would normally exist on `scope` but they need access to *all* of the scopes
+
+    /// This method cleans up any references to data held within our hook list. This prevents mutable aliasing from
+    /// causing UB in our tree.
+    ///
+    /// This works by cleaning up our references from the bottom of the tree to the top. The directed graph of components
+    /// essentially forms a dependency tree that we can traverse from the bottom to the top. As we traverse, we remove
+    /// any possible references to the data in the hook list.
+    ///
+    /// References to hook data can only be stored in listeners and component props. During diffing, we make sure to log
+    /// all listeners and borrowed props so we can clear them here.
+    ///
+    /// This also makes sure that drop order is consistent and predictable. All resources that rely on being dropped will
+    /// be dropped.
+    pub(crate) fn ensure_drop_safety(&self, scope_id: &ScopeId) {
+        let scope = self.get_scope(scope_id).unwrap();
+
+        // make sure we drop all borrowed props manually to guarantee that their drop implementation is called before we
+        // run the hooks (which hold an &mut Reference)
+        // right now, we don't drop
+        scope
+            .items
+            .borrow_mut()
+            .borrowed_props
+            .drain(..)
+            .for_each(|comp| {
+                // First drop the component's undropped references
+                let scope_id = comp
+                    .associated_scope
+                    .get()
+                    .expect("VComponents should be associated with a valid Scope");
+
+                todo!("move this onto virtualdom");
+                // let scope = unsafe { &mut *scope_id };
+
+                // scope.ensure_drop_safety();
+
+                todo!("drop the component's props");
+                // let mut drop_props = comp.drop_props.borrow_mut().take().unwrap();
+                // drop_props();
+            });
+
+        // Now that all the references are gone, we can safely drop our own references in our listeners.
+        scope
+            .items
+            .borrow_mut()
+            .listeners
+            .drain(..)
+            .map(|li| unsafe { &*li })
+            .for_each(|listener| drop(listener.callback.borrow_mut().take()));
+    }
 }

+ 8 - 24
packages/core/src/virtual_dom.rs

@@ -36,8 +36,6 @@ use std::{
     rc::Rc,
 };
 
-use crate::innerlude::*;
-
 /// An integrated virtual node system that progresses events and diffs UI trees.
 ///
 /// Differences are converted into patches which a renderer can use to draw the UI.
@@ -423,7 +421,7 @@ impl VirtualDom {
 
                     if self.run_scope(&scopeid) {
                         let scope = self.scopes.get_scope(&scopeid).unwrap();
-                        let (old, new) = (scope.frames.wip_head(), scope.frames.fin_head());
+                        let (old, new) = (scope.wip_head(), scope.fin_head());
                         diff_state.stack.scope_stack.push(scopeid);
                         diff_state.stack.push(DiffInstruction::Diff { new, old });
                     }
@@ -543,7 +541,7 @@ impl VirtualDom {
         // Cycle to the next frame and then reset it
         // This breaks any latent references, invalidating every pointer referencing into it.
         // Remove all the outdated listeners
-        scope.ensure_drop_safety();
+        self.scopes.ensure_drop_safety(id);
 
         // Safety:
         // - We dropped the listeners, so no more &mut T can be used while these are held
@@ -551,11 +549,10 @@ impl VirtualDom {
         unsafe { scope.hooks.reset() };
 
         // Safety:
-        // - We've dropped all references to the wip bump frame
-        todo!("reset wip frame");
-        // unsafe { scope.frames.reset_wip_frame() };
+        // - We've dropped all references to the wip bump frame with "ensure_drop_safety"
+        unsafe { scope.reset_wip_frame() };
 
-        let items = scope.items.get_mut();
+        let mut items = scope.items.borrow_mut();
 
         // just forget about our suspended nodes while we're at it
         items.suspended_nodes.clear();
@@ -568,12 +565,12 @@ impl VirtualDom {
         log::debug!("Borrowed stuff is successfully cleared");
 
         // temporarily cast the vcomponent to the right lifetime
-        let vcomp = scope.load_vcomp();
+        // let vcomp = scope.load_vcomp();
 
         let render: &dyn Fn(&ScopeState) -> Element = todo!();
 
         // Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders.
-        if let Some(builder) = render(scope) {
+        if let Some(key) = render(scope) {
             todo!("attach the niode");
             // let new_head = builder.into_vnode(NodeFactory {
             //     bump: &scope.frames.wip_frame().bump,
@@ -589,19 +586,6 @@ impl VirtualDom {
             false
         }
     }
-
-    pub fn reserve_node(&self, node: &VNode) -> ElementId {
-        todo!()
-        // self.node_reservations.insert(id);
-    }
-
-    pub fn collect_garbage(&self, id: ElementId) {
-        todo!()
-    }
-
-    pub fn try_remove(&self, id: &ScopeId) -> Option<ScopeState> {
-        todo!()
-    }
 }
 
 pub enum SchedulerMsg {
@@ -627,7 +611,7 @@ pub struct UserEvent {
     /// The name that the renderer will use to mount the listener.
     pub name: &'static str,
 
-    /// The type of event
+    /// Event Data
     pub event: Box<dyn Any + Send>,
 }