浏览代码

wip: successfully building

Jonathan Kelley 4 年之前
父节点
当前提交
e3d9db0847

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

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

+ 18 - 0
packages/core/src/arena.rs

@@ -61,6 +61,24 @@ impl ScopeArena {
         // todo!()
         // todo!()
     }
     }
 
 
+    pub fn with_scope<'b, O: 'static>(
+        &'b self,
+        id: ScopeIdx,
+        f: impl FnOnce(&'b mut Scope) -> O,
+    ) -> Result<O> {
+        todo!()
+    }
+
+    // return a bumpframe with a lifetime attached to the arena borrow
+    // this is useful for merging lifetimes
+    pub fn with_scope_vnode<'b>(
+        &self,
+        id: ScopeIdx,
+        f: impl FnOnce(&mut Scope) -> &VNode<'b>,
+    ) -> Result<&VNode<'b>> {
+        todo!()
+    }
+
     pub fn try_remove(&mut self, id: ScopeIdx) -> Result<Scope> {
     pub fn try_remove(&mut self, id: ScopeIdx) -> Result<Scope> {
         let inner = unsafe { &mut *self.0.borrow().arena.get() };
         let inner = unsafe { &mut *self.0.borrow().arena.get() };
         inner
         inner

+ 41 - 2
packages/core/src/component.rs

@@ -9,7 +9,7 @@ use crate::innerlude::FC;
 
 
 pub type ScopeIdx = generational_arena::Index;
 pub type ScopeIdx = generational_arena::Index;
 
 
-pub unsafe trait Properties: PartialEq {
+pub unsafe trait Properties: PartialEq + Sized {
     type Builder;
     type Builder;
     const CAN_BE_MEMOIZED: bool;
     const CAN_BE_MEMOIZED: bool;
     fn builder() -> Self::Builder;
     fn builder() -> Self::Builder;
@@ -36,7 +36,9 @@ pub fn fc_to_builder<T: Properties>(_f: FC<T>) -> T::Builder {
 }
 }
 
 
 mod testing {
 mod testing {
-    use std::any::Any;
+    use std::{any::Any, ops::Deref};
+
+    use crate::innerlude::VNode;
 
 
     // trait PossibleProps {
     // trait PossibleProps {
     //     type POut: PartialEq;
     //     type POut: PartialEq;
@@ -137,6 +139,43 @@ mod testing {
             // blah(10, "asd");
             // blah(10, "asd");
         }
         }
     }
     }
+
+    struct Inner<'a> {
+        a: String,
+        b: i32,
+        c: &'a str,
+    }
+
+    struct Custom<'a, P: 'a> {
+        inner: &'a P,
+        // inner: *const (),
+        _p: std::marker::PhantomData<&'a P>,
+    }
+
+    impl<'a, P> Custom<'a, P> {
+        fn props(&self) -> &P {
+            todo!()
+        }
+    }
+
+    // impl<P> Deref for Custom<P> {
+    //     type Target = Inner;
+
+    //     fn deref(&self) -> &Self::Target {
+    //         unsafe { &*self.inner }
+    //     }
+    // }
+
+    fn test2<'a>(a: Custom<'a, Inner<'a>>) -> VNode {
+        let r = a.inner;
+
+        todo!()
+        // let g = a.props();
+        // todo!()
+        // let g = &a.a;
+    }
+
+    fn is_comp() {}
 }
 }
 
 
 mod style {}
 mod style {}

+ 175 - 101
packages/core/src/diff.rs

@@ -59,52 +59,29 @@ use std::{
 /// subscriptions and props changes.
 /// subscriptions and props changes.
 pub struct DiffMachine<'a> {
 pub struct DiffMachine<'a> {
     pub create_diffs: bool,
     pub create_diffs: bool,
+    pub cur_idx: ScopeIdx,
     pub change_list: EditMachine<'a>,
     pub change_list: EditMachine<'a>,
     pub diffed: FxHashSet<ScopeIdx>,
     pub diffed: FxHashSet<ScopeIdx>,
-    pub lifecycle_events: VecDeque<LifeCycleEvent<'a>>,
-    pub vdom: ScopeArena,
-}
-pub enum LifeCycleEvent<'a> {
-    Mount {
-        caller: Weak<dyn Fn(&Scope) -> VNode + 'a>,
-        stable_scope_addr: Weak<VCompAssociatedScope>,
-        root_id: u32,
-    },
-    PropsChanged {
-        caller: Weak<dyn Fn(&Scope) -> VNode + 'a>,
-        stable_scope_addr: Weak<VCompAssociatedScope>,
-        root_id: u32,
-    },
-    SameProps {
-        caller: Weak<dyn Fn(&Scope) -> VNode + 'a>,
-        stable_scope_addr: Weak<VCompAssociatedScope>,
-        root_id: u32,
-    },
-    Replace {
-        caller: Weak<dyn Fn(&Scope) -> VNode + 'a>,
-        old_scope: Weak<VCompAssociatedScope>,
-        new_scope: Weak<VCompAssociatedScope>,
-        root_id: u32,
-    },
-    Remove {
-        stable_scope_addr: Weak<VCompAssociatedScope>,
-        root_id: u32,
-    },
+    pub components: ScopeArena,
+    pub event_queue: EventQueue,
+    pub seen_nodes: FxHashSet<ScopeIdx>,
 }
 }
 
 
 static COUNTER: AtomicU32 = AtomicU32::new(1);
 static COUNTER: AtomicU32 = AtomicU32::new(1);
