Sfoglia il codice sorgente

wip: move things into a "shared" object

Jonathan Kelley 3 anni fa
parent
commit
f644d7c441

+ 17 - 15
packages/core-macro/src/rsx/element.rs

@@ -109,20 +109,29 @@ impl Parse for Element<AS_HTML> {
             let name_str = name.to_string();
             stream.parse::<Token![=]>()?;
             if name_str.starts_with("on") {
-                todo!()
+                let inner;
+                syn::braced!(inner in stream);
+                let toks = inner.parse::<Expr>()?;
+                let ty = AttrType::EventTokens(toks);
+                listeners.push(ElementAttr {
+                    element_name: el_name.clone(),
+                    name,
+                    value: ty,
+                    namespace: None,
+                })
             } else {
                 match name_str.as_str() {
-                    "style" => todo!(),
-                    "key" => todo!(),
-                    "classes" => todo!(),
-                    "namespace" => todo!(),
-                    "ref" => todo!(),
-                    _ => {
+                    "style" => {}
+                    "key" => {}
+                    "classes" | "namespace" | "ref" | _ => {
                         let ty = if stream.peek(LitStr) {
                             let rawtext = stream.parse::<LitStr>().unwrap();
                             AttrType::BumpText(rawtext)
                         } else {
-                            let toks = stream.parse::<Expr>()?;
+                            // like JSX, we expect raw expressions
+                            let inner;
+                            syn::braced!(inner in stream);
+                            let toks = inner.parse::<Expr>()?;
                             AttrType::FieldTokens(toks)
                         };
                         attributes.push(ElementAttr {
@@ -134,13 +143,6 @@ impl Parse for Element<AS_HTML> {
                     }
                 }
             };
-            // if stream.peek(LitStr) {
-
-            // } else {
-            // }
-            // if name_str.starts_with("on") {}
-
-            // attributes.push(stream.parse()?);
         }
         stream.parse::<Token![>]>()?;
 

+ 1 - 1
packages/core/.vscode/settings.json

@@ -1,3 +1,3 @@
 {
-    "rust-analyzer.inlayHints.enable": false
+  "rust-analyzer.inlayHints.enable": true
 }

+ 109 - 35
packages/core/src/arena.rs

@@ -1,53 +1,97 @@
+use std::cell::{RefCell, RefMut};
 use std::{cell::UnsafeCell, rc::Rc};
 
+use crate::heuristics::*;
 use crate::innerlude::*;
+use futures_util::stream::FuturesUnordered;
 use slotmap::SlotMap;
 
-#[derive(Clone)]
-pub struct SharedArena {
-    pub components: Rc<UnsafeCell<ScopeMap>>,
+slotmap::new_key_type! {
+    // A dedicated key type for the all the scopes
+    pub struct ScopeId;
 }
-pub type ScopeMap = SlotMap<ScopeId, Scope>;
 
-enum MutStatus {
-    Immut,
-    Mut,
+slotmap::new_key_type! {
+    // A dedicated key type for every real element that the virtualdom creates.
+    //
+    // This is a slotmap key because we expect the "mirror" realdom to also maintain a slotmap mapping
+    // of virtual element to real element.
+    pub struct ElementId;
 }
 
-impl SharedArena {
-    pub fn new(arena: ScopeMap) -> Self {
-        let components = Rc::new(UnsafeCell::new(arena));
-        SharedArena { components }
+impl ElementId {
+    pub fn as_u64(self) -> u64 {
+        self.0.as_ffi()
     }
+}
 
-    /// THIS METHOD IS CURRENTLY UNSAFE
-    /// THERE ARE NO CHECKS TO VERIFY THAT WE ARE ALLOWED TO DO THIS
-    pub fn get(&self, idx: ScopeId) -> Option<&Scope> {
-        let inner = unsafe { &*self.components.get() };
-        inner.get(idx)
-    }
+type Shared<T> = Rc<RefCell<T>>;
 
-    /// THIS METHOD IS CURRENTLY UNSAFE
-    /// THERE ARE NO CHECKS TO VERIFY THAT WE ARE ALLOWED TO DO THIS
-    pub fn get_mut(&self, idx: ScopeId) -> Option<&mut Scope> {
-        let inner = unsafe { &mut *self.components.get() };
-        inner.get_mut(idx)
-    }
+/// These are resources shared among all the components and the virtualdom itself
+#[derive(Clone)]
+pub struct SharedResources {
+    pub components: Rc<UnsafeCell<SlotMap<ScopeId, Scope>>>,
 
-    fn inner(&self) -> &ScopeMap {
-        todo!()
+    pub event_queue: Shared<Vec<HeightMarker>>,
+
+    pub events: Shared<Vec<EventTrigger>>,
+
+    pub(crate) heuristics: Shared<HeuristicsEngine>,
+
+    pub(crate) tasks: Shared<FuturesUnordered<FiberTask>>,
+
+    /// We use a SlotSet to keep track of the keys that are currently being used.
+    /// However, we don't store any specific data since the "mirror"
+    pub raw_elements: Shared<SlotMap<ElementId, ()>>,
+
+    pub task_setter: Rc<dyn Fn(ScopeId)>,
+}
+
+impl SharedResources {
+    pub fn new() -> Self {
+        // preallocate 1000 elements and 20 scopes to avoid dynamic allocation
+        let components = Rc::new(UnsafeCell::new(
+            SlotMap::<ScopeId, Scope>::with_capacity_and_key(20),
+        ));
+        let raw_elements = SlotMap::<ElementId, ()>::with_capacity_and_key(1000);
+
+        let event_queue = Rc::new(RefCell::new(Vec::new()));
+        let tasks = Vec::new();
+        let heuristics = HeuristicsEngine::new();
+
+        let queue = event_queue.clone();
+        let _components = components.clone();
+        let task_setter = Rc::new(move |idx| {
+            let comps = unsafe { &*_components.get() };
+            if let Some(scope) = comps.get(idx) {
+                queue.borrow_mut().push(HeightMarker {
+                    height: scope.height,
+                    idx,
+                })
+            }
+        });
+
+        Self {
+            event_queue,
+            components,
+            tasks: Rc::new(RefCell::new(FuturesUnordered::new())),
+            events: Rc::new(RefCell::new(tasks)),
+            heuristics: Rc::new(RefCell::new(heuristics)),
+            raw_elements: Rc::new(RefCell::new(raw_elements)),
+            task_setter,
+        }
     }
 
-    fn inner_mut(&mut self) -> &mut ScopeMap {
-        todo!()
+    /// this is unsafe because the caller needs to track which other scopes it's already using
+    pub unsafe fn get_scope(&self, idx: ScopeId) -> Option<&Scope> {
+        let inner = &*self.components.get();
+        inner.get(idx)
     }
 
-    /// THIS METHOD IS CURRENTLY UNSAFE
-    /// THERE ARE NO CHECKS TO VERIFY THAT WE ARE ALLOWED TO DO THIS
-    pub fn with<T>(&self, f: impl FnOnce(&mut ScopeMap) -> T) -> Result<T> {
-        let inner = unsafe { &mut *self.components.get() };
-        Ok(f(inner))
-        // todo!()
+    /// this is unsafe because the caller needs to track which other scopes it's already using
+    pub unsafe fn get_sope_mut(&self, idx: ScopeId) -> Option<&mut Scope> {
+        let inner = &mut *self.components.get();
+        inner.get_mut(idx)
     }
 
     pub fn with_scope<'b, O: 'static>(
@@ -75,7 +119,37 @@ impl SharedArena {
             .ok_or_else(|| Error::FatalInternal("Scope not found"))
     }
 
-    unsafe fn inner_unchecked<'s>() -> &'s mut ScopeMap {
-        todo!()
+    pub fn reserve_node(&self) -> ElementId {
+        self.raw_elements.borrow_mut().insert(())
+    }
+
+    /// return the id, freeing the space of the original node
+    pub fn collect_garbage(&self, id: ElementId) {}
+
+    pub fn borrow_queue(&self) -> RefMut<Vec<HeightMarker>> {
+        self.event_queue.borrow_mut()
     }
+
+    pub fn insert_scope_with_key(&self, f: impl FnOnce(ScopeId) -> Scope) -> ScopeId {
+        let g = unsafe { &mut *self.components.get() };
+        g.insert_with_key(f)
+    }
+
+    pub fn schedule_update(&self) -> Rc<dyn Fn(ScopeId)> {
+        self.task_setter.clone()
+    }
+
+    pub fn submit_task(&self, task: FiberTask) -> TaskHandle {
+        self.tasks.borrow_mut().push(task);
+        TaskHandle {}
+    }
+}
+
+pub struct TaskHandle {}
+
+impl TaskHandle {
+    pub fn toggle(&self) {}
+    pub fn start(&self) {}
+    pub fn stop(&self) {}
+    pub fn restart(&self) {}
 }

+ 67 - 45
packages/core/src/bumpframe.rs

@@ -1,10 +1,10 @@
 use crate::innerlude::*;
 use bumpalo::Bump;
+use std::cell::Cell;
 
-use std::cell::RefCell;
 pub struct ActiveFrame {
     // We use a "generation" for users of contents in the bump frames to ensure their data isn't broken
-    pub generation: RefCell<usize>,
+    pub generation: Cell<usize>,
 
     // The double-buffering situation that we will use
     pub frames: [BumpFrame; 2],
@@ -13,78 +13,100 @@ pub struct ActiveFrame {
 pub struct BumpFrame {
     pub bump: Bump,
     pub head_node: VNode<'static>,
+
+    #[cfg(test)]
+    name: &'static str,
 }
 
 impl ActiveFrame {
     pub fn new() -> Self {
-        Self::from_frames(
-            BumpFrame {
-                bump: Bump::new(),
-                head_node: NodeFactory::static_text(""),
-            },
-            BumpFrame {
-                bump: Bump::new(),
-                head_node: NodeFactory::static_text(""),
-            },
-        )
-    }
+        let frame_a = BumpFrame {
+            bump: Bump::new(),
+            head_node: NodeFactory::unstable_place_holder(),
+
+            #[cfg(test)]
+            name: "old",
+        };
+        let frame_b = BumpFrame {
+            bump: Bump::new(),
+            head_node: NodeFactory::unstable_place_holder(),
 
-    pub fn from_frames(a: BumpFrame, b: BumpFrame) -> Self {
+            #[cfg(test)]
+            name: "new",
+        };
         Self {
             generation: 0.into(),
-            frames: [a, b],
+            frames: [frame_a, frame_b],
         }
     }
 
-    pub fn cur_frame(&self) -> &BumpFrame {
-        match *self.generation.borrow() & 1 == 0 {
+    pub fn prev_frame(&self) -> &BumpFrame {
+        match self.generation.get() & 1 == 0 {
             true => &self.frames[0],
             false => &self.frames[1],
         }
     }
-    pub fn cur_frame_mut(&mut self) -> &mut BumpFrame {
-        match *self.generation.borrow() & 1 == 0 {
+
+    pub fn prev_frame_mut(&mut self) -> &mut BumpFrame {
+        match self.generation.get() & 1 == 0 {
             true => &mut self.frames[0],
             false => &mut self.frames[1],
         }
     }
 
-    pub fn current_head_node<'b>(&'b self) -> &'b VNode<'b> {
-        let raw_node = match *self.generation.borrow() & 1 == 0 {
+    pub fn cur_frame(&self) -> &BumpFrame {
+        match self.generation.get() & 1 == 1 {
             true => &self.frames[0],
             false => &self.frames[1],
-        };
-
-        // Give out our self-referential item with our own borrowed lifetime
-        unsafe {
-            let unsafe_head = &raw_node.head_node;
-            let safe_node = std::mem::transmute::<&VNode<'static>, &VNode<'b>>(unsafe_head);
-            safe_node
         }
     }
 
-    pub fn prev_head_node<'b>(&'b self) -> &'b VNode<'b> {
-        let raw_node = match *self.generation.borrow() & 1 != 0 {
-            true => &self.frames[0],
-            false => &self.frames[1],
-        };
-
-        // Give out our self-referential item with our own borrowed lifetime
-        unsafe {
-            let unsafe_head = &raw_node.head_node;
-            let safe_node = std::mem::transmute::<&VNode<'static>, &VNode<'b>>(unsafe_head);
-            safe_node
+    pub fn cur_frame_mut(&mut self) -> &mut BumpFrame {
+        match self.generation.get() & 1 == 1 {
+            true => &mut self.frames[0],
+            false => &mut self.frames[1],
         }
     }
+    /// Give out our self-referential item with our own borrowed lifetime
+    pub fn current_head_node<'b>(&'b self) -> &'b VNode<'b> {
+        let cur_head = &self.cur_frame().head_node;
+        unsafe { std::mem::transmute::<&VNode<'static>, &VNode<'b>>(cur_head) }
+    }
 
-    pub fn old_frame_mut(&mut self) -> &mut BumpFrame {
-        match *self.generation.borrow() & 1 == 0 {
-            true => &mut self.frames[1],
-            false => &mut self.frames[0],
-        }
+    /// Give out our self-referential item with our own borrowed lifetime
+    pub fn prev_head_node<'b>(&'b self) -> &'b VNode<'b> {
+        let cur_head = &self.prev_frame().head_node;
+        unsafe { std::mem::transmute::<&VNode<'static>, &VNode<'b>>(cur_head) }
     }
 
     pub fn cycle_frame(&mut self) {
-        *self.generation.borrow_mut() += 1;
+        self.generation.set(self.generation.get() + 1);
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    //! These tests are bad. I don't have a good way of properly testing the ActiveFrame stuff
+    use super::*;
+
+    #[test]
+    fn test_bump_frame() {
+        let mut frames = ActiveFrame::new();
+
+        // just cycle a few times and make sure we get the right frames out
+        for _ in 0..5 {
+            let old = frames.prev_frame();
+            let new = frames.cur_frame();
+            assert_eq!(old.name, "old");
+            assert_eq!(new.name, "new");
+            frames.cycle_frame();
+
+            let old = frames.prev_frame();
+            let new = frames.cur_frame();
+            assert_eq!(old.name, "new");
+            assert_eq!(new.name, "old");
+            frames.cycle_frame();
+        }
+        assert_eq!(frames.generation.get(), 10);
     }
 }

+ 0 - 91
packages/core/src/childiter.rs

@@ -1,91 +0,0 @@
-/// This iterator iterates through a list of virtual children and only returns real children (Elements or Text).
-///
-/// This iterator is useful when it's important to load the next real root onto the top of the stack for operations like
-/// "InsertBefore".
-struct RealChildIterator<'a> {
-    scopes: &'a SharedArena,
-
-    // Heuristcally we should never bleed into 5 completely nested fragments/components
-    // Smallvec lets us stack allocate our little stack machine so the vast majority of cases are sane
-    stack: smallvec::SmallVec<[(u16, &'a VNode<'a>); 5]>,
-}
-
-impl<'a> RealChildIterator<'a> {
-    fn new(starter: &'a VNode<'a>, scopes: &'a SharedArena) -> Self {
-        Self {
-            scopes,
-            stack: smallvec::smallvec![(0, starter)],
-        }
-    }
-}
-
-impl<'a> Iterator for RealChildIterator<'a> {
-    type Item = &'a VNode<'a>;
-
-    fn next(&mut self) -> Option<&'a VNode<'a>> {
-        let mut should_pop = false;
-        let mut returned_node = None;
-        let mut should_push = None;
-
-        while returned_node.is_none() {
-            if let Some((count, node)) = self.stack.last_mut() {
-                match node {
-                    // We can only exit our looping when we get "real" nodes
-                    // This includes fragments and components when they're empty (have a single root)
-                    VNode::Element(_) | VNode::Text(_) => {
-                        // We've recursed INTO an element/text
-                        // We need to recurse *out* of it and move forward to the next
-                        should_pop = true;
-                        returned_node = Some(&**node);
-                    }
-
-                    // If we get a fragment we push the next child
-                    VNode::Fragment(frag) => {
-                        let subcount = *count as usize;
-
-                        if frag.children.len() == 0 {
-                            should_pop = true;
-                            returned_node = Some(&**node);
-                        }
-
-                        if subcount >= frag.children.len() {
-                            should_pop = true;
-                        } else {
-                            should_push = Some(&frag.children[subcount]);
-                        }
-                    }
-
-                    // Immediately abort suspended nodes - can't do anything with them yet
-                    // VNode::Suspended => should_pop = true,
-                    VNode::Suspended { real } => todo!(),
-
-                    // For components, we load their root and push them onto the stack
-                    VNode::Component(sc) => {
-                        let scope = self.scopes.try_get(sc.ass_scope.get().unwrap()).unwrap();
-
-                        // Simply swap the current node on the stack with the root of the component
-                        *node = scope.root();
-                    }
-                }
-            } else {
-                // If there's no more items on the stack, we're done!
-                return None;
-            }
-
-            if should_pop {
-                self.stack.pop();
-                if let Some((id, _)) = self.stack.last_mut() {
-                    *id += 1;
-                }
-                should_pop = false;
-            }
-
-            if let Some(push) = should_push {
-                self.stack.push((0, push));
-                should_push = None;
-            }
-        }
-
-        returned_node
-    }
-}

+ 43 - 1
packages/core/src/component.rs

@@ -5,8 +5,40 @@
 //! if the type suppports PartialEq. The Properties trait is used by the rsx! and html! macros to generate the type-safe builder
 //! that ensures compile-time required and optional fields on cx.
 
-use crate::innerlude::{Context, DomTree, LazyNodes, VNode, FC};
+use crate::innerlude::{Context, DomTree, LazyNodes, FC};
 
+/// Every "Props" used for a component must implement the `Properties` trait. This trait gives some hints to Dioxus
+/// on how to memoize the props and some additional optimizations that can be made. We strongly encourage using the
+/// derive macro to implement the `Properties` trait automatically as guarantee that your memoization strategy is safe.
+///
+/// If your props are 'static, then Dioxus will require that they also be PartialEq for the derived memoize strategy. However,
+/// if your props borrow data, then the memoization strategy will simply default to "false" and the PartialEq will be ignored.
+/// This tends to be useful when props borrow something that simply cannot be compared (IE a reference to a closure);
+///
+/// By default, the memoization strategy is very conservative, but can be tuned to be more aggressive manually. However,
+/// this is only safe if the props are 'static - otherwise you might borrow references after-free.
+///
+/// We strongly suggest that any changes to memoization be done at the "PartialEq" level for 'static props. Additionally,
+/// we advise the use of smart pointers in cases where memoization is important.
+///
+/// ## Example
+///
+/// For props that are 'static:
+/// ```rust ignore
+/// #[derive(Props, PartialEq)]
+/// struct MyProps {
+///     data: String
+/// }
+/// ```
+///
+/// For props that borrow:
+///
+/// ```rust ignore
+/// #[derive(Props)]
+/// struct MyProps<'a >{
+///     data: &'a str
+/// }
+/// ```
 pub trait Properties: Sized {
     type Builder;
     const IS_STATIC: bool = false;
@@ -50,6 +82,16 @@ pub fn fc_to_builder<T: Properties>(_: FC<T>) -> T::Builder {
 ///
 /// Fragments are incredibly useful when necessary, but *do* add cost in the diffing phase.
 /// Try to avoid nesting fragments if you can. Infinitely nested Fragments *will* cause diffing to crash.
+///
+/// This function defines a dedicated `Fragment` component that can be used to create inline fragments in the RSX macro.
+///
+/// You want to use this free-function when your fragment needs a key and simply returning multiple nodes from rsx! won't cut it.
+///
+/// ```rust
+/// rsx!{
+///     Fragment { key: "abc" }
+/// }
+/// ```
 #[allow(non_upper_case_globals, non_snake_case)]
 pub fn Fragment<'a>(cx: Context<'a, ()>) -> DomTree<'a> {
     cx.render(LazyNodes::new(move |f| f.fragment_from_iter(cx.children())))

+ 28 - 27
packages/core/src/context.rs

@@ -99,12 +99,21 @@ impl<'src, P> Context<'src, P> {
     /// }
     /// ```
     pub fn children(&self) -> &'src [VNode<'src>] {
-        self.scope.child_nodes
+        self.scope.child_nodes()
     }
 
     /// Create a subscription that schedules a future render for the reference component
+    ///
+    /// ## Notice: you should prefer using prepare_update and get_scope_id
+    ///
     pub fn schedule_update(&self) -> Rc<dyn Fn() + 'static> {
-        self.scope.event_channel.clone()
+        let cb = self.scope.vdom.schedule_update();
+        let id = self.get_scope_id();
+        Rc::new(move || cb(id))
+    }
+
+    fn prepare_update(&self) -> Rc<dyn Fn(ScopeId)> {
+        self.scope.vdom.schedule_update()
     }
 
     pub fn schedule_effect(&self) -> Rc<dyn Fn() + 'static> {
@@ -142,7 +151,7 @@ impl<'src, P> Context<'src, P> {
         let scope_ref = self.scope;
         let listener_id = &scope_ref.listener_idx;
         Some(lazy_nodes.into_vnode(NodeFactory {
-            scope_ref,
+            scope: scope_ref,
             listener_id,
         }))
     }
@@ -297,7 +306,7 @@ Any function prefixed with "use" should not be called conditionally.
                     } else {
                         match inner.parent_idx {
                             Some(parent_id) => {
-                                scope = inner.arena_link.get(parent_id);
+                                scope = unsafe { inner.vdom.get_scope(parent_id) };
                             }
                             None => break,
                         }
@@ -329,8 +338,7 @@ Any function prefixed with "use" should not be called conditionally.
     ///
     ///
     pub fn submit_task(&self, task: FiberTask) -> TaskHandle {
-        (self.scope.task_submitter)(task);
-        TaskHandle { _p: PhantomData {} }
+        self.scope.vdom.submit_task(task)
     }
 
     /// Awaits the given task, forcing the component to re-render when the value is ready.
@@ -341,13 +349,14 @@ Any function prefixed with "use" should not be called conditionally.
     pub fn use_task<Out, Fut, Init>(
         self,
         task_initializer: Init,
-    ) -> (TaskHandle<'src>, &'src Option<Out>)
+    ) -> (&'src TaskHandle, &'src Option<Out>)
     where
         Out: 'static,
         Fut: Future<Output = Out> + 'static,
         Init: FnOnce() -> Fut + 'src,
     {
         struct TaskHook<T> {
+            handle: TaskHandle,
             task_dump: Rc<RefCell<Option<T>>>,
             value: Option<T>,
         }
@@ -360,12 +369,15 @@ Any function prefixed with "use" should not be called conditionally.
                 let task_dump = Rc::new(RefCell::new(None));
 
                 let slot = task_dump.clone();
-                let update = self.schedule_update();
+
+                let updater = self.prepare_update();
+                let update_id = self.get_scope_id();
+
                 let originator = self.scope.our_arena_idx.clone();
 
-                self.submit_task(Box::pin(task_fut.then(move |output| async move {
+                let handle = self.submit_task(Box::pin(task_fut.then(move |output| async move {
                     *slot.as_ref().borrow_mut() = Some(output);
-                    update();
+                    updater(update_id);
                     EventTrigger {
                         event: VirtualEvent::AsyncEvent { hook_idx },
                         originator,
@@ -377,13 +389,14 @@ Any function prefixed with "use" should not be called conditionally.
                 TaskHook {
                     task_dump,
                     value: None,
+                    handle,
                 }
             },
             |hook| {
                 if let Some(val) = hook.task_dump.as_ref().borrow_mut().take() {
                     hook.value = Some(val);
                 }
-                (TaskHandle { _p: PhantomData }, &hook.value)
+                (&hook.handle, &hook.value)
             },
             |_| {},
         )
@@ -393,7 +406,7 @@ Any function prefixed with "use" should not be called conditionally.
 pub(crate) struct SuspenseHook {
     pub value: Rc<RefCell<Option<Box<dyn Any>>>>,
     pub callback: SuspendedCallback,
-    pub dom_node_id: Rc<Cell<RealDomNode>>,
+    pub dom_node_id: Rc<Cell<Option<ElementId>>>,
 }
 type SuspendedCallback = Box<dyn for<'a> Fn(SuspendedContext<'a>) -> DomTree<'a>>;
 
@@ -422,7 +435,7 @@ impl<'src, P> Context<'src, P> {
             move |hook_idx| {
                 let value = Rc::new(RefCell::new(None));
 
-                let dom_node_id = Rc::new(RealDomNode::empty_cell());
+                let dom_node_id = Rc::new(empty_cell());
                 let domnode = dom_node_id.clone();
 
                 let slot = value.clone();
@@ -438,7 +451,7 @@ impl<'src, P> Context<'src, P> {
                         None => {
                             //
                             Some(VNode {
-                                dom_id: RealDomNode::empty_cell(),
+                                dom_id: empty_cell(),
                                 key: None,
                                 kind: VNodeKind::Suspended {
                                     node: domnode.clone(),
@@ -495,20 +508,8 @@ impl<'src> SuspendedContext<'src> {
         let scope_ref = self.inner.scope;
         let listener_id = &scope_ref.listener_idx;
         Some(lazy_nodes.into_vnode(NodeFactory {
-            scope_ref,
+            scope: scope_ref,
             listener_id,
         }))
     }
 }
-
-#[derive(Clone, Copy)]
-pub struct TaskHandle<'src> {
-    _p: PhantomData<&'src ()>,
-}
-
-impl<'src> TaskHandle<'src> {
-    pub fn toggle(&self) {}
-    pub fn start(&self) {}
-    pub fn stop(&self) {}
-    pub fn restart(&self) {}
-}

+ 91 - 75
packages/core/src/diff.rs

@@ -15,7 +15,7 @@
 //! whose keys can be converted to u64 on FFI boundaries.
 //!
 //! When new nodes are created through `render`, they won't know which real node they correspond to. During diffing, we
-//! always make sure to copy over the ID. If we don't do this properly, the realdomnode will be populated incorrectly and
+//! always make sure to copy over the ID. If we don't do this properly, the ElementId will be populated incorrectly and
 //! brick the user's page.
 //!
 //! ## Subtree Memoization
@@ -48,11 +48,11 @@
 //! More info on how to improve this diffing algorithm:
 //!  - https://hacks.mozilla.org/2019/03/fast-bump-allocated-virtual-doms-with-rust-and-wasm/
 
-use crate::{arena::SharedArena, innerlude::*, tasks::TaskQueue};
-use fxhash::FxHashSet;
+use crate::{arena::SharedResources, innerlude::*};
+use fxhash::{FxHashMap, FxHashSet};
 use smallvec::{smallvec, SmallVec};
 
-use std::any::Any;
+use std::{any::Any, borrow::Borrow};
 
 /// The accompanying "real dom" exposes an imperative API for controlling the UI layout
 ///
@@ -66,21 +66,35 @@ use std::any::Any;
 /// any modifications that follow. This technique enables the diffing algorithm to avoid directly handling or storing any
 /// target-specific Node type as well as easily serializing the edits to be sent over a network or IPC connection.
 pub trait RealDom<'a> {
-    fn request_available_node(&mut self) -> RealDomNode;
     fn raw_node_as_any(&self) -> &mut dyn Any;
 }
 
 pub struct DiffMachine<'real, 'bump, Dom: RealDom<'bump>> {
-    pub dom: &'real mut Dom,
+    pub real_dom: &'real mut Dom,
+    pub vdom: &'bump SharedResources,
     pub edits: DomEditor<'real, 'bump>,
-    pub components: &'bump SharedArena,
-    pub task_queue: &'bump TaskQueue,
+
     pub cur_idxs: SmallVec<[ScopeId; 5]>,
     pub diffed: FxHashSet<ScopeId>,
-    pub event_queue: EventQueue,
     pub seen_nodes: FxHashSet<ScopeId>,
 }
 
+impl<'r, 'b, D: RealDom<'b>> DiffMachine<'r, 'b, D> {
+    pub fn get_scope_mut(&mut self, id: &ScopeId) -> Option<&'b mut Scope> {
+        // ensure we haven't seen this scope before
+        // if we have, then we're trying to alias it, which is not allowed
+        debug_assert!(!self.seen_nodes.contains(id));
+        let compon = unsafe { &mut *self.vdom.components.get() };
+        compon.get_mut(*id)
+    }
+    pub fn get_scope(&mut self, id: &ScopeId) -> Option<&'b Scope> {
+        // ensure we haven't seen this scope before
+        // if we have, then we're trying to alias it, which is not allowed
+        let compon = unsafe { &*self.vdom.components.get() };
+        compon.get(*id)
+    }
+}
+
 impl<'real, 'bump, Dom> DiffMachine<'real, 'bump, Dom>
 where
     Dom: RealDom<'bump>,
@@ -88,18 +102,14 @@ where
     pub fn new(
         edits: &'real mut Vec<DomEdit<'bump>>,
         dom: &'real mut Dom,
-        components: &'bump SharedArena,
         cur_idx: ScopeId,
-        event_queue: EventQueue,
-        task_queue: &'bump TaskQueue,
+        shared: &'bump SharedResources,
     ) -> Self {
         Self {
+            real_dom: dom,
             edits: DomEditor::new(edits),
-            components,
-            dom,
             cur_idxs: smallvec![cur_idx],
-            event_queue,
-            task_queue,
+            vdom: shared,
             diffed: FxHashSet::default(),
             seen_nodes: FxHashSet::default(),
         }
@@ -112,20 +122,24 @@ where
     //
     // each function call assumes the stack is fresh (empty).
     pub fn diff_node(&mut self, old_node: &'bump VNode<'bump>, new_node: &'bump VNode<'bump>) {
+        let root = old_node
+            .dom_id
+            .get()
+            .expect("Should not be diffing old nodes that were never assigned");
+
         match (&old_node.kind, &new_node.kind) {
             // Handle the "sane" cases first.
             // The rsx and html macros strongly discourage dynamic lists not encapsulated by a "Fragment".
             // So the sane (and fast!) cases are where the virtual structure stays the same and is easily diffable.
             (VNodeKind::Text(old), VNodeKind::Text(new)) => {
-                let root = old_node.dom_id.get();
                 if old.text != new.text {
-                    self.edits.push(root);
+                    self.edits.push_root(root);
                     log::debug!("Text has changed {}, {}", old.text, new.text);
                     self.edits.set_text(new.text);
                     self.edits.pop();
                 }
 
-                new_node.dom_id.set(root);
+                new_node.dom_id.set(Some(root));
             }
 
             (VNodeKind::Element(old), VNodeKind::Element(new)) => {
@@ -133,20 +147,19 @@ where
                 // This is an optimization React makes due to how users structure their code
                 //
                 // In Dioxus, this is less likely to occur unless through a fragment
-                let root = old_node.dom_id.get();
                 if new.tag_name != old.tag_name || new.namespace != old.namespace {
-                    self.edits.push(root);
+                    self.edits.push_root(root);
                     let meta = self.create(new_node);
                     self.edits.replace_with(meta.added_to_stack);
                     self.edits.pop();
                     return;
                 }
 
-                new_node.dom_id.set(root);
+                new_node.dom_id.set(Some(root));
 
                 // push it just in case
                 // TODO: remove this - it clogs up things and is inefficient
-                self.edits.push(root);
+                self.edits.push_root(root);
                 self.diff_listeners(old.listeners, new.listeners);
                 self.diff_attr(old.attributes, new.attributes, new.namespace);
                 self.diff_children(old.children, new.children);
@@ -160,11 +173,11 @@ where
                     self.cur_idxs.push(old.ass_scope.get().unwrap());
 
                     // Make sure the new component vnode is referencing the right scope id
-                    let scope_id = old.ass_scope.get();
-                    new.ass_scope.set(scope_id);
+                    let scope_addr = old.ass_scope.get().unwrap();
+                    new.ass_scope.set(Some(scope_addr));
 
                     // make sure the component's caller function is up to date
-                    let scope = self.components.get_mut(scope_id.unwrap()).unwrap();
+                    let scope = self.get_scope_mut(&scope_addr).unwrap();
 
                     scope.caller = new.caller.clone();
 
@@ -185,22 +198,22 @@ where
                     }
                     self.cur_idxs.pop();
 
-                    self.seen_nodes.insert(scope_id.unwrap());
+                    self.seen_nodes.insert(scope_addr);
                 } else {
                     // this seems to be a fairy common code path that we could
-                    let mut old_iter = RealChildIterator::new(old_node, &self.components);
+                    let mut old_iter = RealChildIterator::new(old_node, &self.vdom);
                     let first = old_iter
                         .next()
                         .expect("Components should generate a placeholder root");
 
                     // remove any leftovers
                     for to_remove in old_iter {
-                        self.edits.push(to_remove);
+                        self.edits.push_root(to_remove);
                         self.edits.remove();
                     }
 
                     // seems like we could combine this into a single instruction....
-                    self.edits.push(first);
+                    self.edits.push_root(first);
                     let meta = self.create(new_node);
                     self.edits.replace_with(meta.added_to_stack);
                     self.edits.pop();
@@ -229,8 +242,12 @@ where
             }
 
             // The strategy here is to pick the first possible node from the previous set and use that as our replace with root
+            //
             // We also walk the "real node" list to make sure all latent roots are claened up
             // This covers the case any time a fragment or component shows up with pretty much anything else
+            //
+            // This likely isn't the fastest way to go about replacing one node with a virtual node, but the "insane" cases
+            // are pretty rare.  IE replacing a list (component or fragment) with a single node.
             (
                 VNodeKind::Component(_)
                 | VNodeKind::Fragment(_)
@@ -242,12 +259,12 @@ where
                 | VNodeKind::Element(_),
             ) => {
                 // Choose the node to use as the placeholder for replacewith
-                let back_node = match old_node.kind {
+                let back_node_id = match old_node.kind {
                     // We special case these two types to avoid allocating the small-vecs
-                    VNodeKind::Element(_) | VNodeKind::Text(_) => old_node.dom_id.get(),
+                    VNodeKind::Element(_) | VNodeKind::Text(_) => root,
 
                     _ => {
-                        let mut old_iter = RealChildIterator::new(old_node, &self.components);
+                        let mut old_iter = RealChildIterator::new(old_node, &self.vdom);
 
                         let back_node = old_iter
                             .next()
@@ -255,7 +272,7 @@ where
 
                         // remove any leftovers
                         for to_remove in old_iter {
-                            self.edits.push(to_remove);
+                            self.edits.push_root(to_remove);
                             self.edits.remove();
                         }
 
@@ -264,7 +281,7 @@ where
                 };
 
                 // replace the placeholder or first node with the nodes generated from the "new"
-                self.edits.push(back_node);
+                self.edits.push_root(back_node_id);
                 let meta = self.create(new_node);
                 self.edits.replace_with(meta.added_to_stack);
 
@@ -314,9 +331,10 @@ where
         log::warn!("Creating node! ... {:#?}", node);
         match &node.kind {
             VNodeKind::Text(text) => {
-                let real_id = self.dom.request_available_node();
+                let real_id = self.vdom.reserve_node();
                 self.edits.create_text_node(text.text, real_id);
-                node.dom_id.set(real_id);
+                node.dom_id.set(Some(real_id));
+
                 CreateMeta::new(text.is_static, 1)
             }
             VNodeKind::Element(el) => {
@@ -338,18 +356,18 @@ where
                     static_listeners: _,
                 } = el;
 
-                let real_id = self.dom.request_available_node();
+                let real_id = self.vdom.reserve_node();
                 if let Some(namespace) = namespace {
                     self.edits
                         .create_element(tag_name, Some(namespace), real_id)
                 } else {
                     self.edits.create_element(tag_name, None, real_id)
                 };
-                node.dom_id.set(real_id);
+                node.dom_id.set(Some(real_id));
 
                 listeners.iter().enumerate().for_each(|(idx, listener)| {
                     log::info!("setting listener id to {:#?}", real_id);
-                    listener.mounted_node.set(real_id);
+                    listener.mounted_node.set(Some(real_id));
                     self.edits
                         .new_event_listener(listener.event, listener.scope, idx, real_id);
 
@@ -405,25 +423,18 @@ where
                 let parent_idx = self.cur_idxs.last().unwrap().clone();
 
                 // Insert a new scope into our component list
-                let new_idx = self
-                    .components
-                    .with(|components| {
-                        components.insert_with_key(|new_idx| {
-                            let parent_scope = self.components.get(parent_idx).unwrap();
-                            let height = parent_scope.height + 1;
-                            Scope::new(
-                                caller,
-                                new_idx,
-                                Some(parent_idx),
-                                height,
-                                self.event_queue.new_channel(height, new_idx),
-                                self.components.clone(),
-                                vcomponent.children,
-                                self.task_queue.new_submitter(),
-                            )
-                        })
-                    })
-                    .unwrap();
+                let new_idx = self.vdom.insert_scope_with_key(|new_idx| {
+                    let parent_scope = self.get_scope(&parent_idx).unwrap();
+                    let height = parent_scope.height + 1;
+                    Scope::new(
+                        caller,
+                        new_idx,
+                        Some(parent_idx),
+                        height,
+                        vcomponent.children,
+                        self.vdom.clone(),
+                    )
+                });
 
                 // This code is supposed to insert the new idx into the parent's descendent list, but it doesn't really work.
                 // This is mostly used for cleanup - to remove old scopes when components are destroyed.
@@ -437,7 +448,7 @@ where
                 //     .insert(idx);
 
                 // TODO: abstract this unsafe into the arena abstraction
-                let inner: &'bump mut _ = unsafe { &mut *self.components.components.get() };
+                let inner: &'bump mut _ = unsafe { &mut *self.vdom.components.get() };
                 let new_component = inner.get_mut(new_idx).unwrap();
 
                 // Actually initialize the caller's slot with the right address
@@ -477,10 +488,10 @@ where
             }
 
             VNodeKind::Suspended { node: real_node } => {
-                let id = self.dom.request_available_node();
+                let id = self.vdom.reserve_node();
                 self.edits.create_placeholder(id);
-                node.dom_id.set(id);
-                real_node.set(id);
+                node.dom_id.set(Some(id));
+                real_node.set(Some(id));
                 CreateMeta::new(false, 1)
             }
         }
@@ -500,7 +511,7 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
         while let Some(scope_id) = scopes_to_explore.pop() {
             // If we're planning on deleting this node, then we don't need to both rendering it
             self.seen_nodes.insert(scope_id);
-            let scope = self.components.get(scope_id).unwrap();
+            let scope = self.get_scope(&scope_id).unwrap();
             for child in scope.descendents.borrow().iter() {
                 // Add this node to be explored
                 scopes_to_explore.push(child.clone());
@@ -513,7 +524,7 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
         // Delete all scopes that we found as part of this subtree
         for node in nodes_to_delete {
             log::debug!("Removing scope {:#?}", node);
-            let _scope = self.components.try_remove(node).unwrap();
+            let _scope = self.vdom.try_remove(node).unwrap();
             // do anything we need to do to delete the scope
             // I think we need to run the destructors on the hooks
             // TODO
@@ -527,7 +538,7 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
     //     [... node]
     //
     // The change list stack is left unchanged.
-    fn diff_listeners(&mut self, old: &[Listener<'_>], new: &[Listener<'_>]) {
+    fn diff_listeners(&mut self, old: &[&mut Listener<'_>], new: &[&mut Listener<'_>]) {
         if !old.is_empty() || !new.is_empty() {
             // self.edits.commit_traversal();
         }
@@ -547,7 +558,7 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
                     new_l.mounted_node.set(old_l.mounted_node.get());
                     // if new_l.id != old_l.id {
                     //     self.edits.remove_event_listener(event_type);
-                    //     // TODO! we need to mess with events and assign them by RealDomNode
+                    //     // TODO! we need to mess with events and assign them by ElementId
                     //     // self.edits
                     //     //     .update_event_listener(event_type, new_l.scope, new_l.id)
                     // }
@@ -1270,7 +1281,7 @@ enum KeyedPrefixResult {
 /// This iterator is useful when it's important to load the next real root onto the top of the stack for operations like
 /// "InsertBefore".
 struct RealChildIterator<'a> {
-    scopes: &'a SharedArena,
+    scopes: &'a SharedResources,
 
     // Heuristcally we should never bleed into 4 completely nested fragments/components
     // Smallvec lets us stack allocate our little stack machine so the vast majority of cases are sane
@@ -1279,7 +1290,7 @@ struct RealChildIterator<'a> {
 }
 
 impl<'a> RealChildIterator<'a> {
-    fn new(starter: &'a VNode<'a>, scopes: &'a SharedArena) -> Self {
+    fn new(starter: &'a VNode<'a>, scopes: &'a SharedResources) -> Self {
         Self {
             scopes,
             stack: smallvec::smallvec![(0, starter)],
@@ -1288,9 +1299,9 @@ impl<'a> RealChildIterator<'a> {
 }
 
 impl<'a> Iterator for RealChildIterator<'a> {
-    type Item = RealDomNode;
+    type Item = ElementId;
 
-    fn next(&mut self) -> Option<RealDomNode> {
+    fn next(&mut self) -> Option<ElementId> {
         let mut should_pop = false;
         let mut returned_node = None;
         let mut should_push = None;
@@ -1304,7 +1315,7 @@ impl<'a> Iterator for RealChildIterator<'a> {
                         // We've recursed INTO an element/text
                         // We need to recurse *out* of it and move forward to the next
                         should_pop = true;
-                        returned_node = Some(node.dom_id.get());
+                        returned_node = node.dom_id.get();
                     }
 
                     // If we get a fragment we push the next child
@@ -1313,7 +1324,7 @@ impl<'a> Iterator for RealChildIterator<'a> {
 
                         if frag.children.len() == 0 {
                             should_pop = true;
-                            returned_node = Some(node.dom_id.get());
+                            returned_node = node.dom_id.get();
                         }
 
                         if subcount >= frag.children.len() {
@@ -1325,11 +1336,16 @@ impl<'a> Iterator for RealChildIterator<'a> {
 
                     // Immediately abort suspended nodes - can't do anything with them yet
                     // VNodeKind::Suspended => should_pop = true,
-                    VNodeKind::Suspended { .. } => todo!(),
+                    VNodeKind::Suspended { node, .. } => {
+                        todo!()
+                        // *node = node.as_ref().borrow().get().expect("msg");
+                    }
 
                     // For components, we load their root and push them onto the stack
                     VNodeKind::Component(sc) => {
-                        let scope = self.scopes.get(sc.ass_scope.get().unwrap()).unwrap();
+                        let scope =
+                            unsafe { self.scopes.get_scope(sc.ass_scope.get().unwrap()) }.unwrap();
+                        // let scope = self.scopes.get(sc.ass_scope.get().unwrap()).unwrap();
 
                         // Simply swap the current node on the stack with the root of the component
                         *node = scope.root();

+ 6 - 6
packages/core/src/editor.rs

@@ -5,7 +5,7 @@
 //!
 //!
 
-use crate::{innerlude::ScopeId, RealDomNode};
+use crate::{innerlude::ScopeId, ElementId};
 
 /// The `DomEditor` provides an imperative interface for the Diffing algorithm to plan out its changes.
 ///
@@ -24,7 +24,7 @@ impl<'real, 'bump> DomEditor<'real, 'bump> {
     }
 
     // Navigation
-    pub(crate) fn push(&mut self, root: RealDomNode) {
+    pub(crate) fn push_root(&mut self, root: ElementId) {
         let id = root.as_u64();
         self.edits.push(PushRoot { id });
     }
@@ -61,7 +61,7 @@ impl<'real, 'bump> DomEditor<'real, 'bump> {
 
     // Create
     #[inline]
-    pub(crate) fn create_text_node(&mut self, text: &'bump str, id: RealDomNode) {
+    pub(crate) fn create_text_node(&mut self, text: &'bump str, id: ElementId) {
         let id = id.as_u64();
         self.edits.push(CreateTextNode { text, id });
     }
@@ -70,7 +70,7 @@ impl<'real, 'bump> DomEditor<'real, 'bump> {
         &mut self,
         tag: &'static str,
         ns: Option<&'static str>,
-        id: RealDomNode,
+        id: ElementId,
     ) {
         let id = id.as_u64();
         match ns {
@@ -80,7 +80,7 @@ impl<'real, 'bump> DomEditor<'real, 'bump> {
     }
 
     // placeholders are nodes that don't get rendered but still exist as an "anchor" in the real dom
-    pub(crate) fn create_placeholder(&mut self, id: RealDomNode) {
+    pub(crate) fn create_placeholder(&mut self, id: ElementId) {
         let id = id.as_u64();
         self.edits.push(CreatePlaceholder { id });
     }
@@ -91,7 +91,7 @@ impl<'real, 'bump> DomEditor<'real, 'bump> {
         event: &'static str,
         scope: ScopeId,
         element_id: usize,
-        realnode: RealDomNode,
+        realnode: ElementId,
     ) {
         self.edits.push(NewEventListener {
             scope,

+ 10 - 9
packages/core/src/events.rs

@@ -6,7 +6,7 @@
 
 use std::{cell::Cell, rc::Rc};
 
-use crate::innerlude::{RealDomNode, ScopeId};
+use crate::innerlude::{ElementId, HeightMarker, ScopeId};
 
 #[derive(Debug)]
 pub struct EventTrigger {
@@ -14,7 +14,7 @@ pub struct EventTrigger {
     pub originator: ScopeId,
 
     /// The optional real node associated with the trigger
-    pub real_node_id: Option<RealDomNode>,
+    pub real_node_id: Option<ElementId>,
 
     /// The type of event
     pub event: VirtualEvent,
@@ -72,7 +72,7 @@ impl EventTrigger {
     pub fn new(
         event: VirtualEvent,
         scope: ScopeId,
-        mounted_dom_id: Option<RealDomNode>,
+        mounted_dom_id: Option<ElementId>,
         priority: EventPriority,
     ) -> Self {
         Self {
@@ -105,6 +105,9 @@ pub enum VirtualEvent {
 
     // image event has conflicting method types
     // ImageEvent(event_data::ImageEvent),
+    SetStateEvent {
+        height: HeightMarker,
+    },
 
     // Whenever a task is ready (complete) Dioxus produces this "AsyncEvent"
     //
@@ -117,10 +120,8 @@ pub enum VirtualEvent {
     // These are more intrusive than the rest
     SuspenseEvent {
         hook_idx: usize,
-        domnode: Rc<Cell<RealDomNode>>,
+        domnode: Rc<Cell<Option<ElementId>>>,
     },
-
-    OtherEvent,
 }
 
 pub mod on {
@@ -138,7 +139,7 @@ pub mod on {
 
     use crate::{
         innerlude::NodeFactory,
-        innerlude::{Attribute, Listener, RealDomNode, VNode},
+        innerlude::{Attribute, ElementId, Listener, VNode},
     };
     use std::cell::Cell;
 
@@ -180,8 +181,8 @@ pub mod on {
                         let bump = &c.bump();
                         Listener {
                             event: stringify!($name),
-                            mounted_node: bump.alloc(Cell::new(RealDomNode::empty())),
-                            scope: c.scope_ref.our_arena_idx,
+                            mounted_node: Cell::new(None),
+                            scope: c.scope.our_arena_idx,
                             callback: bump.alloc(move |evt: VirtualEvent| match evt {
                                 VirtualEvent::$wrapper(event) => callback(event),
                                 _ => unreachable!("Downcasted VirtualEvent to wrong event type - this is an internal bug!")

+ 14 - 5
packages/core/src/heuristics.rs

@@ -1,14 +1,23 @@
 use std::collections::HashMap;
 
+use fxhash::FxHashMap;
+
 use crate::FC;
 
-pub(crate) struct HeuristicsEngine {
-    heuristics: HashMap<FcSlot, Heuristic>,
+/// Provides heuristics to the "SharedResources" object for improving allocation performance.
+///
+/// This heueristic engine records the memory footprint of bump arenas and hook lists for each component. These records are
+/// then used later on to optimize the initial allocation for future components. This helps save large allocations later on
+/// that would slow down the diffing and initializion process.
+///
+///
+pub struct HeuristicsEngine {
+    heuristics: FxHashMap<FcSlot, Heuristic>,
 }
 
-pub(crate) type FcSlot = *const ();
+pub type FcSlot = *const ();
 
-pub(crate) struct Heuristic {
+pub struct Heuristic {
     hooks: u32,
     bump_size: u64,
 }
@@ -16,7 +25,7 @@ pub(crate) struct Heuristic {
 impl HeuristicsEngine {
     pub(crate) fn new() -> Self {
         Self {
-            heuristics: HashMap::new(),
+            heuristics: FxHashMap::default(),
         }
     }
 

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

@@ -10,8 +10,8 @@
 //!
 
 pub use crate::innerlude::{
-    format_args_f, html, rsx, Context, DioxusElement, DomEdit, DomTree, EventTrigger, LazyNodes,
-    NodeFactory, Properties, RealDom, RealDomNode, ScopeId, VNode, VNodeKind, VirtualDom,
+    format_args_f, html, rsx, Context, DioxusElement, DomEdit, DomTree, ElementId, EventTrigger,
+    LazyNodes, NodeFactory, Properties, RealDom, ScopeId, VNode, VNodeKind, VirtualDom,
     VirtualEvent, FC,
 };
 
@@ -38,7 +38,6 @@ pub(crate) mod innerlude {
     pub use crate::hooklist::*;
     pub use crate::nodes::*;
     pub use crate::scope::*;
-    pub use crate::tasks::*;
     pub use crate::util::*;
     pub use crate::virtual_dom::*;
 
@@ -66,6 +65,5 @@ pub mod hooklist;
 pub mod nodes;
 pub mod scope;
 pub mod signals;
-pub mod tasks;
 pub mod util;
 pub mod virtual_dom;

+ 41 - 26
packages/core/src/nodes.rs

@@ -5,7 +5,7 @@
 //! These VNodes should be *very* cheap and *very* fast to construct - building a full tree should be insanely quick.
 use crate::{
     events::VirtualEvent,
-    innerlude::{Context, DomTree, Properties, RealDomNode, Scope, ScopeId, FC},
+    innerlude::{empty_cell, Context, DomTree, ElementId, Properties, Scope, ScopeId, FC},
 };
 use std::{
     cell::Cell,
@@ -16,7 +16,12 @@ use std::{
 
 pub struct VNode<'src> {
     pub kind: VNodeKind<'src>,
-    pub(crate) dom_id: Cell<RealDomNode>,
+
+    ///
+    /// ElementId supports NonZero32 and Cell is zero cost, so the size of this field is unaffected
+    ///
+    ///
+    pub(crate) dom_id: Cell<Option<ElementId>>,
     pub(crate) key: Option<&'src str>,
 }
 impl VNode<'_> {
@@ -39,7 +44,7 @@ pub enum VNodeKind<'src> {
 
     Component(&'src VComponent<'src>),
 
-    Suspended { node: Rc<Cell<RealDomNode>> },
+    Suspended { node: Rc<Cell<Option<ElementId>>> },
 }
 
 pub struct VText<'src> {
@@ -50,7 +55,6 @@ pub struct VText<'src> {
 pub struct VFragment<'src> {
     pub children: &'src [VNode<'src>],
     pub is_static: bool,
-    pub is_error: bool,
 }
 
 pub trait DioxusElement {
@@ -72,7 +76,7 @@ pub struct VElement<'a> {
     pub namespace: Option<&'static str>,
 
     pub static_listeners: bool,
-    pub listeners: &'a [Listener<'a>],
+    pub listeners: &'a [&'a mut Listener<'a>],
 
     pub static_attrs: bool,
     pub attributes: &'a [Attribute<'a>],
@@ -105,9 +109,9 @@ pub struct Listener<'bump> {
 
     pub scope: ScopeId,
 
-    pub mounted_node: &'bump mut Cell<RealDomNode>,
+    pub mounted_node: Cell<Option<ElementId>>,
 
-    pub(crate) callback: &'bump dyn FnMut(VirtualEvent),
+    pub(crate) callback: &'bump mut dyn FnMut(VirtualEvent),
 }
 
 /// Virtual Components for custom user-defined components
@@ -136,20 +140,31 @@ pub struct VComponent<'src> {
 /// This struct adds metadata to the final VNode about listeners, attributes, and children
 #[derive(Copy, Clone)]
 pub struct NodeFactory<'a> {
-    pub scope_ref: &'a Scope,
+    pub scope: &'a Scope,
     pub listener_id: &'a Cell<usize>,
 }
 
 impl<'a> NodeFactory<'a> {
     #[inline]
     pub fn bump(&self) -> &'a bumpalo::Bump {
-        &self.scope_ref.cur_frame().bump
+        &self.scope.cur_frame().bump
+    }
+
+    pub fn unstable_place_holder() -> VNode<'static> {
+        VNode {
+            dom_id: empty_cell(),
+            key: None,
+            kind: VNodeKind::Text(VText {
+                text: "",
+                is_static: true,
+            }),
+        }
     }
 
     /// Used in a place or two to make it easier to build vnodes from dummy text
-    pub fn static_text(text: &'static str) -> VNode {
+    pub fn static_text(&self, text: &'static str) -> VNode<'a> {
         VNode {
-            dom_id: RealDomNode::empty_cell(),
+            dom_id: empty_cell(),
             key: None,
             kind: VNodeKind::Text(VText {
                 text,
@@ -178,7 +193,7 @@ impl<'a> NodeFactory<'a> {
     pub fn text(&self, args: Arguments) -> VNode<'a> {
         let (text, is_static) = self.raw_text(args);
         VNode {
-            dom_id: RealDomNode::empty_cell(),
+            dom_id: empty_cell(),
             key: None,
             kind: VNodeKind::Text(VText { text, is_static }),
         }
@@ -187,7 +202,7 @@ impl<'a> NodeFactory<'a> {
     pub fn element(
         &self,
         el: impl DioxusElement,
-        listeners: &'a mut [Listener<'a>],
+        listeners: &'a mut [&'a mut Listener<'a>],
         attributes: &'a [Attribute<'a>],
         children: &'a [VNode<'a>],
         key: Option<&'a str>,
@@ -206,7 +221,7 @@ impl<'a> NodeFactory<'a> {
         &self,
         tag: &'static str,
         namespace: Option<&'static str>,
-        listeners: &'a mut [Listener],
+        listeners: &'a mut [&'a mut Listener<'a>],
         attributes: &'a [Attribute],
         children: &'a [VNode<'a>],
         key: Option<&'a str>,
@@ -215,15 +230,17 @@ impl<'a> NodeFactory<'a> {
         // TODO: this code shouldn't necessarily be here of all places
         // It would make more sense to do this in diffing
 
-        let mut queue = self.scope_ref.listeners.borrow_mut();
+        let mut queue = self.scope.listeners.borrow_mut();
         for listener in listeners.iter_mut() {
-            let mounted = listener.mounted_node as *mut _;
-            let callback = listener.callback as *const _ as *mut _;
-            queue.push((mounted, callback))
+            let raw_listener = &mut **listener;
+
+            let long_listener: &mut Listener<'static> =
+                unsafe { std::mem::transmute(raw_listener) };
+            queue.push(long_listener as *mut _)
         }
 
         VNode {
-            dom_id: RealDomNode::empty_cell(),
+            dom_id: empty_cell(),
             key,
             kind: VNodeKind::Element(self.bump().alloc(VElement {
                 tag_name: tag,
@@ -242,10 +259,10 @@ impl<'a> NodeFactory<'a> {
 
     pub fn suspended() -> VNode<'static> {
         VNode {
-            dom_id: RealDomNode::empty_cell(),
+            dom_id: empty_cell(),
             key: None,
             kind: VNodeKind::Suspended {
-                node: Rc::new(RealDomNode::empty_cell()),
+                node: Rc::new(empty_cell()),
             },
         }
     }
@@ -322,7 +339,7 @@ impl<'a> NodeFactory<'a> {
 
         VNode {
             key,
-            dom_id: Cell::new(RealDomNode::empty()),
+            dom_id: empty_cell(),
             kind: VNodeKind::Component(self.bump().alloc_with(|| VComponent {
                 user_fc,
                 comparator,
@@ -383,12 +400,11 @@ To help you identify where this error is coming from, we've generated a backtrac
             }
         }
         VNode {
-            dom_id: RealDomNode::empty_cell(),
+            dom_id: empty_cell(),
             key: None,
             kind: VNodeKind::Fragment(VFragment {
                 children: nodes.into_bump_slice(),
                 is_static: false,
-                is_error: false,
             }),
         }
     }
@@ -444,7 +460,6 @@ impl<'a> IntoVNode<'a> for &VNode<'a> {
             VNodeKind::Fragment(fragment) => VNodeKind::Fragment(VFragment {
                 children: fragment.children,
                 is_static: fragment.is_static,
-                is_error: false,
             }),
             VNodeKind::Component(component) => VNodeKind::Component(component),
 
@@ -535,7 +550,7 @@ impl<'a> IntoVNode<'a> for Option<VNode<'a>> {
 
 impl IntoVNode<'_> for &'static str {
     fn into_vnode<'a>(self, cx: NodeFactory<'a>) -> VNode<'a> {
-        NodeFactory::static_text(self)
+        cx.static_text(self)
     }
 }
 impl IntoVNode<'_> for Arguments<'_> {

+ 31 - 54
packages/core/src/scope.rs

@@ -18,40 +18,30 @@ use std::{
 /// We expose the `Scope` type so downstream users can traverse the Dioxus VirtualDOM for whatever
 /// usecase they might have.
 pub struct Scope {
-    // Book-keeping about the arena
+    // Book-keeping about our spot in the arena
     pub(crate) parent_idx: Option<ScopeId>,
-    pub(crate) descendents: RefCell<HashSet<ScopeId>>,
     pub(crate) our_arena_idx: ScopeId,
     pub(crate) height: u32,
+    pub(crate) descendents: RefCell<HashSet<ScopeId>>,
 
     // Nodes
     // an internal, highly efficient storage of vnodes
     pub(crate) frames: ActiveFrame,
-    pub(crate) child_nodes: &'static [VNode<'static>],
     pub(crate) caller: Rc<WrappedCaller>,
+    pub(crate) child_nodes: &'static [VNode<'static>],
 
     // Listeners
-    pub(crate) listeners: RefCell<Vec<(*mut Cell<RealDomNode>, *mut dyn FnMut(VirtualEvent))>>,
+    pub(crate) listeners: RefCell<Vec<*mut Listener<'static>>>,
     pub(crate) listener_idx: Cell<usize>,
 
     // State
     pub(crate) hooks: HookList,
     pub(crate) shared_contexts: RefCell<HashMap<TypeId, Rc<dyn Any>>>,
 
-    // Events
-    pub(crate) event_channel: Rc<dyn Fn() + 'static>,
-
-    // Tasks
-    pub(crate) task_submitter: TaskSubmitter,
-
-    // A reference to the list of components.
-    // This lets us traverse the component list whenever we need to access our parent or children.
-    pub(crate) arena_link: SharedArena,
+    // A reference to the resources shared by all the comonents
+    pub(crate) vdom: SharedResources,
 }
 
-// The type of the channel function
-type EventChannel = Rc<dyn Fn()>;
-
 // The type of closure that wraps calling components
 pub type WrappedCaller = dyn for<'b> Fn(&'b Scope) -> DomTree<'b>;
 
@@ -68,13 +58,16 @@ impl Scope {
     // Therefore, their lifetimes are connected exclusively to the virtual dom
     pub fn new<'creator_node>(
         caller: Rc<WrappedCaller>,
+
         arena_idx: ScopeId,
+
         parent: Option<ScopeId>,
+
         height: u32,
-        event_channel: EventChannel,
-        arena_link: SharedArena,
+
         child_nodes: &'creator_node [VNode<'creator_node>],
-        task_submitter: TaskSubmitter,
+
+        vdom: SharedResources,
     ) -> Self {
         let child_nodes = unsafe { std::mem::transmute(child_nodes) };
         Self {
@@ -83,9 +76,7 @@ impl Scope {
             parent_idx: parent,
             our_arena_idx: arena_idx,
             height,
-            event_channel,
-            arena_link,
-            task_submitter,
+            vdom,
             listener_idx: Default::default(),
             frames: ActiveFrame::new(),
             hooks: Default::default(),
@@ -113,7 +104,7 @@ impl Scope {
         // Remove all the outdated listeners
 
         // This is a very dangerous operation
-        let next_frame = self.frames.old_frame_mut();
+        let next_frame = self.frames.prev_frame_mut();
         next_frame.bump.reset();
 
         self.listeners.borrow_mut().clear();
@@ -132,7 +123,7 @@ impl Scope {
             }
             Some(new_head) => {
                 // the user's component succeeded. We can safely cycle to the next frame
-                self.frames.old_frame_mut().head_node = unsafe { std::mem::transmute(new_head) };
+                self.frames.prev_frame_mut().head_node = unsafe { std::mem::transmute(new_head) };
                 self.frames.cycle_frame();
                 Ok(())
             }
@@ -162,33 +153,27 @@ impl Scope {
 
         let listners = self.listeners.borrow_mut();
 
-        let raw_listener = listners.iter().find(|(domptr, _)| {
-            let search = unsafe { &**domptr };
-            let search_id = search.get();
+        let raw_listener = listners.iter().find(|lis| {
+            let search = unsafe { &mut ***lis };
+            let search_id = search.mounted_node.get();
             log::info!("searching listener {:#?}", search_id);
-            match real_node_id {
-                Some(e) => search_id == e,
-                None => false,
+
+            match (real_node_id, search_id) {
+                (Some(e), Some(search_id)) => search_id == e,
+                _ => false,
             }
         });
 
-        match raw_listener {
-            Some((_node, listener)) => unsafe {
-                // TODO: Don'tdo a linear scan! Do a hashmap lookup! It'll be faster!
-                let listener_fn = &mut **listener;
-                listener_fn(event);
-            },
-            None => todo!(),
+        if let Some(raw_listener) = raw_listener {
+            let listener = unsafe { &mut **raw_listener };
+            (listener.callback)(event);
+        } else {
+            log::warn!("An event was triggered but there was no listener to handle it");
         }
 
         Ok(())
     }
 
-    pub(crate) fn submit_task(&self, task: FiberTask) {
-        log::debug!("Task submitted into scope");
-        (self.task_submitter)(task);
-    }
-
     #[inline]
     pub(crate) fn next_frame<'bump>(&'bump self) -> &'bump VNode<'bump> {
         self.frames.current_head_node()
@@ -207,18 +192,10 @@ impl Scope {
     /// Get the root VNode of this component
     #[inline]
     pub fn root<'a>(&'a self) -> &'a VNode<'a> {
-        &self.frames.cur_frame().head_node
+        &self.frames.current_head_node()
     }
-}
-
-pub fn errored_fragment() -> VNode<'static> {
-    VNode {
-        dom_id: RealDomNode::empty_cell(),
-        key: None,
-        kind: VNodeKind::Fragment(VFragment {
-            children: &[],
-            is_static: false,
-            is_error: true,
-        }),
+    #[inline]
+    pub fn child_nodes<'a>(&'a self) -> &'a [VNode<'a>] {
+        unsafe { std::mem::transmute(self.child_nodes) }
     }
 }

+ 0 - 137
packages/core/src/tasks.rs

@@ -1,137 +0,0 @@
-//! The TaskQueue serves as a centralized async store for all tasks in Dioxus.
-//! When a component renders, it may submit an async task to the queue.
-//!
-//! Then the task complete, it is emitted from the virtual dom in the event loop, which is then fed back into the virtualdom
-//! as an event trigger.
-//!
-//! When a component is scheduled to re-render, the awaing task must be dumped from the queue.
-//!
-//! This is all pretty unsafe stuff.
-//! The major invariant here is that tasks that enter the queue may be invalidated during transitions.
-
-use std::{
-    cell::Cell,
-    sync::{Arc, RwLock},
-};
-
-use futures_util::{stream::FuturesUnordered, Future, Stream, StreamExt};
-use slotmap::{DefaultKey, SlotMap};
-
-use crate::innerlude::{EventTrigger, FiberTask, ScopeId};
-
-pub type TaskSubmitter = Arc<dyn Fn(FiberTask)>;
-
-pub struct TaskQueue {
-    slots: Arc<RwLock<FuturesUnordered<FiberTask>>>,
-    // slots: Arc<RwLock<SlotMap<DefaultKey, DTask>>>,
-    submitter: TaskSubmitter,
-}
-
-impl TaskQueue {
-    pub fn new() -> Self {
-        let slots = Arc::new(RwLock::new(FuturesUnordered::new()));
-        let slots2 = slots.clone();
-
-        let submitter = Arc::new(move |task| {
-            let mut slots = slots2.write().unwrap();
-            log::debug!("Task submitted into global task queue");
-            slots.push(task);
-        });
-        Self { slots, submitter }
-    }
-
-    pub fn new_submitter(&self) -> TaskSubmitter {
-        self.submitter.clone()
-    }
-
-    pub fn submit_task(&mut self, task: FiberTask) {
-        self.slots.write().unwrap().push(task);
-        // TaskHandle { key }
-    }
-
-    pub fn is_empty(&self) -> bool {
-        self.slots.read().unwrap().is_empty()
-    }
-    pub fn len(&self) -> usize {
-        self.slots.read().unwrap().len()
-    }
-
-    pub async fn next(&mut self) -> Option<EventTrigger> {
-        let mut slots = self.slots.write().unwrap();
-        slots.next().await
-    }
-}
-
-// impl Stream for TaskQueue {
-//     type Item = EventTrigger;
-
-//     /// We can never be finished polling
-//     fn poll_next(
-//         self: Pin<&mut Self>,
-//         cx: &mut std::task::Context<'_>,
-//     ) -> std::task::Poll<Option<Self::Item>> {
-//         // let yield_every = self.len();
-//         // let mut polled = 0;
-
-//         let mut slots = self.slots.write().unwrap();
-//         for (_key, slot) in slots.iter_mut() {
-//             if slot.dead.get() {
-//                 continue;
-//             }
-//             let r = slot.fut;
-//             // let fut = unsafe { &mut *r };
-//             // use futures::{future::Future, poll, FutureExt};
-
-//             let f2 = fut.as_mut();
-//             let w = cx.waker();
-//             let mut cx = Context::from_waker(&w);
-
-//             // Pin::new_unchecked(pointer)
-//             // use std::future::Future;
-//             match f2.poll(&mut cx) {
-//                 Poll::Ready(_) => {
-//                     let trigger = EventTrigger::new_from_task(slot.originator);
-//                     slot.dead.set(true);
-//                     return Poll::Ready(Some(trigger));
-//                 }
-//                 Poll::Pending => continue,
-//             }
-//         }
-
-//         // we tried polling every active task.
-//         // give up and relinquish controlto the parent
-
-//         // We have polled a large number of futures in a row without yielding.
-//         // To ensure we do not starve other tasks waiting on the executor,
-//         // we yield here, but immediately wake ourselves up to continue.
-//         // cx.waker().wake_by_ref();
-//         return Poll::Pending;
-//     }
-// }
-
-pub struct TaskHandle {
-    key: DefaultKey,
-}
-
-pub struct DTask {
-    fut: FiberTask,
-    originator: ScopeId,
-    dead: Cell<bool>,
-}
-impl DTask {
-    pub fn new(fut: FiberTask, originator: ScopeId) -> Self {
-        Self {
-            fut,
-            originator,
-            dead: Cell::new(false),
-        }
-    }
-    pub fn debug_new(fut: FiberTask) -> Self {
-        let originator = ScopeId::default();
-        Self {
-            fut,
-            originator,
-            dead: Cell::new(false),
-        }
-    }
-}

+ 7 - 67
packages/core/src/util.rs

@@ -1,35 +1,11 @@
-use std::{
-    cell::{Cell, RefCell, RefMut},
-    rc::Rc,
-};
-
-use futures_util::StreamExt;
-use slotmap::{DefaultKey, Key, KeyData};
+use std::cell::Cell;
 
 use crate::innerlude::*;
 
-#[derive(PartialEq, Debug, Clone, Default)]
-pub struct EventQueue {
-    pub queue: Rc<RefCell<Vec<HeightMarker>>>,
-}
-
-impl EventQueue {
-    pub fn new_channel(&self, height: u32, idx: ScopeId) -> Rc<dyn Fn()> {
-        let inner = self.clone();
-        let marker = HeightMarker { height, idx };
-        Rc::new(move || {
-            log::debug!("channel updated {:#?}", marker);
-            inner.queue.as_ref().borrow_mut().push(marker)
-        })
-    }
-
-    pub fn sort_unstable(&self) {
-        self.queue.borrow_mut().sort_unstable()
-    }
-
-    pub fn borrow_mut(&self) -> RefMut<Vec<HeightMarker>> {
-        self.queue.borrow_mut()
-    }
+// create a cell with a "none" value
+#[inline]
+pub fn empty_cell() -> Cell<Option<ElementId>> {
+    Cell::new(None as Option<ElementId>)
 }
 
 /// A helper type that lets scopes be ordered by their height
@@ -51,41 +27,10 @@ impl PartialOrd for HeightMarker {
     }
 }
 
-/// The `RealDomNode` is an ID handle that corresponds to a foreign DOM node.
-///
-/// "u64" was chosen for two reasons
-/// - 0 cost hashing
-/// - use with slotmap and other versioned slot arenas
-
-#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub struct RealDomNode(pub u64);
-impl RealDomNode {
-    #[inline]
-    pub fn empty() -> Self {
-        Self(u64::MIN)
-    }
-    #[inline]
-    pub fn empty_cell() -> Cell<Self> {
-        Cell::new(Self::empty())
-    }
-    #[inline]
-    pub fn from_u64(id: u64) -> Self {
-        Self(id)
-    }
-
-    #[inline]
-    pub fn as_u64(&self) -> u64 {
-        self.0
-    }
-}
-
-pub struct DebugDom {
-    counter: u64,
-}
+pub struct DebugDom {}
 impl DebugDom {
     pub fn new() -> Self {
-        Self { counter: 0 }
+        Self {}
     }
 }
 
@@ -93,9 +38,4 @@ impl<'a> RealDom<'a> for DebugDom {
     fn raw_node_as_any(&self) -> &mut dyn std::any::Any {
         todo!()
     }
-
-    fn request_available_node(&mut self) -> RealDomNode {
-        self.counter += 1;
-        RealDomNode::from_u64(self.counter)
-    }
 }

+ 51 - 81
packages/core/src/virtual_dom.rs

@@ -19,8 +19,7 @@
 //! This module includes just the barebones for a complete VirtualDOM API.
 //! Additional functionality is defined in the respective files.
 
-use crate::tasks::TaskQueue;
-use crate::{arena::SharedArena, innerlude::*};
+use crate::{arena::SharedResources, innerlude::*};
 
 use slotmap::DefaultKey;
 use slotmap::SlotMap;
@@ -30,10 +29,6 @@ use std::any::TypeId;
 use std::cell::RefCell;
 use std::pin::Pin;
 
-slotmap::new_key_type! {
-    pub struct ScopeId;
-}
-
 /// 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.
 ///
@@ -49,7 +44,7 @@ pub struct VirtualDom {
     ///
     /// This is wrapped in an UnsafeCell because we will need to get mutable access to unique values in unique bump arenas
     /// and rusts's guartnees cannot prove that this is safe. We will need to maintain the safety guarantees manually.
-    pub components: SharedArena,
+    pub shared: SharedResources,
 
     /// The index of the root component
     /// Should always be the first (gen=0, id=0)
@@ -57,21 +52,12 @@ pub struct VirtualDom {
 
     pub triggers: RefCell<Vec<EventTrigger>>,
 
-    /// All components dump their updates into a queue to be processed
-    pub event_queue: EventQueue,
-
-    pub tasks: TaskQueue,
-
-    heuristics: HeuristicsEngine,
-
-    root_props: std::pin::Pin<Box<dyn std::any::Any>>,
-
-    /// Type of the original props. This is stored as TypeId so VirtualDom does not need to be generic.
-    ///
-    /// Whenver props need to be updated, an Error will be thrown if the new props do not
-    /// match the props used to create the VirtualDom.
+    // for managing the props that were used to create the dom
     #[doc(hidden)]
     _root_prop_type: std::any::TypeId,
+
+    #[doc(hidden)]
+    _root_props: std::pin::Pin<Box<dyn std::any::Any>>,
 }
 
 // ======================================
@@ -142,37 +128,22 @@ impl VirtualDom {
     /// let dom = VirtualDom::new(Example);
     /// ```
     pub fn new_with_props<P: Properties + 'static>(root: FC<P>, root_props: P) -> Self {
-        let components = SharedArena::new(SlotMap::<ScopeId, Scope>::with_key());
+        let components = SharedResources::new();
 
         let root_props: Pin<Box<dyn Any>> = Box::pin(root_props);
         let props_ptr = root_props.as_ref().downcast_ref::<P>().unwrap() as *const P;
 
-        // Build a funnel for hooks to send their updates into. The `use_hook` method will call into the update funnel.
-        let event_queue = EventQueue::default();
-        let _event_queue = event_queue.clone();
-
         let link = components.clone();
 
-        let tasks = TaskQueue::new();
-        let submitter = tasks.new_submitter();
-
-        let base_scope = components
-            .with(|arena| {
-                arena.insert_with_key(move |myidx| {
-                    let event_channel = _event_queue.new_channel(0, myidx);
-                    let caller = NodeFactory::create_component_caller(root, props_ptr as *const _);
-                    Scope::new(caller, myidx, None, 0, event_channel, link, &[], submitter)
-                })
-            })
-            .unwrap();
+        let base_scope = components.insert_scope_with_key(move |myidx| {
+            let caller = NodeFactory::create_component_caller(root, props_ptr as *const _);
+            Scope::new(caller, myidx, None, 0, &[], link)
+        });
 
         Self {
             base_scope,
-            event_queue,
-            components,
-            root_props,
-            tasks,
-            heuristics: HeuristicsEngine::new(),
+            _root_props: root_props,
+            shared: components,
             triggers: Default::default(),
             _root_prop_type: TypeId::of::<P>(),
         }
@@ -209,29 +180,28 @@ impl VirtualDom {
 
     /// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom rom scratch
     ///
-    /// Currently this doesn't do what we want it to do
-    ///
     /// The diff machine expects the RealDom's stack to be the root of the application
     pub fn rebuild<'s, Dom: RealDom<'s>>(
         &'s mut self,
         realdom: &mut Dom,
         edits: &mut Vec<DomEdit<'s>>,
     ) -> Result<()> {
-        let mut diff_machine = DiffMachine::new(
-            edits,
-            realdom,
-            &self.components,
-            self.base_scope,
-            self.event_queue.clone(),
-            &self.tasks,
-        );
+        let mut diff_machine = DiffMachine::new(edits, realdom, self.base_scope, &self.shared);
 
-        let cur_component = self.components.get_mut(self.base_scope).unwrap();
+        let cur_component = diff_machine
+            .get_scope_mut(&self.base_scope)
+            .expect("The base scope should never be moved");
 
         // We run the component. If it succeeds, then we can diff it and add the changes to the dom.
         if cur_component.run_scope().is_ok() {
             let meta = diff_machine.create(cur_component.next_frame());
             diff_machine.edits.append_children(meta.added_to_stack);
+        } else {
+            // todo: should this be a hard error?
+            log::warn!(
+                "Component failed to run succesfully during rebuild.
+                This does not result in a failed rebuild, but indicates a logic failure within your app."
+            );
         }
 
         Ok(())
@@ -242,10 +212,9 @@ impl VirtualDom {
     ///
     ///
     ///
-    pub fn queue_event(&self, trigger: EventTrigger) -> Result<()> {
+    pub fn queue_event(&self, trigger: EventTrigger) {
         let mut triggers = self.triggers.borrow_mut();
         triggers.push(trigger);
-        Ok(())
     }
 
     /// This method is the most sophisticated way of updating the virtual dom after an external event has been triggered.
@@ -298,26 +267,18 @@ impl VirtualDom {
     ) -> Result<()> {
         let trigger = self.triggers.borrow_mut().pop().expect("failed");
 
-        let mut diff_machine = DiffMachine::new(
-            edits,
-            realdom,
-            &self.components,
-            trigger.originator,
-            self.event_queue.clone(),
-            &self.tasks,
-        );
+        let mut diff_machine = DiffMachine::new(edits, realdom, trigger.originator, &self.shared);
 
         match &trigger.event {
-            VirtualEvent::OtherEvent => todo!(),
-
             // Nothing yet
             VirtualEvent::AsyncEvent { .. } => {}
 
             // Suspense Events! A component's suspended node is updated
             VirtualEvent::SuspenseEvent { hook_idx, domnode } => {
-                let scope = self.components.get_mut(trigger.originator).unwrap();
+                // Safety: this handler is the only thing that can mutate shared items at this moment in tim
+                let scope = diff_machine.get_scope_mut(&trigger.originator).unwrap();
 
-                // safety: we are sure that there are no other references to the inner content of this hook
+                // safety: we are sure that there are no other references to the inner content of suspense hooks
                 let hook = unsafe { scope.hooks.get_mut::<SuspenseHook>(*hook_idx) }.unwrap();
 
                 let cx = Context { scope, props: &() };
@@ -325,17 +286,24 @@ impl VirtualDom {
 
                 // generate the new node!
                 let nodes: Option<VNode<'s>> = (&hook.callback)(scx);
-                let nodes = nodes.unwrap_or_else(|| errored_fragment());
-                let nodes = scope.cur_frame().bump.alloc(nodes);
+                match nodes {
+                    None => {
+                        log::warn!("Suspense event came through, but there was no mounted node to update >:(");
+                    }
+                    Some(nodes) => {
+                        let nodes = scope.cur_frame().bump.alloc(nodes);
 
-                // push the old node's root onto the stack
-                diff_machine.edits.push(domnode.get());
+                        // push the old node's root onto the stack
+                        let real_id = domnode.get().ok_or(Error::NotMounted)?;
+                        diff_machine.edits.push_root(real_id);
 
-                // push these new nodes onto the diff machines stack
-                let meta = diff_machine.create(&*nodes);
+                        // push these new nodes onto the diff machines stack
+                        let meta = diff_machine.create(&*nodes);
 
-                // replace the placeholder with the new nodes we just pushed on the stack
-                diff_machine.edits.replace_with(meta.added_to_stack);
+                        // replace the placeholder with the new nodes we just pushed on the stack
+                        diff_machine.edits.replace_with(meta.added_to_stack);
+                    }
+                }
             }
 
             // This is the "meat" of our cooperative scheduler
@@ -343,12 +311,12 @@ impl VirtualDom {
             //
             // We use the reconciler to request new IDs and then commit/uncommit the IDs when the scheduler is finished
             _ => {
-                self.components
-                    .get_mut(trigger.originator)
+                diff_machine
+                    .get_scope_mut(&trigger.originator)
                     .map(|f| f.call_listener(trigger));
 
                 // Now, there are events in the queue
-                let mut updates = self.event_queue.queue.as_ref().borrow_mut();
+                let mut updates = self.shared.borrow_queue();
 
                 // Order the nodes by their height, we want the nodes with the smallest depth on top
                 // This prevents us from running the same component multiple times
@@ -370,8 +338,10 @@ impl VirtualDom {
                     diff_machine.seen_nodes.insert(update.idx.clone());
 
                     // Start a new mutable borrow to components
-                    // We are guaranteeed that this scope is unique because we are tracking which nodes have modified
-                    let cur_component = self.components.get_mut(update.idx).unwrap();
+                    // We are guaranteeed that this scope is unique because we are tracking which nodes have modified in the diff machine
+                    let cur_component = diff_machine
+                        .get_scope_mut(&update.idx)
+                        .expect("Failed to find scope or borrow would be aliasing");
 
                     if cur_component.run_scope().is_ok() {
                         let (old, new) = (cur_component.old_frame(), cur_component.next_frame());
@@ -385,7 +355,7 @@ impl VirtualDom {
     }
 
     pub fn base_scope(&self) -> &Scope {
-        self.components.get(self.base_scope).unwrap()
+        unsafe { self.shared.get_scope(self.base_scope).unwrap() }
     }
 }
 

+ 2 - 2
packages/ssr/src/lib.rs

@@ -21,7 +21,7 @@ pub fn render_vdom_scope(vdom: &VirtualDom, scope: ScopeId) -> Option<String> {
         "{:}",
         TextRenderer {
             cfg: SsrConfig::default(),
-            root: vdom.components.get(scope).unwrap().root(),
+            root: vdom.shared.get_scope(scope).unwrap().root(),
             vdom: Some(vdom)
         }
     ))
@@ -163,7 +163,7 @@ impl<'a> TextRenderer<'a> {
             VNodeKind::Component(vcomp) => {
                 let idx = vcomp.ass_scope.get().unwrap();
                 if let Some(vdom) = self.vdom {
-                    let new_node = vdom.components.get(idx).unwrap().root();
+                    let new_node = vdom.shared.get_scope(idx).unwrap().root();
                     self.html_render(new_node, f, il + 1)?;
                 }
             }

+ 16 - 11
packages/web/src/new.rs

@@ -13,7 +13,7 @@ use web_sys::{
 
 pub struct WebsysDom {
     pub stack: Stack,
-    nodes: slotmap::SlotMap<DefaultKey, Option<Node>>,
+    nodes: slotmap::SlotMap<DefaultKey, Node>,
     document: Document,
     root: Element,
 
@@ -51,7 +51,7 @@ impl WebsysDom {
 
         let mut nodes = slotmap::SlotMap::with_capacity(1000);
 
-        let root_id = nodes.insert(Some(root.clone().dyn_into::<Node>().unwrap()));
+        let root_id = nodes.insert(root.clone().dyn_into::<Node>().unwrap());
 
         Self {
             stack: Stack::with_capacity(10),
@@ -101,11 +101,16 @@ impl WebsysDom {
         let key = DefaultKey::from(KeyData::from_ffi(root));
         let domnode = self.nodes.get_mut(key);
 
-        let domnode = domnode.unwrap().as_mut().unwrap();
+        let real_node: Node = match domnode {
+            Some(n) => n.clone(),
+            None => todo!(),
+        };
+
+        // let domnode = domnode.unwrap().as_mut().unwrap();
         // .expect(&format!("Failed to pop know root: {:#?}", key))
         // .unwrap();
 
-        self.stack.push(domnode.clone());
+        self.stack.push(real_node);
     }
     // drop the node off the stack
     fn pop(&mut self) {
@@ -202,7 +207,7 @@ impl WebsysDom {
         *self
             .nodes
             .get_mut(DefaultKey::from(KeyData::from_ffi(id)))
-            .unwrap() = Some(textnode);
+            .unwrap() = textnode;
     }
 
     fn create_element(&mut self, tag: &str, ns: Option<&'static str>, id: u64) {
@@ -224,7 +229,7 @@ impl WebsysDom {
         let id = DefaultKey::from(KeyData::from_ffi(id));
 
         self.stack.push(el.clone());
-        *self.nodes.get_mut(id).unwrap() = Some(el);
+        *self.nodes.get_mut(id).unwrap() = el;
         // let nid = self.node_counter.?next();
         // let nid = self.nodes.insert(el).data().as_ffi();
         // log::debug!("Called [`create_element`]: {}, {:?}", tag, nid);
@@ -344,11 +349,11 @@ impl WebsysDom {
 }
 
 impl<'a> dioxus_core::diff::RealDom<'a> for WebsysDom {
-    fn request_available_node(&mut self) -> RealDomNode {
-        let key = self.nodes.insert(None);
-        log::debug!("making new key: {:#?}", key);
-        RealDomNode(key.data().as_ffi())
-    }
+    // fn request_available_node(&mut self) -> RealDomNode {
+    //     let key = self.nodes.insert(None);
+    //     log::debug!("making new key: {:#?}", key);
+    //     RealDomNode(key.data().as_ffi())
+    // }
 
     fn raw_node_as_any(&self) -> &mut dyn std::any::Any {
         todo!()