Переглянути джерело

wip: successfully building

Jonathan Kelley 4 роки тому
батько
коміт
e3d9db0

+ 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!()
     }
 
+    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> {
         let inner = unsafe { &mut *self.0.borrow().arena.get() };
         inner

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

@@ -9,7 +9,7 @@ use crate::innerlude::FC;
 
 pub type ScopeIdx = generational_arena::Index;
 
-pub unsafe trait Properties: PartialEq {
+pub unsafe trait Properties: PartialEq + Sized {
     type Builder;
     const CAN_BE_MEMOIZED: bool;
     fn builder() -> Self::Builder;
@@ -36,7 +36,9 @@ pub fn fc_to_builder<T: Properties>(_f: FC<T>) -> T::Builder {
 }
 
 mod testing {
-    use std::any::Any;
+    use std::{any::Any, ops::Deref};
+
+    use crate::innerlude::VNode;
 
     // trait PossibleProps {
     //     type POut: PartialEq;
@@ -137,6 +139,43 @@ mod testing {
             // 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 {}

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

@@ -59,52 +59,29 @@ use std::{
 /// subscriptions and props changes.
 pub struct DiffMachine<'a> {
     pub create_diffs: bool,
+    pub cur_idx: ScopeIdx,
     pub change_list: EditMachine<'a>,
     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);
-fn get_id() -> u32 {
+fn next_id() -> u32 {
     COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
 }
 
 impl<'a> DiffMachine<'a> {
-    pub fn new(vdom: ScopeArena) -> Self {
+    pub fn new(components: ScopeArena, cur_idx: ScopeIdx, event_queue: EventQueue) -> Self {
         Self {
-            vdom,
+            components,
+            cur_idx,
+            event_queue,
             create_diffs: true,
-            lifecycle_events: VecDeque::new(),
             change_list: EditMachine::new(),
             diffed: FxHashSet::default(),
+            seen_nodes: FxHashSet::default(),
         }
     }
 
@@ -112,14 +89,14 @@ impl<'a> DiffMachine<'a> {
         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>) {
         /*
         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.
         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)) => {
                 if old_text != new_text {
                     self.change_list.commit_traversal();
@@ -129,13 +106,13 @@ impl<'a> DiffMachine<'a> {
 
             (VNode::Text(_), VNode::Element(_)) => {
                 self.change_list.commit_traversal();
-                self.create(new);
+                self.create(new_node);
                 self.change_list.replace_with();
             }
 
             (VNode::Element(_), VNode::Text(_)) => {
                 self.change_list.commit_traversal();
-                self.create(new);
+                self.create(new_node);
                 self.change_list.replace_with();
             }
 
@@ -153,57 +130,84 @@ impl<'a> DiffMachine<'a> {
             }
 
             (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 {
-                    // 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 {
-                    // 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
-            (_, VNode::Component(_new)) => {
+            (_, VNode::Component(_)) => {
                 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), _) => {
-                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) => {
                 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")
             }
-            (_, 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) => {
-                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 => {
                 todo!("Creation of VNode::Suspended not yet supported")
             }
+
+            // we go the the "known root" but only operate on a sibling basis
             VNode::Fragment(frag) => {
                 //
                 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`.
     //
     // 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.
     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() {
             self.change_list.commit_traversal();
         }
@@ -325,26 +406,15 @@ impl<'a> DiffMachine<'a> {
                             .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;
                 }
             }
 
-            // if let Some(scope) = self.current_idx {
-            // let cb = CbIdx::from_gi_index(scope, l_idx);
             self.change_list
                 .new_event_listener(event_type, new_l.scope, new_l.id);
-            // }
         }
 
         'outer2: for old_l in old {
-            // registry.remove(old_l);
-
             for new_l in new {
                 if new_l.event == old_l.event {
                     continue 'outer2;
@@ -369,6 +439,9 @@ impl<'a> DiffMachine<'a> {
     ) {
         // Do O(n^2) passes to add/update and remove attributes, since
         // 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 {
             if new_attr.is_volatile() {
                 self.change_list.commit_traversal();
@@ -386,6 +459,8 @@ impl<'a> DiffMachine<'a> {
                             );
                         }
                         continue 'outer;
+                    } else {
+                        // names are different, a varying order of attributes has arrived
                     }
                 }
 
@@ -433,12 +508,10 @@ impl<'a> DiffMachine<'a> {
                 (_, &VNode::Text(text)) => {
                     self.change_list.commit_traversal();
                     self.change_list.set_text(text);
-                    // for o in old {
-                    //     registry.remove_subtree(o);
-                    // }
                     return;
                 }
 
+                // todo: any more optimizations
                 (_, _) => {}
             }
         }
@@ -465,6 +538,7 @@ impl<'a> DiffMachine<'a> {
 
         if new_is_keyed && old_is_keyed {
             let t = self.change_list.next_temporary();
+            self.diff_keyed_children(old, new);
             self.change_list.set_next_temporary(t);
         } else {
             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 use super::nodebuilder::*;
 }
-pub mod scope;
+
 pub mod support;
 
 // 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 }
 }
 
-pub fn virtual_child<'a, T: Properties>(
+pub fn virtual_child<'a, T: Properties + 'a>(
     ctx: &NodeCtx<'a>,
     f: FC<T>,
     props: T,
@@ -658,9 +658,11 @@ pub fn virtual_child<'a, T: Properties>(
 ) -> VNode<'a> {
     // currently concerned about if props have a custom drop implementation
     // 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 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>],
 
@@ -287,18 +287,24 @@ impl<'a> VComponent<'a> {
 
         // 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 {
             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 key: NodeKey<'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 bumpalo::Bump;
 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
     #[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.
     ///
     /// 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
         // 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
             // this closure moves root props (which is static) into this closure
             let props = unsafe { &*(&root_props as *const _) };
@@ -154,10 +155,11 @@ impl VirtualDom {
         // Make the first scope
         // We don't run the component though, so renderers will need to call "rebuild" when they initialize their DOM
         let link = components.clone();
-        let event_channel = Rc::new(move || {});
+
         let base_scope = components
             .with(|arena| {
                 arena.insert_with(move |myidx| {
+                    let event_channel = _event_queue.new_channel(0, myidx);
                     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
     /// Currently this doesn't do what we want it to do
     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)?;
 
-        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)?;
 
         Ok(diff_machine.consume())
@@ -260,212 +267,48 @@ impl VirtualDom {
         diff_machine: &'_ mut DiffMachine<'s>,
     ) -> Result<()> {
         // 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
-        let mut seen_nodes = HashSet::<ScopeIdx>::new();
         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
         // This prevents us from running the same component multiple times
         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
         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
             // 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;
             }
 
             // 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
             // 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 inner: &'s mut _ = unsafe { &mut *self.components.0.borrow().arena.get() };
+            // let cur_component = inner.get_mut(update.idx).unwrap();
 
             cur_component.run_scope()?;
 
             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(())
@@ -489,7 +332,7 @@ pub struct Scope {
 
     // IDs of children that this scope has created
     // 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>],
 
@@ -507,8 +350,8 @@ pub struct Scope {
     pub event_channel: Rc<dyn Fn() + 'static>,
 
     // pub event_queue: EventQueue,
-    pub caller: Weak<OpaqueComponent<'static>>,
-
+    pub caller: Weak<OpaqueComponent>,
+    // pub caller: Weak<OpaqueComponent<'static>>,
     pub hookidx: RefCell<usize>,
 
     // ==========================
@@ -542,8 +385,9 @@ impl Scope {
     //
     // Scopes cannot be made anywhere else except for this file
     // 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,
         parent: Option<ScopeIdx>,
         height: u32,
@@ -569,8 +413,10 @@ impl Scope {
         // 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>>,
+                Weak<OpaqueComponent>,
+                Weak<OpaqueComponent>,
+                // Weak<OpaqueComponent<'creator_node>>,
+                // Weak<OpaqueComponent<'static>>,
             >(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 {
             std::mem::transmute::<
-                Weak<OpaqueComponent<'creator_node>>,
-                Weak<OpaqueComponent<'static>>,
+                Weak<OpaqueComponent>,
+                Weak<OpaqueComponent>,
+                // Weak<OpaqueComponent<'creator_node>>,
+                // Weak<OpaqueComponent<'static>>,
             >(caller)
         };
 
@@ -627,8 +476,10 @@ impl Scope {
             .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<'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) };
 
@@ -638,7 +489,7 @@ impl Scope {
     }
 
     // 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 out: VNode<'static> = std::mem::transmute(new_head);
         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 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)]
-pub(crate) struct EventQueue(pub Rc<RefCell<Vec<HeightMarker>>>);
+pub struct EventQueue(pub Rc<RefCell<Vec<HeightMarker>>>);
 
 impl EventQueue {
     pub fn new_channel(&self, height: u32, idx: ScopeIdx) -> Rc<dyn Fn()> {
         let inner = self.clone();
         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
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub(crate) struct HeightMarker {
+pub struct HeightMarker {
     pub idx: ScopeIdx,
     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 {
             generation: 0.into(),
             frames: [a, b],
         }
     }
 
-    fn cur_frame(&self) -> &BumpFrame {
+    pub fn cur_frame(&self) -> &BumpFrame {
         match *self.generation.borrow() & 1 == 0 {
             true => &self.frames[0],
             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 {
             true => &mut self.frames[0],
             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;
 
         if *self.generation.borrow() % 2 == 0 {

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

@@ -368,7 +368,7 @@ mod root {
 
 mod hooks {
     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(())) {
         ctx.use_create_context(move || RefCell::new(RecoilRoot::new()))