-fn get_id() -> u32 {
+fn next_id() -> u32 {
     COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
     COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
 }
 }
 
 
 impl<'a> DiffMachine<'a> {
 impl<'a> DiffMachine<'a> {
-    pub fn new(vdom: ScopeArena) -> Self {
+    pub fn new(components: ScopeArena, cur_idx: ScopeIdx, event_queue: EventQueue) -> Self {
         Self {
         Self {
-            vdom,
+            components,
+            cur_idx,
+            event_queue,
             create_diffs: true,
             create_diffs: true,
-            lifecycle_events: VecDeque::new(),
             change_list: EditMachine::new(),
             change_list: EditMachine::new(),
             diffed: FxHashSet::default(),
             diffed: FxHashSet::default(),
+            seen_nodes: FxHashSet::default(),
         }
         }
     }
     }
 
 
@@ -112,14 +89,14 @@ impl<'a> DiffMachine<'a> {
         self.change_list.emitter
         self.change_list.emitter
     }
     }
 
 
-    pub fn diff_node(&mut self, old: &VNode<'a>, new: &VNode<'a>) {
+    pub fn diff_node(&mut self, old_node: &VNode<'a>, new_node: &VNode<'a>) {
         // pub fn diff_node(&mut self, old: &VNode<'a>, new: &VNode<'a>) {
         // pub fn diff_node(&mut self, old: &VNode<'a>, new: &VNode<'a>) {
         /*
         /*
         For each valid case, we "commit traversal", meaning we save this current position in the tree.
         For each valid case, we "commit traversal", meaning we save this current position in the tree.
         Then, we diff and queue an edit event (via chagelist). s single trees - when components show up, we save that traversal and then re-enter later.
         Then, we diff and queue an edit event (via chagelist). s single trees - when components show up, we save that traversal and then re-enter later.
         When re-entering, we reuse the EditList in DiffState
         When re-entering, we reuse the EditList in DiffState
         */
         */
-        match (old, new) {
+        match (old_node, new_node) {
             (VNode::Text(old_text), VNode::Text(new_text)) => {
             (VNode::Text(old_text), VNode::Text(new_text)) => {
                 if old_text != new_text {
                 if old_text != new_text {
                     self.change_list.commit_traversal();
                     self.change_list.commit_traversal();
@@ -129,13 +106,13 @@ impl<'a> DiffMachine<'a> {
 
 
             (VNode::Text(_), VNode::Element(_)) => {
             (VNode::Text(_), VNode::Element(_)) => {
                 self.change_list.commit_traversal();
                 self.change_list.commit_traversal();
-                self.create(new);
+                self.create(new_node);
                 self.change_list.replace_with();
                 self.change_list.replace_with();
             }
             }
 
 
             (VNode::Element(_), VNode::Text(_)) => {
             (VNode::Element(_), VNode::Text(_)) => {
                 self.change_list.commit_traversal();
                 self.change_list.commit_traversal();
-                self.create(new);
+                self.create(new_node);
                 self.change_list.replace_with();
                 self.change_list.replace_with();
             }
             }
 
 
@@ -153,57 +130,84 @@ impl<'a> DiffMachine<'a> {
             }
             }
 
 
             (VNode::Component(cold), VNode::Component(cnew)) => {
             (VNode::Component(cold), VNode::Component(cnew)) => {
-                // todo!("should not happen")
-                // self.change_list.commit_traversal();
+                // Make sure we're dealing with the same component (by function pointer)
                 if cold.user_fc == cnew.user_fc {
                 if cold.user_fc == cnew.user_fc {
-                    // todo: create a stable addr
-                    // let caller = Rc::downgrade(&cnew.caller);
-                    // let id = cold.stable_addr.borrow().unwrap();
-                    // *cnew.stable_addr.borrow_mut() = Some(id);
-                    // *cnew.ass_scope.borrow_mut() = *cold.ass_scope.borrow();
-
-                    // let scope = Rc::downgrade(&cold.ass_scope);
-                    todo!()
-                    // self.lifecycle_events
-                    //     .push_back(LifeCycleEvent::PropsChanged {
-                    //         caller,
-                    //         root_id: id,
-                    //         stable_scope_addr: scope,
-                    //     });
+                    // Make sure the new component vnode is referencing the right scope id
+                    let scope_id = cold.ass_scope.borrow().clone();
+                    *cnew.ass_scope.borrow_mut() = scope_id;
+
+                    // make sure the component's caller function is up to date
+                    self.components
+                        .with_scope(scope_id.unwrap(), |scope| {
+                            scope.caller = Rc::downgrade(&cnew.caller)
+                        })
+                        .unwrap();
+
+                    // React doesn't automatically memoize, but we do.
+                    // The cost is low enough to make it worth checking
+                    let should_render = match cold.comparator {
+                        Some(comparator) => comparator(cnew),
+                        None => true,
+                    };
+
+                    if should_render {
+                        self.change_list.commit_traversal();
+                        self.components
+                            .with_scope(scope_id.unwrap(), |f| {
+                                f.run_scope().unwrap();
+                            })
+                            .unwrap();
+                        // diff_machine.change_list.load_known_root(root_id);
+                        // run the scope
+                        //
+                    } else {
+                        // Component has memoized itself and doesn't need to be re-rendered.
+                        // We still need to make sure the child's props are up-to-date.
+                        // Don't commit traversal
+                    }
                 } else {
                 } else {
-                    // let caller = Rc::downgrade(&cnew.caller);
-                    // let id = cold.stable_addr.borrow().unwrap();
-                    // let old_scope = Rc::downgrade(&cold.ass_scope);
-                    // let new_scope = Rc::downgrade(&cnew.ass_scope);
-
-                    todo!()
-                    // self.lifecycle_events.push_back(LifeCycleEvent::Replace {
-                    //     caller,
-                    //     root_id: id,
-                    //     old_scope,
-                    //     new_scope,
-                    // });
+                    // A new component has shown up! We need to destroy the old node
+
+                    // Wipe the old one and plant the new one
+                    self.change_list.commit_traversal();
+                    self.create(new_node);
+                    self.change_list.replace_with();
+
+                    // Now we need to remove the old scope and all of its descendents
+                    let old_scope = cold.ass_scope.borrow().as_ref().unwrap().clone();
+                    self.destroy_scopes(old_scope);
                 }
                 }
             }
             }
 
 
             // todo: knock out any listeners
             // todo: knock out any listeners
-            (_, VNode::Component(_new)) => {
+            (_, VNode::Component(_)) => {
                 self.change_list.commit_traversal();
                 self.change_list.commit_traversal();
+                self.create(new_node);
+                self.change_list.replace_with();
             }
             }
 
 
+            // A component is being torn down in favor of a non-component node
             (VNode::Component(_old), _) => {
             (VNode::Component(_old), _) => {
-                todo!("Usage of component VNode not currently supported");
+                self.change_list.commit_traversal();
+                self.create(new_node);
+                self.change_list.replace_with();
+
+                // Destroy the original scope and any of its children
+                self.destroy_scopes(_old.ass_scope.borrow().unwrap());
             }
             }
 
 
+            // Anything suspended is not enabled ATM
             (VNode::Suspended, _) | (_, VNode::Suspended) => {
             (VNode::Suspended, _) | (_, VNode::Suspended) => {
                 todo!("Suspended components not currently available")
                 todo!("Suspended components not currently available")
             }
             }
 
 
-            (VNode::Fragment(_), VNode::Fragment(_)) => {
+            // (VNode::Fragment(_), VNode::Fragment(_)) => {
+            //     todo!("Fragments not currently supported in diffing")
+            // }
+            // Fragments are special
+            (VNode::Fragment(_), _) | (_, VNode::Fragment(_)) => {
                 todo!("Fragments not currently supported in diffing")
                 todo!("Fragments not currently supported in diffing")
             }
             }
-            (_, VNode::Fragment(_)) => todo!("Fragments not currently supported in diffing"),
-            (VNode::Fragment(_), _) => todo!("Fragments not currently supported in diffing"),
         }
         }
     }
     }
 
 
@@ -266,28 +270,74 @@ impl<'a> DiffMachine<'a> {
                 }
                 }
             }
             }
 
 
-            /*
-            todo: integrate re-entrace
-            */
             VNode::Component(component) => {
             VNode::Component(component) => {
-                todo!()
-                // self.change_list
-                //     .create_text_node("placeholder for vcomponent");
+                self.change_list
+                    .create_text_node("placeholder for vcomponent");
+
+                let root_id = next_id();
+                self.change_list.save_known_root(root_id);
+
+                log::debug!("Mounting a new component");
+                let caller: Weak<OpaqueComponent> = Rc::downgrade(&component.caller);
+
+                // We're modifying the component arena while holding onto references into the assoiated bump arenas of its children
+                // those references are stable, even if the component arena moves around in memory, thanks to the bump arenas.
+                // However, there is no way to convey this to rust, so we need to use unsafe to pierce through the lifetime.
+
+                // Insert a new scope into our component list
+                let idx = self
+                    .components
+                    .with(|components| {
+                        components.insert_with(|new_idx| {
+                            let cur_idx = self.cur_idx;
+                            let cur_scope = self.components.try_get(cur_idx).unwrap();
+                            let height = cur_scope.height + 1;
+                            Scope::new(
+                                caller,
+                                new_idx,
+                                Some(cur_idx),
+                                height,
+                                self.event_queue.new_channel(height, new_idx),
+                                self.components.clone(),
+                                &[],
+                            )
+                        })
+                    })
+                    .unwrap();
+
+                {
+                    let cur_component = self.components.try_get_mut(idx).unwrap();
+                    let mut ch = cur_component.descendents.borrow_mut();
+                    ch.insert(idx);
+                    std::mem::drop(ch);
+                }
 
 
-                // let id = get_id();
-                // *component.stable_addr.as_ref().borrow_mut() = Some(id);
-                // self.change_list.save_known_root(id);
-                // let scope = Rc::downgrade(&component.ass_scope);
-                // todo!()
-                // self.lifecycle_events.push_back(LifeCycleEvent::Mount {
-                //     caller: Rc::downgrade(&component.caller),
-                //     root_id: id,
-                //     stable_scope_addr: scope,
-                // });
+                // yaaaaay lifetimes out of thin air
+                // really tho, we're merging the frame lifetimes together
+                let inner: &'a mut _ = unsafe { &mut *self.components.0.borrow().arena.get() };
+                let new_component = inner.get_mut(idx).unwrap();
+
+                // Actually initialize the caller's slot with the right address
+                *component.ass_scope.borrow_mut() = Some(idx);
+
+                // Run the scope for one iteration to initialize it
+                new_component.run_scope().unwrap();
+
+                // // Navigate the diff machine to the right point in the output dom
+                // self.change_list.load_known_root(id);
+                // let root_id = component.stable_addr
+
+                // And then run the diff algorithm
+                self.diff_node(new_component.old_frame(), new_component.next_frame());
+
+                // Finally, insert this node as a seen node.
+                self.seen_nodes.insert(idx);
             }
             }
             VNode::Suspended => {
             VNode::Suspended => {
                 todo!("Creation of VNode::Suspended not yet supported")
                 todo!("Creation of VNode::Suspended not yet supported")
             }
             }
+
+            // we go the the "known root" but only operate on a sibling basis
             VNode::Fragment(frag) => {
             VNode::Fragment(frag) => {
                 //
                 //
                 todo!("Cannot current create fragments")
                 todo!("Cannot current create fragments")
@@ -295,6 +345,38 @@ impl<'a> DiffMachine<'a> {
         }
         }
     }
     }
 
 
+    /// Destroy a scope and all of its descendents.
+    ///
+    /// Calling this will run the destuctors on all hooks in the tree.
+    /// It will also add the destroyed nodes to the `seen_nodes` cache to prevent them from being renderered.
+    fn destroy_scopes(&mut self, old_scope: ScopeIdx) {
+        let mut nodes_to_delete = vec![old_scope];
+        let mut scopes_to_explore = vec![old_scope];
+
+        // explore the scope tree breadth first
+        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.try_get(scope_id).unwrap();
+            for child in scope.descendents.borrow().iter() {
+                // Add this node to be explored
+                scopes_to_explore.push(child.clone());
+
+                // Also add it for deletion
+                nodes_to_delete.push(child.clone());
+            }
+        }
+
+        // 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();
+            // do anything we need to do to delete the scope
+            // I think we need to run the destructors on the hooks
+            // TODO
+        }
+    }
+
     // Diff event listeners between `old` and `new`.
     // Diff event listeners between `old` and `new`.
     //
     //
     // The listeners' node must be on top of the change list stack:
     // The listeners' node must be on top of the change list stack:
@@ -303,7 +385,6 @@ impl<'a> DiffMachine<'a> {
     //
     //
     // The change list stack is left unchanged.
     // The change list stack is left unchanged.
     fn diff_listeners(&mut self, old: &[Listener<'_>], new: &[Listener<'_>]) {
     fn diff_listeners(&mut self, old: &[Listener<'_>], new: &[Listener<'_>]) {
-        // fn diff_listeners(&mut self, old: &[Listener<'a>], new: &[Listener<'a>]) {
         if !old.is_empty() || !new.is_empty() {
         if !old.is_empty() || !new.is_empty() {
             self.change_list.commit_traversal();
             self.change_list.commit_traversal();
         }
         }
@@ -325,26 +406,15 @@ impl<'a> DiffMachine<'a> {
                             .update_event_listener(event_type, new_l.scope, new_l.id)
                             .update_event_listener(event_type, new_l.scope, new_l.id)
                     }
                     }
 
 
-                    // if let Some(scope) = self.current_idx {
-                    //     let cb = CbIdx::from_gi_index(scope, l_idx);
-                    // self.change_list
-                    //     .update_event_listener(event_type, new_l.scope, new_l.id);
-                    // }
-
                     continue 'outer1;
                     continue 'outer1;
                 }
                 }
             }
             }
 
 
-            // if let Some(scope) = self.current_idx {
-            // let cb = CbIdx::from_gi_index(scope, l_idx);
             self.change_list
             self.change_list
                 .new_event_listener(event_type, new_l.scope, new_l.id);
                 .new_event_listener(event_type, new_l.scope, new_l.id);
-            // }
         }
         }
 
 
         'outer2: for old_l in old {
         'outer2: for old_l in old {
-            // registry.remove(old_l);
-
             for new_l in new {
             for new_l in new {
                 if new_l.event == old_l.event {
                 if new_l.event == old_l.event {
                     continue 'outer2;
                     continue 'outer2;
@@ -369,6 +439,9 @@ impl<'a> DiffMachine<'a> {
     ) {
     ) {
         // Do O(n^2) passes to add/update and remove attributes, since
         // Do O(n^2) passes to add/update and remove attributes, since
         // there are almost always very few attributes.
         // there are almost always very few attributes.
+        //
+        // The "fast" path is when the list of attributes name is identical and in the same order
+        // With the Rsx and Html macros, this will almost always be the case
         'outer: for new_attr in new {
         'outer: for new_attr in new {
             if new_attr.is_volatile() {
             if new_attr.is_volatile() {
                 self.change_list.commit_traversal();
                 self.change_list.commit_traversal();
@@ -386,6 +459,8 @@ impl<'a> DiffMachine<'a> {
                             );
                             );
                         }
                         }
                         continue 'outer;
                         continue 'outer;
+                    } else {
+                        // names are different, a varying order of attributes has arrived
                     }
                     }
                 }
                 }
 
 
@@ -433,12 +508,10 @@ impl<'a> DiffMachine<'a> {
                 (_, &VNode::Text(text)) => {
                 (_, &VNode::Text(text)) => {
                     self.change_list.commit_traversal();
                     self.change_list.commit_traversal();
                     self.change_list.set_text(text);
                     self.change_list.set_text(text);
-                    // for o in old {
-                    //     registry.remove_subtree(o);
-                    // }
                     return;
                     return;
                 }
                 }
 
 
+                // todo: any more optimizations
                 (_, _) => {}
                 (_, _) => {}
             }
             }
         }
         }
@@ -465,6 +538,7 @@ impl<'a> DiffMachine<'a> {
 
 
         if new_is_keyed && old_is_keyed {
         if new_is_keyed && old_is_keyed {
             let t = self.change_list.next_temporary();
             let t = self.change_list.next_temporary();
+            self.diff_keyed_children(old, new);
             self.change_list.set_next_temporary(t);
             self.change_list.set_next_temporary(t);
         } else {
         } else {
             self.diff_non_keyed_children(old, new);
             self.diff_non_keyed_children(old, new);

+ 1 - 1
packages/core/src/lib.rs

@@ -86,7 +86,7 @@ pub mod virtual_dom; // Most fun logic starts here, manages the lifecycle and su
 pub mod builder {
 pub mod builder {
     pub use super::nodebuilder::*;
     pub use super::nodebuilder::*;
 }
 }
-pub mod scope;
+
 pub mod support;
 pub mod support;
 
 
 // types used internally that are important
 // types used internally that are important

+ 8 - 6
packages/core/src/nodebuilder.rs

@@ -650,7 +650,7 @@ pub fn attr<'a>(name: &'static str, value: &'a str) -> Attribute<'a> {
     Attribute { name, value }
     Attribute { name, value }
 }
 }
 
 
-pub fn virtual_child<'a, T: Properties>(
+pub fn virtual_child<'a, T: Properties + 'a>(
     ctx: &NodeCtx<'a>,
     ctx: &NodeCtx<'a>,
     f: FC<T>,
     f: FC<T>,
     props: T,
     props: T,
@@ -658,9 +658,11 @@ pub fn virtual_child<'a, T: Properties>(
 ) -> VNode<'a> {
 ) -> VNode<'a> {
     // currently concerned about if props have a custom drop implementation
     // currently concerned about if props have a custom drop implementation
     // might override it with the props macro
     // might override it with the props macro
-    todo!()
-    // VNode::Component(
-    //     ctx.bump()
-    //         .alloc(crate::nodes::VComponent::new(f, props, key)),
-    // )
+    // todo!()
+    VNode::Component(
+        ctx.bump()
+            .alloc(crate::nodes::VComponent::new(ctx.bump(), f, props, key)),
+        // ctx.bump()
+        //     .alloc(crate::nodes::VComponent::new(f, props, key)),
+    )
 }
 }

+ 43 - 13
packages/core/src/nodes.rs

@@ -226,7 +226,7 @@ pub struct VComponent<'src> {
     pub ass_scope: RefCell<VCompAssociatedScope>,
     pub ass_scope: RefCell<VCompAssociatedScope>,
 
 
     // pub comparator: Rc<dyn Fn(&VComponent) -> bool + 'src>,
     // pub comparator: Rc<dyn Fn(&VComponent) -> bool + 'src>,
-    pub caller: Rc<dyn Fn(&Scope) -> VNode + 'src>,
+    pub caller: Rc<dyn Fn(&Scope) -> VNode>,
 
 
     pub children: &'src [VNode<'src>],
     pub children: &'src [VNode<'src>],
 
 
@@ -287,18 +287,24 @@ impl<'a> VComponent<'a> {
 
 
         // let prref: &'a P = props.as_ref();
         // let prref: &'a P = props.as_ref();
 
 
-        let caller: Rc<dyn Fn(&Scope) -> VNode> = Rc::new(move |scope| {
-            //
-            // let props2 = bad_props;
-            // props.as_ref()
-            // let ctx = Context {
-            //     props: prref,
-            //     scope,
-            // };
-            todo!()
-            // component(ctx)
-        });
-        // let caller = Rc::new(create_closure(component, raw_props));
+        // let r = create_closure(component, raw_props);
+        // let caller: Rc<dyn for<'g> Fn(&'g Scope) -> VNode<'g>> = Rc::new(move |scope| {
+        //     // r(scope);
+        //     //
+        //     // let props2 = bad_props;
+        //     // props.as_ref();
+        //     // let ctx = Context {
+        //     //     props: prref,
+        //     //     scope,
+        //     // };
+        //     // let ctx: Context<'g, P> = todo!();
+        //     // todo!()
+        //     // let r = component(ctx);
+        //     todo!()
+        // });
+        let caller = create_closure(component, raw_props);
+
+        // let caller: Rc<dyn Fn(&Scope) -> VNode> = Rc::new(create_closure(component, raw_props));
 
 
         let key = match key {
         let key = match key {
             Some(key) => NodeKey::new(key),
             Some(key) => NodeKey::new(key),
@@ -321,6 +327,30 @@ impl<'a> VComponent<'a> {
     }
     }
 }
 }
 
 
+type Captured<'a> = Rc<dyn for<'r> Fn(&'r Scope) -> VNode<'r> + 'a>;
+
+fn create_closure<'a, P: Properties + 'a>(
+    component: FC<P>,
+    raw_props: *const (),
+) -> Rc<dyn for<'r> Fn(&'r Scope) -> VNode<'r>> {
+    // ) -> impl for<'r> Fn(&'r Scope) -> VNode<'r> {
+    let g: Captured = Rc::new(move |scp: &Scope| -> VNode {
+        // cast back into the right lifetime
+        let safe_props: &'_ P = unsafe { &*(raw_props as *const P) };
+        // let ctx: Context<P2> = todo!();
+        let ctx: Context<P> = Context {
+            props: safe_props,
+            scope: scp,
+        };
+
+        let g = component(ctx);
+        let g2 = unsafe { std::mem::transmute(g) };
+        g2
+    });
+    let r: Captured<'static> = unsafe { std::mem::transmute(g) };
+    r
+}
+
 pub struct VFragment<'src> {
 pub struct VFragment<'src> {
     pub key: NodeKey<'src>,
     pub key: NodeKey<'src>,
     pub children: &'src [VNode<'src>],
     pub children: &'src [VNode<'src>],

+ 0 - 488
packages/core/src/scope.rs

@@ -1,488 +0,0 @@
-use crate::{arena::ScopeArena, innerlude::*};
-use bumpalo::Bump;
-use generational_arena::Arena;
-use std::{
-    any::{Any, TypeId},
-    cell::RefCell,
-    collections::{HashMap, HashSet, VecDeque},
-    fmt::Debug,
-    future::Future,
-    ops::Deref,
-    pin::Pin,
-    rc::{Rc, Weak},
-};
-
-/// Every component in Dioxus is represented by a `Scope`.
-///
-/// Scopes contain the state for hooks, the component's props, and other lifecycle information.
-///
-/// Scopes are allocated in a generational arena. As components are mounted/unmounted, they will replace slots of dead components.
-/// The actual contents of the hooks, though, will be allocated with the standard allocator. These should not allocate as frequently.
-pub struct Scope {
-    // The parent's scope ID
-    pub parent: Option<ScopeIdx>,
-
-    // IDs of children that this scope has created
-    // This enables us to drop the children and their children when this scope is destroyed
-    pub(crate) descendents: RefCell<HashSet<ScopeIdx>>,
-
-    pub(crate) child_nodes: &'static [VNode<'static>],
-
-    // 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: ScopeArena,
-
-    pub shared_contexts: RefCell<HashMap<TypeId, Rc<dyn Any>>>,
-
-    // Our own ID accessible from the component map
-    pub arena_idx: ScopeIdx,
-
-    pub height: u32,
-
-    pub event_channel: Rc<dyn Fn() + 'static>,
-
-    // pub event_queue: EventQueue,
-    pub caller: Weak<OpaqueComponent<'static>>,
-
-    pub hookidx: RefCell<usize>,
-
-    // ==========================
-    // slightly unsafe stuff
-    // ==========================
-    // an internal, highly efficient storage of vnodes
-    pub frames: ActiveFrame,
-
-    // These hooks are actually references into the hook arena
-    // These two could be combined with "OwningRef" to remove unsafe usage
-    // or we could dedicate a tiny bump arena just for them
-    // could also use ourborous
-    hooks: RefCell<Vec<Hook>>,
-
-    // Unsafety:
-    // - is self-refenrential and therefore needs to point into the bump
-    // Stores references into the listeners attached to the vnodes
-    // NEEDS TO BE PRIVATE
-    pub(crate) listeners: RefCell<Vec<*const dyn Fn(VirtualEvent)>>,
-}
-
-// We need to pin the hook so it doesn't move as we initialize the list of hooks
-type Hook = Pin<Box<dyn std::any::Any>>;
-type EventChannel = Rc<dyn Fn()>;
-
-impl Scope {
-    // we are being created in the scope of an existing component (where the creator_node lifetime comes into play)
-    // we are going to break this lifetime by force in order to save it on ourselves.
-    // To make sure that the lifetime isn't truly broken, we receive a Weak RC so we can't keep it around after the parent dies.
-    // This should never happen, but is a good check to keep around
-    //
-    // Scopes cannot be made anywhere else except for this file
-    // Therefore, their lifetimes are connected exclusively to the virtual dom
-    pub(crate) fn new<'creator_node>(
-        caller: Weak<OpaqueComponent<'creator_node>>,
-        arena_idx: ScopeIdx,
-        parent: Option<ScopeIdx>,
-        height: u32,
-        event_channel: EventChannel,
-        arena_link: ScopeArena,
-        child_nodes: &'creator_node [VNode<'creator_node>],
-    ) -> Self {
-        log::debug!(
-            "New scope created, height is {}, idx is {:?}",
-            height,
-            arena_idx
-        );
-
-        // The function to run this scope is actually located in the parent's bump arena.
-        // Every time the parent is updated, that function is invalidated via double-buffering wiping the old frame.
-        // If children try to run this invalid caller, it *will* result in UB.
-        //
-        // During the lifecycle progression process, this caller will need to be updated. Right now,
-        // until formal safety abstractions are implemented, we will just use unsafe to "detach" the caller
-        // lifetime from the bump arena, exposing ourselves to this potential for invalidation. Truthfully,
-        // this is a bit of a hack, but will remain this way until we've figured out a cleaner solution.
-        //
-        // Not the best solution, so TODO on removing this in favor of a dedicated resource abstraction.
-        let caller = unsafe {
-            std::mem::transmute::<
-                Weak<OpaqueComponent<'creator_node>>,
-                Weak<OpaqueComponent<'static>>,
-            >(caller)
-        };
-
-        Self {
-            child_nodes: &[],
-            caller,
-            parent,
-            arena_idx,
-            height,
-            event_channel,
-            arena_link,
-            frames: ActiveFrame::new(),
-            hooks: Default::default(),
-            shared_contexts: Default::default(),
-            listeners: Default::default(),
-            hookidx: Default::default(),
-            descendents: Default::default(),
-        }
-    }
-
-    pub fn update_caller<'creator_node>(&mut self, caller: Weak<OpaqueComponent<'creator_node>>) {
-        let broken_caller = unsafe {
-            std::mem::transmute::<
-                Weak<OpaqueComponent<'creator_node>>,
-                Weak<OpaqueComponent<'static>>,
-            >(caller)
-        };
-
-        self.caller = broken_caller;
-    }
-
-    /// Create a new context and run the component with references from the Virtual Dom
-    /// This function downcasts the function pointer based on the stored props_type
-    ///
-    /// Props is ?Sized because we borrow the props and don't need to know the size. P (sized) is used as a marker (unsized)
-    pub fn run_scope<'sel>(&'sel mut self) -> Result<()> {
-        // Cycle to the next frame and then reset it
-        // This breaks any latent references, invalidating every pointer referencing into it.
-        self.frames.next().bump.reset();
-
-        // Remove all the outdated listeners
-        //
-        self.listeners
-            .try_borrow_mut()
-            .ok()
-            .ok_or(Error::FatalInternal("Borrowing listener failed"))?
-            .drain(..);
-
-        *self.hookidx.borrow_mut() = 0;
-
-        let caller = self
-            .caller
-            .upgrade()
-            .ok_or(Error::FatalInternal("Failed to get caller"))?;
-
-        // Cast the caller ptr from static to one with our own reference
-        let c2: &OpaqueComponent<'static> = caller.as_ref();
-        let c3: &OpaqueComponent<'_> = unsafe { std::mem::transmute(c2) };
-
-        let unsafe_head = unsafe { self.own_vnodes(c3) };
-
-        self.frames.cur_frame_mut().head_node = unsafe_head;
-
-        Ok(())
-    }
-
-    // this is its own function so we can preciesly control how lifetimes flow
-    unsafe fn own_vnodes<'a>(&'a self, f: &OpaqueComponent<'a>) -> VNode<'static> {
-        let new_head: VNode<'a> = f(self);
-        let out: VNode<'static> = std::mem::transmute(new_head);
-        out
-    }
-
-    // A safe wrapper around calling listeners
-    // calling listeners will invalidate the list of listeners
-    // The listener list will be completely drained because the next frame will write over previous listeners
-    pub fn call_listener(&mut self, trigger: EventTrigger) -> Result<()> {
-        let EventTrigger {
-            listener_id, event, ..
-        } = trigger;
-        //
-        unsafe {
-            // Convert the raw ptr into an actual object
-            // This operation is assumed to be safe
-            let listener_fn = self
-                .listeners
-                .try_borrow()
-                .ok()
-                .ok_or(Error::FatalInternal("Borrowing listener failed"))?
-                .get(listener_id as usize)
-                .ok_or(Error::FatalInternal("Event should exist if triggered"))?
-                .as_ref()
-                .ok_or(Error::FatalInternal("Raw event ptr is invalid"))?;
-
-            // Run the callback with the user event
-            listener_fn(event);
-        }
-        Ok(())
-    }
-
-    pub fn next_frame<'bump>(&'bump self) -> &'bump VNode<'bump> {
-        self.frames.current_head_node()
-    }
-
-    pub fn old_frame<'bump>(&'bump self) -> &'bump VNode<'bump> {
-        self.frames.prev_head_node()
-    }
-
-    pub fn cur_frame(&self) -> &BumpFrame {
-        self.frames.cur_frame()
-    }
-}
-
-/// Components in Dioxus use the "Context" object to interact with their lifecycle.
-/// This lets components schedule updates, integrate hooks, and expose their context via the context api.
-///
-/// Properties passed down from the parent component are also directly accessible via the exposed "props" field.
-///
-/// ```ignore
-/// #[derive(Properties)]
-/// struct Props {
-///     name: String
-///
-/// }
-///
-/// fn example(ctx: Context, props: &Props -> VNode {
-///     html! {
-///         <div> "Hello, {ctx.ctx.name}" </div>
-///     }
-/// }
-/// ```
-// todo: force lifetime of source into T as a valid lifetime too
-// it's definitely possible, just needs some more messing around
-
-pub struct Context<'src, T> {
-    pub props: &'src T,
-    pub scope: &'src Scope,
-}
-
-impl<'src, T> Copy for Context<'src, T> {}
-impl<'src, T> Clone for Context<'src, T> {
-    fn clone(&self) -> Self {
-        Self {
-            props: self.props,
-            scope: self.scope,
-        }
-    }
-}
-
-impl<'a, T> Deref for Context<'a, T> {
-    type Target = &'a T;
-
-    fn deref(&self) -> &Self::Target {
-        &self.props
-    }
-}
-
-impl<'src, T> Scoped<'src> for Context<'src, T> {
-    fn get_scope(&self) -> &'src Scope {
-        self.scope
-    }
-}
-
-pub trait Scoped<'src>: Sized {
-    fn get_scope(&self) -> &'src Scope;
-
-    /// Access the children elements passed into the component
-    fn children(&self) -> &'src [VNode<'src>] {
-        // We're re-casting the nodes back out
-        // They don't really have a static lifetime
-        unsafe {
-            let scope = self.get_scope();
-            let nodes: &'src [VNode<'static>] = scope.child_nodes;
-
-            // cast the lifetime back correctly
-            std::mem::transmute(nodes)
-        }
-    }
-
-    /// Create a subscription that schedules a future render for the reference component
-    fn schedule_update(&self) -> Rc<dyn Fn() + 'static> {
-        self.get_scope().event_channel.clone()
-    }
-
-    /// Take a lazy VNode structure and actually build it with the context of the VDom's efficient VNode allocator.
-    ///
-    /// This function consumes the context and absorb the lifetime, so these VNodes *must* be returned.
-    ///
-    /// ## Example
-    ///
-    /// ```ignore
-    /// fn Component(ctx: Context<()>) -> VNode {
-    ///     // Lazy assemble the VNode tree
-    ///     let lazy_tree = html! {<div> "Hello World" </div>};
-    ///     
-    ///     // Actually build the tree and allocate it
-    ///     ctx.render(lazy_tree)
-    /// }
-    ///```
-    fn render<'a, F: for<'b> FnOnce(&'b NodeCtx<'src>) -> VNode<'src> + 'src + 'a>(
-        self,
-        lazy_nodes: LazyNodes<'src, F>,
-    ) -> VNode<'src> {
-        lazy_nodes.into_vnode(&NodeCtx {
-            scope_ref: self.get_scope(),
-            listener_id: 0.into(),
-        })
-    }
-
-    // impl<'scope> Context<'scope> {
-    /// Store a value between renders
-    ///
-    /// - Initializer: closure used to create the initial hook state
-    /// - Runner: closure used to output a value every time the hook is used
-    /// - Cleanup: closure used to teardown the hook once the dom is cleaned up
-    ///
-    /// ```ignore
-    /// // use_ref is the simplest way of storing a value between renders
-    /// pub fn use_ref<T: 'static>(initial_value: impl FnOnce() -> T + 'static) -> Rc<RefCell<T>> {
-    ///     use_hook(
-    ///         || Rc::new(RefCell::new(initial_value())),
-    ///         |state| state.clone(),
-    ///         |_| {},
-    ///     )
-    /// }
-    /// ```
-    fn use_hook<InternalHookState: 'static, Output: 'src>(
-        &self,
-
-        // The closure that builds the hook state
-        initializer: impl FnOnce() -> InternalHookState,
-
-        // The closure that takes the hookstate and returns some value
-        runner: impl FnOnce(&'src mut InternalHookState) -> Output,
-
-        // The closure that cleans up whatever mess is left when the component gets torn down
-        // TODO: add this to the "clean up" group for when the component is dropped
-        _cleanup: impl FnOnce(InternalHookState),
-    ) -> Output {
-        let scope = self.get_scope();
-
-        let idx = *scope.hookidx.borrow();
-
-        // Grab out the hook list
-        let mut hooks = scope.hooks.borrow_mut();
-
-        // If the idx is the same as the hook length, then we need to add the current hook
-        if idx >= hooks.len() {
-            let new_state = initializer();
-            hooks.push(Box::pin(new_state));
-        }
-
-        *scope.hookidx.borrow_mut() += 1;
-
-        let stable_ref = hooks
-            .get_mut(idx)
-            .expect("Should not fail, idx is validated")
-            .as_mut();
-
-        let pinned_state = unsafe { Pin::get_unchecked_mut(stable_ref) };
-
-        let internal_state = pinned_state.downcast_mut::<InternalHookState>().expect(
-            r###"
-Unable to retrive the hook that was initialized in this index.
-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.
-Any function prefixed with "use" should not be called conditionally.
-            "###,
-        );
-
-        // We extend the lifetime of the internal state
-        runner(unsafe { &mut *(internal_state as *mut _) })
-    }
-
-    /// This hook enables the ability to expose state to children further down the VirtualDOM Tree.
-    ///
-    /// This is a hook, so it may not be called conditionally!
-    ///
-    /// The init method is ran *only* on first use, otherwise it is ignored. However, it uses hooks (ie `use`)
-    /// so don't put it in a conditional.
-    ///
-    /// When the component is dropped, so is the context. Be aware of this behavior when consuming
-    /// the context via Rc/Weak.
-    ///
-    ///
-    ///
-    fn use_create_context<T: 'static>(&self, init: impl Fn() -> T) {
-        let scope = self.get_scope();
-        let mut ctxs = scope.shared_contexts.borrow_mut();
-        let ty = TypeId::of::<T>();
-
-        let is_initialized = self.use_hook(
-            || false,
-            |s| {
-                let i = s.clone();
-                *s = true;
-                i
-            },
-            |_| {},
-        );
-
-        match (is_initialized, ctxs.contains_key(&ty)) {
-            // Do nothing, already initialized and already exists
-            (true, true) => {}
-
-            // Needs to be initialized
-            (false, false) => {
-                log::debug!("Initializing context...");
-                ctxs.insert(ty, Rc::new(init()));
-            }
-
-            _ => debug_assert!(false, "Cannot initialize two contexts of the same type"),
-        }
-    }
-
-    /// There are hooks going on here!
-    fn use_context<T: 'static>(&self) -> &'src Rc<T> {
-        self.try_use_context().unwrap()
-    }
-
-    /// Uses a context, storing the cached value around
-    fn try_use_context<T: 'static>(&self) -> Result<&'src Rc<T>> {
-        struct UseContextHook<C> {
-            par: Option<Rc<C>>,
-            we: Option<Weak<C>>,
-        }
-
-        self.use_hook(
-            move || UseContextHook {
-                par: None as Option<Rc<T>>,
-                we: None as Option<Weak<T>>,
-            },
-            move |hook| {
-                let scope = self.get_scope();
-                let mut scope = Some(scope);
-
-                if let Some(we) = &hook.we {
-                    if let Some(re) = we.upgrade() {
-                        hook.par = Some(re);
-                        return Ok(hook.par.as_ref().unwrap());
-                    }
-                }
-
-                let ty = TypeId::of::<T>();
-                while let Some(inner) = scope {
-                    log::debug!("Searching {:#?} for valid shared_context", inner.arena_idx);
-                    let shared_contexts = inner.shared_contexts.borrow();
-
-                    if let Some(shared_ctx) = shared_contexts.get(&ty) {
-                        log::debug!("found matching ctx");
-                        let rc = shared_ctx
-                            .clone()
-                            .downcast::<T>()
-                            .expect("Should not fail, already validated the type from the hashmap");
-
-                        hook.we = Some(Rc::downgrade(&rc));
-                        hook.par = Some(rc);
-                        return Ok(hook.par.as_ref().unwrap());
-                    } else {
-                        match inner.parent {
-                            Some(parent_id) => {
-                                let parent = inner
-                                    .arena_link
-                                    .try_get(parent_id)
-                                    .map_err(|_| Error::FatalInternal("Failed to find parent"))?;
-
-                                scope = Some(parent);
-                            }
-                            None => return Err(Error::MissingSharedContext),
-                        }
-                    }
-                }
-
-                Err(Error::MissingSharedContext)
-            },
-            |_| {},
-        )
-    }
-}

+ 0 - 1
packages/core/src/support.rs

@@ -1,4 +1,3 @@
-pub use crate::scope::*;
 use crate::{arena::ScopeArena, innerlude::*};
 use crate::{arena::ScopeArena, innerlude::*};
 use bumpalo::Bump;
 use bumpalo::Bump;
 use generational_arena::Arena;
 use generational_arena::Arena;

+ 69 - 215
packages/core/src/virtual_dom.rs

@@ -52,8 +52,8 @@ pub struct VirtualDom {
 
 
     /// a strong allocation to the "caller" for the original component and its props
     /// a strong allocation to the "caller" for the original component and its props
     #[doc(hidden)]
     #[doc(hidden)]
-    _root_caller: Rc<OpaqueComponent<'static>>,
-
+    _root_caller: Rc<OpaqueComponent>,
+    // _root_caller: Rc<OpaqueComponent<'static>>,
     /// Type of the original ctx. This is stored as TypeId so VirtualDom does not need to be generic.
     /// Type of the original ctx. 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
     /// Whenver props need to be updated, an Error will be thrown if the new props do not
@@ -137,7 +137,8 @@ impl VirtualDom {
 
 
         // Normally, a component would be passed as a child in the RSX macro which automatically produces OpaqueComponents
         // Normally, a component would be passed as a child in the RSX macro which automatically produces OpaqueComponents
         // Here, we need to make it manually, using an RC to force the Weak reference to stick around for the main scope.
         // Here, we need to make it manually, using an RC to force the Weak reference to stick around for the main scope.
-        let _root_caller: Rc<OpaqueComponent<'static>> = Rc::new(move |scope| {
+        let _root_caller: Rc<OpaqueComponent> = Rc::new(move |scope| {
+            // let _root_caller: Rc<OpaqueComponent<'static>> = Rc::new(move |scope| {
             // the lifetime of this closure is just as long as the lifetime on the scope reference
             // the lifetime of this closure is just as long as the lifetime on the scope reference
             // this closure moves root props (which is static) into this closure
             // this closure moves root props (which is static) into this closure
             let props = unsafe { &*(&root_props as *const _) };
             let props = unsafe { &*(&root_props as *const _) };
@@ -154,10 +155,11 @@ impl VirtualDom {
         // Make the first scope
         // Make the first scope
         // We don't run the component though, so renderers will need to call "rebuild" when they initialize their DOM
         // We don't run the component though, so renderers will need to call "rebuild" when they initialize their DOM
         let link = components.clone();
         let link = components.clone();
-        let event_channel = Rc::new(move || {});
+
         let base_scope = components
         let base_scope = components
             .with(|arena| {
             .with(|arena| {
                 arena.insert_with(move |myidx| {
                 arena.insert_with(move |myidx| {
+                    let event_channel = _event_queue.new_channel(0, myidx);
                     Scope::new(caller_ref, myidx, None, 0, event_channel, link, &[])
                     Scope::new(caller_ref, myidx, None, 0, event_channel, link, &[])
                 })
                 })
             })
             })
@@ -175,21 +177,24 @@ impl VirtualDom {
     /// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom rom scratch
     /// 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
     /// Currently this doesn't do what we want it to do
     pub fn rebuild<'s>(&'s mut self) -> Result<EditList<'s>> {
     pub fn rebuild<'s>(&'s mut self) -> Result<EditList<'s>> {
-        todo!()
-        // let mut diff_machine = DiffMachine::new();
+        let mut diff_machine = DiffMachine::new(
+            self.components.clone(),
+            self.base_scope,
+            self.event_queue.clone(),
+        );
 
 
-        // // Schedule an update and then immediately call it on the root component
-        // // This is akin to a hook being called from a listener and requring a re-render
-        // // Instead, this is done on top-level component
+        // Schedule an update and then immediately call it on the root component
+        // This is akin to a hook being called from a listener and requring a re-render
+        // Instead, this is done on top-level component
 
 
-        // let base = self.components.try_get(self.base_scope)?;
+        let base = self.components.try_get(self.base_scope)?;
 
 
-        // let update = &base.event_channel;
-        // update();
+        let update = &base.event_channel;
+        update();
 
 
-        // self.progress_completely(&mut diff_machine)?;
+        self.progress_completely(&mut diff_machine)?;
 
 
-        // Ok(diff_machine.consume())
+        Ok(diff_machine.consume())
     }
     }
 }
 }
 
 
@@ -245,7 +250,9 @@ impl VirtualDom {
 
 
         self.components.try_get_mut(id)?.call_listener(event)?;
         self.components.try_get_mut(id)?.call_listener(event)?;
 
 
-        let mut diff_machine = DiffMachine::new(self.components.clone());
+        let mut diff_machine =
+            DiffMachine::new(self.components.clone(), id, self.event_queue.clone());
+
         self.progress_completely(&mut diff_machine)?;
         self.progress_completely(&mut diff_machine)?;
 
 
         Ok(diff_machine.consume())
         Ok(diff_machine.consume())
@@ -260,212 +267,48 @@ impl VirtualDom {
         diff_machine: &'_ mut DiffMachine<'s>,
         diff_machine: &'_ mut DiffMachine<'s>,
     ) -> Result<()> {
     ) -> Result<()> {
         // Add this component to the list of components that need to be difed
         // Add this component to the list of components that need to be difed
-        #[allow(unused_assignments)]
-        let mut cur_height: u32 = 0;
+        // #[allow(unused_assignments)]
+        // let mut cur_height: u32 = 0;
 
 
         // Now, there are events in the queue
         // Now, there are events in the queue
-        let mut seen_nodes = HashSet::<ScopeIdx>::new();
         let mut updates = self.event_queue.0.as_ref().borrow_mut();
         let mut updates = self.event_queue.0.as_ref().borrow_mut();
 
 
         // Order the nodes by their height, we want the biggest nodes on the top
         // Order the nodes by their height, we want the biggest nodes on the top
         // This prevents us from running the same component multiple times
         // This prevents us from running the same component multiple times
         updates.sort_unstable();
         updates.sort_unstable();
 
 
+        log::debug!("There are: {:#?} updates to be processed", updates.len());
+
         // Iterate through the triggered nodes (sorted by height) and begin to diff them
         // Iterate through the triggered nodes (sorted by height) and begin to diff them
         for update in updates.drain(..) {
         for update in updates.drain(..) {
+            log::debug!("Running updates for: {:#?}", update);
             // Make sure this isn't a node we've already seen, we don't want to double-render anything
             // Make sure this isn't a node we've already seen, we don't want to double-render anything
             // If we double-renderer something, this would cause memory safety issues
             // If we double-renderer something, this would cause memory safety issues
-            if seen_nodes.contains(&update.idx) {
+            if diff_machine.seen_nodes.contains(&update.idx) {
                 continue;
                 continue;
             }
             }
 
 
             // Now, all the "seen nodes" are nodes that got notified by running this listener
             // Now, all the "seen nodes" are nodes that got notified by running this listener
-            seen_nodes.insert(update.idx.clone());
+            diff_machine.seen_nodes.insert(update.idx.clone());
 
 
             // Start a new mutable borrow to components
             // Start a new mutable borrow to components
             // We are guaranteeed that this scope is unique because we are tracking which nodes have modified
             // We are guaranteeed that this scope is unique because we are tracking which nodes have modified
 
 
             let cur_component = self.components.try_get_mut(update.idx).unwrap();
             let cur_component = self.components.try_get_mut(update.idx).unwrap();
+            // let inner: &'s mut _ = unsafe { &mut *self.components.0.borrow().arena.get() };
+            // let cur_component = inner.get_mut(update.idx).unwrap();
 
 
             cur_component.run_scope()?;
             cur_component.run_scope()?;
 
 
             diff_machine.diff_node(cur_component.old_frame(), cur_component.next_frame());
             diff_machine.diff_node(cur_component.old_frame(), cur_component.next_frame());
 
 
-            cur_height = cur_component.height;
-
-            log::debug!(
-                "Processing update: {:#?} with height {}",
-                &update.idx,
-                cur_height
-            );
-
-            // Now, the entire subtree has been invalidated. We need to descend depth-first and process
-            // any updates that the diff machine has proprogated into the component lifecycle queue
-            while let Some(event) = diff_machine.lifecycle_events.pop_front() {
-                match event {
-                    // A new component has been computed from the diffing algorithm
-                    // create a new component in the arena, run it, move the diffing machine to this new spot, and then diff it
-                    // this will flood the lifecycle queue with new updates to build up the subtree
-                    LifeCycleEvent::Mount {
-                        caller,
-                        root_id: id,
-                        stable_scope_addr,
-                    } => {
-                        log::debug!("Mounting a new component");
-
-                        // We're modifying the component arena while holding onto references into the assoiated bump arenas of its children
-                        // those references are stable, even if the component arena moves around in memory, thanks to the bump arenas.
-                        // However, there is no way to convey this to rust, so we need to use unsafe to pierce through the lifetime.
-
-                        // Insert a new scope into our component list
-                        let idx = self.components.with(|components| {
-                            components.insert_with(|new_idx| {
-                                let height = cur_height + 1;
-                                Scope::new(
-                                    caller,
-                                    new_idx,
-                                    Some(cur_component.arena_idx),
-                                    height,
-                                    self.event_queue.new_channel(height, new_idx),
-                                    self.components.clone(),
-                                    &[],
-                                )
-                            })
-                        })?;
-
-                        {
-                            let cur_component = self.components.try_get_mut(update.idx).unwrap();
-                            let mut ch = cur_component.descendents.borrow_mut();
-                            ch.insert(idx);
-                            std::mem::drop(ch);
-                        }
-
-                        // Grab out that component
-                        let new_component = self.components.try_get_mut(idx).unwrap();
-
-                        // Actually initialize the caller's slot with the right address
-                        *stable_scope_addr.upgrade().unwrap().as_ref().borrow_mut() = Some(idx);
-
-                        // Run the scope for one iteration to initialize it
-                        new_component.run_scope()?;
-
-                        // Navigate the diff machine to the right point in the output dom
-                        diff_machine.change_list.load_known_root(id);
-
-                        // And then run the diff algorithm
-                        diff_machine
-                            .diff_node(new_component.old_frame(), new_component.next_frame());
-
-                        // Finally, insert this node as a seen node.
-                        seen_nodes.insert(idx);
-                    }
+            // cur_height = cur_component.height;
 
 
-                    // A component has remained in the same location but its properties have changed
-                    // We need to process this component and then dump the output lifecycle events into the queue
-                    LifeCycleEvent::PropsChanged {
-                        caller,
-                        root_id,
-                        stable_scope_addr,
-                    } => {
-                        log::debug!("Updating a component after its props have changed");
-
-                        // Get the stable index to the target component
-                        // This *should* exist due to guarantees in the diff algorithm
-                        let idx = stable_scope_addr
-                            .upgrade()
-                            .unwrap()
-                            .as_ref()
-                            .borrow()
-                            .unwrap();
-
-                        // Grab out that component
-                        let component = self.components.try_get_mut(idx).unwrap();
-
-                        // We have to move the caller over or running the scope will fail
-                        component.update_caller(caller);
-
-                        // Run the scope
-                        component.run_scope()?;
-
-                        // Navigate the diff machine to the right point in the output dom
-                        diff_machine.change_list.load_known_root(root_id);
-
-                        // And then run the diff algorithm
-                        diff_machine.diff_node(component.old_frame(), component.next_frame());
-
-                        // Finally, insert this node as a seen node.
-                        seen_nodes.insert(idx);
-                    }
-
-                    // A component's parent has updated, but its properties did not change.
-                    // This means the caller ptr is invalidated and needs to be updated, but the component itself does not need to be re-ran
-                    LifeCycleEvent::SameProps {
-                        caller,
-                        stable_scope_addr,
-                        ..
-                    } => {
-                        // In this case, the parent made a new VNode that resulted in the same props for us
-                        // However, since our caller is located in a Bump frame, we need to update the caller pointer (which is now invalid)
-                        log::debug!("Received the same props");
-
-                        // Get the stable index to the target component
-                        // This *should* exist due to guarantees in the diff algorithm
-                        let idx = stable_scope_addr
-                            .upgrade()
-                            .unwrap()
-                            .as_ref()
-                            .borrow()
-                            .unwrap();
-
-                        // Grab out that component
-                        let component = self.components.try_get_mut(idx).unwrap();
-
-                        // We have to move the caller over or running the scope will fail
-                        component.update_caller(caller);
-
-                        // This time, we will not add it to our seen nodes since we did not actually run it
-                    }
-
-                    LifeCycleEvent::Remove {
-                        root_id,
-                        stable_scope_addr,
-                    } => {
-                        let id = stable_scope_addr
-                            .upgrade()
-                            .unwrap()
-                            .as_ref()
-                            .borrow()
-                            .unwrap();
-
-                        log::warn!("Removing node {:#?}", id);
-
-                        // This would normally be recursive but makes sense to do linear to
-                        let mut children_to_remove = VecDeque::new();
-                        children_to_remove.push_back(id);
-
-                        // Accumulate all the child components that need to be removed
-                        while let Some(child_id) = children_to_remove.pop_back() {
-                            let comp = self.components.try_get(child_id).unwrap();
-                            let children = comp.descendents.borrow();
-                            for child in children.iter() {
-                                children_to_remove.push_front(*child);
-                            }
-                            log::debug!("Removing component: {:#?}", child_id);
-                            self.components
-                                .with(|components| components.remove(child_id).unwrap())
-                                .unwrap();
-                        }
-                    }
-
-                    LifeCycleEvent::Replace {
-                        caller,
-                        root_id: id,
-                        ..
-                    } => {
-                        unimplemented!("This feature (Replace) is unimplemented")
-                    }
-                }
-            }
+            // log::debug!(
+            //     "Processing update: {:#?} with height {}",
+            //     &update.idx,
+            //     cur_height
+            // );
         }
         }
 
 
         Ok(())
         Ok(())
@@ -489,7 +332,7 @@ pub struct Scope {
 
 
     // IDs of children that this scope has created
     // IDs of children that this scope has created
     // This enables us to drop the children and their children when this scope is destroyed
     // This enables us to drop the children and their children when this scope is destroyed
-    descendents: RefCell<HashSet<ScopeIdx>>,
+    pub(crate) descendents: RefCell<HashSet<ScopeIdx>>,
 
 
     child_nodes: &'static [VNode<'static>],
     child_nodes: &'static [VNode<'static>],
 
 
@@ -507,8 +350,8 @@ pub struct Scope {
     pub event_channel: Rc<dyn Fn() + 'static>,
     pub event_channel: Rc<dyn Fn() + 'static>,
 
 
     // pub event_queue: EventQueue,
     // pub event_queue: EventQueue,
-    pub caller: Weak<OpaqueComponent<'static>>,
-
+    pub caller: Weak<OpaqueComponent>,
+    // pub caller: Weak<OpaqueComponent<'static>>,
     pub hookidx: RefCell<usize>,
     pub hookidx: RefCell<usize>,
 
 
     // ==========================
     // ==========================
@@ -542,8 +385,9 @@ impl Scope {
     //
     //
     // Scopes cannot be made anywhere else except for this file
     // Scopes cannot be made anywhere else except for this file
     // Therefore, their lifetimes are connected exclusively to the virtual dom
     // Therefore, their lifetimes are connected exclusively to the virtual dom
-    fn new<'creator_node>(
-        caller: Weak<OpaqueComponent<'creator_node>>,
+    pub fn new<'creator_node>(
+        caller: Weak<OpaqueComponent>,
+        // caller: Weak<OpaqueComponent<'creator_node>>,
         arena_idx: ScopeIdx,
         arena_idx: ScopeIdx,
         parent: Option<ScopeIdx>,
         parent: Option<ScopeIdx>,
         height: u32,
         height: u32,
@@ -569,8 +413,10 @@ impl Scope {
         // Not the best solution, so TODO on removing this in favor of a dedicated resource abstraction.
         // Not the best solution, so TODO on removing this in favor of a dedicated resource abstraction.
         let caller = unsafe {
         let caller = unsafe {
             std::mem::transmute::<
             std::mem::transmute::<
-                Weak<OpaqueComponent<'creator_node>>,
-                Weak<OpaqueComponent<'static>>,
+                Weak<OpaqueComponent>,
+                Weak<OpaqueComponent>,
+                // Weak<OpaqueComponent<'creator_node>>,
+                // Weak<OpaqueComponent<'static>>,
             >(caller)
             >(caller)
         };
         };
 
 
@@ -591,11 +437,14 @@ impl Scope {
         }
         }
     }
     }
 
 
-    pub fn update_caller<'creator_node>(&mut self, caller: Weak<OpaqueComponent<'creator_node>>) {
+    pub fn update_caller<'creator_node>(&mut self, caller: Weak<OpaqueComponent>) {
+        // pub fn update_caller<'creator_node>(&mut self, caller: Weak<OpaqueComponent<'creator_node>>) {
         let broken_caller = unsafe {
         let broken_caller = unsafe {
             std::mem::transmute::<
             std::mem::transmute::<
-                Weak<OpaqueComponent<'creator_node>>,
-                Weak<OpaqueComponent<'static>>,
+                Weak<OpaqueComponent>,
+                Weak<OpaqueComponent>,
+                // Weak<OpaqueComponent<'creator_node>>,
+                // Weak<OpaqueComponent<'static>>,
             >(caller)
             >(caller)
         };
         };
 
 
@@ -627,8 +476,10 @@ impl Scope {
             .ok_or(Error::FatalInternal("Failed to get caller"))?;
             .ok_or(Error::FatalInternal("Failed to get caller"))?;
 
 
         // Cast the caller ptr from static to one with our own reference
         // Cast the caller ptr from static to one with our own reference
-        let c2: &OpaqueComponent<'static> = caller.as_ref();
-        let c3: &OpaqueComponent<'sel> = unsafe { std::mem::transmute(c2) };
+        let c2: &OpaqueComponent = caller.as_ref();
+        let c3: &OpaqueComponent = unsafe { std::mem::transmute(c2) };
+        // let c2: &OpaqueComponent<'static> = caller.as_ref();
+        // let c3: &OpaqueComponent<'sel> = unsafe { std::mem::transmute(c2) };
 
 
         let unsafe_head = unsafe { self.own_vnodes(c3) };
         let unsafe_head = unsafe { self.own_vnodes(c3) };
 
 
@@ -638,7 +489,7 @@ impl Scope {
     }
     }
 
 
     // this is its own function so we can preciesly control how lifetimes flow
     // this is its own function so we can preciesly control how lifetimes flow
-    unsafe fn own_vnodes<'a>(&'a self, f: &OpaqueComponent<'a>) -> VNode<'static> {
+    unsafe fn own_vnodes<'a>(&'a self, f: &OpaqueComponent) -> VNode<'static> {
         let new_head: VNode<'a> = f(self);
         let new_head: VNode<'a> = f(self);
         let out: VNode<'static> = std::mem::transmute(new_head);
         let out: VNode<'static> = std::mem::transmute(new_head);
         out
         out
@@ -956,22 +807,25 @@ Any function prefixed with "use" should not be called conditionally.
 
 
 // We actually allocate the properties for components in their parent's properties
 // We actually allocate the properties for components in their parent's properties
 // We then expose a handle to use those props for render in the form of "OpaqueComponent"
 // We then expose a handle to use those props for render in the form of "OpaqueComponent"
-pub(crate) type OpaqueComponent<'e> = dyn for<'b> Fn(&'b Scope) -> VNode<'b> + 'e;
+pub type OpaqueComponent = dyn for<'b> Fn(&'b Scope) -> VNode<'b>;
 
 
 #[derive(PartialEq, Debug, Clone, Default)]
 #[derive(PartialEq, Debug, Clone, Default)]
-pub(crate) struct EventQueue(pub Rc<RefCell<Vec<HeightMarker>>>);
+pub struct EventQueue(pub Rc<RefCell<Vec<HeightMarker>>>);
 
 
 impl EventQueue {
 impl EventQueue {
     pub fn new_channel(&self, height: u32, idx: ScopeIdx) -> Rc<dyn Fn()> {
     pub fn new_channel(&self, height: u32, idx: ScopeIdx) -> Rc<dyn Fn()> {
         let inner = self.clone();
         let inner = self.clone();
         let marker = HeightMarker { height, idx };
         let marker = HeightMarker { height, idx };
-        Rc::new(move || inner.0.as_ref().borrow_mut().push(marker))
+        Rc::new(move || {
+            log::debug!("channel updated {:#?}", marker);
+            inner.0.as_ref().borrow_mut().push(marker)
+        })
     }
     }
 }
 }
 
 
 /// A helper type that lets scopes be ordered by their height
 /// A helper type that lets scopes be ordered by their height
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub(crate) struct HeightMarker {
+pub struct HeightMarker {
     pub idx: ScopeIdx,
     pub idx: ScopeIdx,
     pub height: u32,
     pub height: u32,
 }
 }
@@ -1047,20 +901,20 @@ impl ActiveFrame {
         )
         )
     }
     }
 
 
-    fn from_frames(a: BumpFrame, b: BumpFrame) -> Self {
+    pub fn from_frames(a: BumpFrame, b: BumpFrame) -> Self {
         Self {
         Self {
             generation: 0.into(),
             generation: 0.into(),
             frames: [a, b],
             frames: [a, b],
         }
         }
     }
     }
 
 
-    fn cur_frame(&self) -> &BumpFrame {
+    pub fn cur_frame(&self) -> &BumpFrame {
         match *self.generation.borrow() & 1 == 0 {
         match *self.generation.borrow() & 1 == 0 {
             true => &self.frames[0],
             true => &self.frames[0],
             false => &self.frames[1],
             false => &self.frames[1],
         }
         }
     }
     }
-    fn cur_frame_mut(&mut self) -> &mut BumpFrame {
+    pub fn cur_frame_mut(&mut self) -> &mut BumpFrame {
         match *self.generation.borrow() & 1 == 0 {
         match *self.generation.borrow() & 1 == 0 {
             true => &mut self.frames[0],
             true => &mut self.frames[0],
             false => &mut self.frames[1],
             false => &mut self.frames[1],
@@ -1095,7 +949,7 @@ impl ActiveFrame {
         }
         }
     }
     }
 
 
-    fn next(&mut self) -> &mut BumpFrame {
+    pub fn next(&mut self) -> &mut BumpFrame {
         *self.generation.borrow_mut() += 1;
         *self.generation.borrow_mut() += 1;
 
 
         if *self.generation.borrow() % 2 == 0 {
         if *self.generation.borrow() % 2 == 0 {

+ 1 - 1
packages/recoil/src/lib.rs

@@ -368,7 +368,7 @@ mod root {
 
 
 mod hooks {
 mod hooks {
     use super::*;
     use super::*;
-    use dioxus_core::{hooks::use_ref, prelude::Context, scope::Scoped};
+    use dioxus_core::{hooks::use_ref, prelude::Context, virtual_dom::Scoped};
 
 
     pub fn use_init_recoil_root<P>(ctx: Context<P>, cfg: impl Fn(())) {
     pub fn use_init_recoil_root<P>(ctx: Context<P>, cfg: impl Fn(())) {
         ctx.use_create_context(move || RefCell::new(RecoilRoot::new()))
         ctx.use_create_context(move || RefCell::new(RecoilRoot::new()))