Bläddra i källkod

wip: fix rebuild

Jonathan Kelley 3 år sedan
förälder
incheckning
15c31d545a

+ 13 - 11
packages/core/src/diff.rs

@@ -125,11 +125,13 @@ impl<'bump> DiffState<'bump> {
 
 impl<'bump> ScopeArena {
     pub fn diff_scope(&'bump self, state: &mut DiffState<'bump>, id: &ScopeId) {
-        if let Some(component) = self.get_scope(id) {
-            let (old, new) = (component.wip_head(), component.fin_head());
-            state.stack.push(DiffInstruction::Diff { new, old });
-            self.work(state, || false);
-        }
+        // if let Some(component) = self.get_scope(id) {
+
+        let (old, new) = (self.wip_head(id), self.fin_head(id));
+
+        state.stack.push(DiffInstruction::Diff { old, new });
+        self.work(state, || false);
+        // }
     }
 
     /// Progress the diffing for this "fiber"
@@ -1146,8 +1148,8 @@ impl<'bump> ScopeArena {
                 }
                 VNode::Component(el) => {
                     let scope_id = el.associated_scope.get().unwrap();
-                    let scope = self.get_scope(&scope_id).unwrap();
-                    search_node = Some(scope.root_node());
+                    // let scope = self.get_scope(&scope_id).unwrap();
+                    search_node = Some(self.root_node(&scope_id));
                 }
             }
         }
@@ -1164,8 +1166,8 @@ impl<'bump> ScopeArena {
                 }
                 VNode::Component(el) => {
                     let scope_id = el.associated_scope.get().unwrap();
-                    let scope = self.get_scope(&scope_id).unwrap();
-                    search_node = Some(scope.root_node());
+                    // let scope = self.get_scope(&scope_id).unwrap();
+                    search_node = Some(self.root_node(&scope_id));
                 }
                 VNode::Linked(link) => {
                     todo!("linked")
@@ -1249,8 +1251,8 @@ impl<'bump> ScopeArena {
 
                 VNode::Component(c) => {
                     let scope_id = c.associated_scope.get().unwrap();
-                    let scope = self.get_scope(&scope_id).unwrap();
-                    let root = scope.root_node();
+                    // let scope = self.get_scope(&scope_id).unwrap();
+                    let root = self.root_node(&scope_id);
                     self.remove_nodes(state, Some(root), gen_muts);
 
                     log::debug!("Destroying scope {:?}", scope_id);

+ 12 - 7
packages/core/src/nodes.rs

@@ -20,7 +20,9 @@ use std::{
 ///
 /// It is used during the diffing/rendering process as a runtime key into an existing set of nodes. The "render" key
 /// is essentially a unique key to guarantee safe usage of the Node.
+#[derive(Clone, Debug)]
 pub struct NodeLink {
+    pub(crate) link_idx: usize,
     pub(crate) gen_id: u32,
     pub(crate) scope_id: ScopeId,
 }
@@ -198,6 +200,7 @@ impl<'src> VNode<'src> {
             VNode::Linked(c) => VNode::Linked(NodeLink {
                 gen_id: c.gen_id,
                 scope_id: c.scope_id,
+                link_idx: c.link_idx,
             }),
         }
     }
@@ -207,20 +210,22 @@ impl Debug for VNode<'_> {
     fn fmt(&self, s: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
         match &self {
             VNode::Element(el) => s
-                .debug_struct("VElement")
+                .debug_struct("VNode::VElement")
                 .field("name", &el.tag_name)
                 .field("key", &el.key)
                 .finish(),
 
-            VNode::Text(t) => write!(s, "VText {{ text: {} }}", t.text),
-            VNode::Anchor(_) => write!(s, "VAnchor"),
+            VNode::Text(t) => write!(s, "VNode::VText {{ text: {} }}", t.text),
+            VNode::Anchor(_) => write!(s, "VNode::VAnchor"),
 
-            VNode::Fragment(frag) => write!(s, "VFragment {{ children: {:?} }}", frag.children),
-            VNode::Suspended { .. } => write!(s, "VSuspended"),
-            VNode::Component(comp) => write!(s, "VComponent {{ fc: {:?}}}", comp.user_fc),
+            VNode::Fragment(frag) => {
+                write!(s, "VNode::VFragment {{ children: {:?} }}", frag.children)
+            }
+            VNode::Suspended { .. } => write!(s, "VNode::VSuspended"),
+            VNode::Component(comp) => write!(s, "VNode::VComponent {{ fc: {:?}}}", comp.user_fc),
             VNode::Linked(c) => write!(
                 s,
-                "VCached {{ gen_id: {}, scope_id: {:?} }}",
+                "VNode::VCached {{ gen_id: {}, scope_id: {:?} }}",
                 c.gen_id, c.scope_id
             ),
         }

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

@@ -59,12 +59,16 @@ pub struct Scope {
 
     pub(crate) is_subtree_root: Cell<bool>,
 
+    pub(crate) generation: Cell<u32>,
+
     // The double-buffering situation that we will use
-    pub(crate) frames: [Bump; 2],
+    pub(crate) frames: [BumpFrame; 2],
 
     pub(crate) old_root: RefCell<Option<NodeLink>>,
     pub(crate) new_root: RefCell<Option<NodeLink>>,
 
+    pub(crate) caller: *mut dyn Fn(&Scope) -> Element,
+
     /*
     we care about:
     - listeners (and how to call them when an event is triggered)
@@ -83,14 +87,6 @@ pub struct Scope {
 }
 
 pub struct SelfReferentialItems<'a> {
-    // nodes stored by "cx.render"
-    pub(crate) cached_nodes_old: Vec<VNode<'a>>,
-    pub(crate) cached_nodes_new: Vec<VNode<'a>>,
-
-    pub(crate) caller: &'a dyn Fn(&Scope) -> Element,
-
-    pub(crate) generation: Cell<u32>,
-
     pub(crate) listeners: Vec<&'a Listener<'a>>,
     pub(crate) borrowed_props: Vec<&'a VComponent<'a>>,
     pub(crate) suspended_nodes: FxHashMap<u64, &'a VSuspended<'a>>,
@@ -100,25 +96,6 @@ pub struct SelfReferentialItems<'a> {
 
 // Public methods exposed to libraries and components
 impl Scope {
-    /// Get the root VNode for this Scope.
-    ///
-    /// This VNode is the "entrypoint" VNode. If the component renders multiple nodes, then this VNode will be a fragment.
-    ///
-    /// # Example
-    /// ```rust
-    /// let mut dom = VirtualDom::new(|(cx, props)|cx.render(rsx!{ div {} }));
-    /// dom.rebuild();
-    ///
-    /// let base = dom.base_scope();
-    ///
-    /// if let VNode::VElement(node) = base.root_node() {
-    ///     assert_eq!(node.tag_name, "div");
-    /// }
-    /// ```
-    pub fn root_node(&self) -> &VNode {
-        self.fin_head()
-    }
-
     /// Get the subtree ID that this scope belongs to.
     ///
     /// Each component has its own subtree ID - the root subtree has an ID of 0. This ID is used by the renderer to route
@@ -239,7 +216,7 @@ impl Scope {
     ///
     /// `ScopeId` is not unique for the lifetime of the VirtualDom - a ScopeId will be reused if a component is unmounted.
     pub fn bump(&self) -> &Bump {
-        &self.wip_frame()
+        &self.wip_frame().bump
     }
 
     /// Take a lazy VNode structure and actually build it with the context of the VDom's efficient VNode allocator.
@@ -258,18 +235,18 @@ impl Scope {
     /// }
     ///```
     pub fn render<'src>(&'src self, lazy_nodes: Option<LazyNodes<'src, '_>>) -> Option<NodeLink> {
-        let bump = &self.wip_frame();
+        let bump = &self.wip_frame().bump;
         let factory = NodeFactory { bump };
         let node = lazy_nodes.map(|f| f.call(factory))?;
 
-        self.items
-            .borrow_mut()
-            .cached_nodes_old
-            .push(unsafe { std::mem::transmute(node) });
+        let idx = self
+            .wip_frame()
+            .add_node(unsafe { std::mem::transmute(node) });
 
         Some(NodeLink {
-            gen_id: self.items.borrow().generation.get(),
+            gen_id: self.generation.get(),
             scope_id: self.our_arena_idx,
+            link_idx: idx,
         })
     }
 
@@ -427,11 +404,7 @@ impl Scope {
             self.hooks.push_hook(initializer(self.hooks.len()));
         }
 
-        runner(self.hooks.next::<State>().expect(HOOK_ERR_MSG))
-    }
-}
-
-const HOOK_ERR_MSG: &str = r###"
+        const HOOK_ERR_MSG: &str = r###"
 Unable to retrieve the hook that was initialized at this index.
 Consult the `rules of hooks` to understand how to use hooks properly.
 
@@ -439,29 +412,24 @@ You likely used the hook in a conditional. Hooks rely on consistent ordering bet
 Functions prefixed with "use" should never be called conditionally.
 "###;
 
-// Important internal methods
-impl Scope {
-    /// Give out our self-referential item with our own borrowed lifetime
-    pub(crate) fn fin_head<'b>(&'b self) -> &'b VNode<'b> {
-        todo!()
-        // let cur_head = &self.finished_frame().head_node;
-        // unsafe { std::mem::transmute::<&VNode<'static>, &VNode<'b>>(cur_head) }
-    }
-
-    /// Give out our self-referential item with our own borrowed lifetime
-    pub(crate) fn wip_head<'b>(&'b self) -> &'b VNode<'b> {
-        todo!()
-        // let cur_head = &self.wip_frame().head_node;
-        // unsafe { std::mem::transmute::<&VNode<'static>, &VNode<'b>>(cur_head) }
+        runner(self.hooks.next::<State>().expect(HOOK_ERR_MSG))
     }
+}
 
+// Important internal methods
+impl Scope {
     /// The "work in progress frame" represents the frame that is currently being worked on.
-    pub(crate) fn wip_frame(&self) -> &Bump {
-        todo!()
-        // match self.cur_generation.get() & 1 == 0 {
-        //     true => &self.frames[0],
-        //     false => &self.frames[1],
-        // }
+    pub(crate) fn wip_frame(&self) -> &BumpFrame {
+        match self.generation.get() & 1 == 0 {
+            true => &self.frames[0],
+            false => &self.frames[1],
+        }
+    }
+    pub(crate) fn fin_frame(&self) -> &BumpFrame {
+        match self.generation.get() & 1 == 1 {
+            true => &self.frames[0],
+            false => &self.frames[1],
+        }
     }
 
     pub unsafe fn reset_wip_frame(&self) {
@@ -469,8 +437,10 @@ impl Scope {
         let bump = self.wip_frame() as *const _ as *mut Bump;
         let g = &mut *bump;
         g.reset();
+    }
 
-        // self.wip_frame_mut().bump.reset()
+    pub fn cycle_frame(&self) {
+        self.generation.set(self.generation.get() + 1);
     }
 
     /// A safe wrapper around calling listeners
@@ -528,3 +498,26 @@ impl Scope {
         // }
     }
 }
+
+pub struct BumpFrame {
+    pub bump: Bump,
+    pub nodes: RefCell<Vec<VNode<'static>>>,
+}
+impl BumpFrame {
+    pub fn new() -> Self {
+        let bump = Bump::new();
+
+        let node = &*bump.alloc(VText {
+            text: "asd",
+            dom_id: Default::default(),
+            is_static: false,
+        });
+        let nodes = RefCell::new(vec![VNode::Text(unsafe { std::mem::transmute(node) })]);
+        Self { bump, nodes }
+    }
+    fn add_node(&self, node: VNode<'static>) -> usize {
+        let mut nodes = self.nodes.borrow_mut();
+        nodes.push(node);
+        nodes.len() - 1
+    }
+}

+ 109 - 33
packages/core/src/scopearena.rs

@@ -1,5 +1,8 @@
 use slab::Slab;
-use std::cell::{Cell, RefCell};
+use std::{
+    borrow::BorrowMut,
+    cell::{Cell, RefCell},
+};
 
 use bumpalo::{boxed::Box as BumpBox, Bump};
 use futures_channel::mpsc::UnboundedSender;
@@ -22,6 +25,7 @@ pub(crate) struct ScopeArena {
     bump: Bump,
     scopes: Vec<*mut Scope>,
     free_scopes: Vec<ScopeId>,
+    nodes: RefCell<Slab<*const VNode<'static>>>,
     pub(crate) sender: UnboundedSender<SchedulerMsg>,
 }
 
@@ -31,7 +35,7 @@ impl ScopeArena {
             bump: Bump::new(),
             scopes: Vec::new(),
             free_scopes: Vec::new(),
-
+            nodes: RefCell::new(Slab::new()),
             sender,
         }
     }
@@ -55,41 +59,49 @@ impl ScopeArena {
             todo!("override the scope contents");
             id
         } else {
-            let id = ScopeId(self.scopes.len());
+            let scope_id = ScopeId(self.scopes.len());
 
-            // cast off the lifetime
-            let caller = unsafe { std::mem::transmute(caller) };
+            let old_root = NodeLink {
+                link_idx: 0,
+                gen_id: 0,
+                scope_id,
+            };
+            let new_root = NodeLink {
+                link_idx: 0,
+                gen_id: 0,
+                scope_id,
+            };
 
             let new_scope = Scope {
                 sender: self.sender.clone(),
                 parent_scope,
-                our_arena_idx: id,
+                our_arena_idx: scope_id,
                 height,
                 subtree: Cell::new(subtree),
                 is_subtree_root: Cell::new(false),
-                frames: [Bump::default(), Bump::default()],
+                frames: [BumpFrame::new(), BumpFrame::new()],
 
                 hooks: Default::default(),
                 shared_contexts: Default::default(),
+                caller,
+                generation: 0.into(),
 
+                old_root: RefCell::new(Some(old_root)),
+                new_root: RefCell::new(Some(new_root)),
+                // old_root: RefCell::new(Some(old_root)),
+                // new_root: RefCell::new(None),
                 items: RefCell::new(SelfReferentialItems {
                     listeners: Default::default(),
                     borrowed_props: Default::default(),
                     suspended_nodes: Default::default(),
                     tasks: Default::default(),
                     pending_effects: Default::default(),
-                    cached_nodes_old: Default::default(),
-                    generation: Default::default(),
-                    cached_nodes_new: Default::default(),
-                    caller,
                 }),
-                old_root: todo!(),
-                new_root: todo!(),
             };
 
             let stable = self.bump.alloc(new_scope);
             self.scopes.push(stable);
-            id
+            scope_id
         }
     }
 
@@ -98,8 +110,20 @@ impl ScopeArena {
     }
 
     pub fn reserve_node(&self, node: &VNode) -> ElementId {
-        todo!()
-        // self.node_reservations.insert(id);
+        let mut els = self.nodes.borrow_mut();
+        let entry = els.vacant_entry();
+        let key = entry.key();
+        let id = ElementId(key);
+        let node = node as *const _;
+        let node = unsafe { std::mem::transmute(node) };
+        entry.insert(node);
+        id
+
+        // let nodes = self.nodes.borrow_mut();
+        // let id = nodes.insert(());
+        // let node_id = ElementId(id);
+        // node = Some(node_id);
+        // node_id
     }
 
     pub fn collect_garbage(&self, id: ElementId) {
@@ -177,38 +201,90 @@ impl ScopeArena {
         // - We've dropped all references to the wip bump frame with "ensure_drop_safety"
         unsafe { scope.reset_wip_frame() };
 
-        let mut items = scope.items.borrow_mut();
+        {
+            let mut items = scope.items.borrow_mut();
 
-        // just forget about our suspended nodes while we're at it
-        items.suspended_nodes.clear();
+            // just forget about our suspended nodes while we're at it
+            items.suspended_nodes.clear();
 
-        // guarantee that we haven't screwed up - there should be no latent references anywhere
-        debug_assert!(items.listeners.is_empty());
-        debug_assert!(items.suspended_nodes.is_empty());
-        debug_assert!(items.borrowed_props.is_empty());
+            // guarantee that we haven't screwed up - there should be no latent references anywhere
+            debug_assert!(items.listeners.is_empty());
+            debug_assert!(items.suspended_nodes.is_empty());
+            debug_assert!(items.borrowed_props.is_empty());
 
-        log::debug!("Borrowed stuff is successfully cleared");
+            log::debug!("Borrowed stuff is successfully cleared");
 
-        // temporarily cast the vcomponent to the right lifetime
-        // let vcomp = scope.load_vcomp();
+            // temporarily cast the vcomponent to the right lifetime
+            // let vcomp = scope.load_vcomp();
+        }
 
-        let render: &dyn Fn(&Scope) -> Element = todo!();
+        let render: &dyn Fn(&Scope) -> Element = unsafe { &*scope.caller };
 
         // Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders.
+        scope.wip_frame().nodes.borrow_mut().clear();
         if let Some(key) = render(scope) {
-            // todo!("attach the niode");
-            // let new_head = builder.into_vnode(NodeFactory {
-            //     bump: &scope.frames.wip_frame().bump,
-            // });
-            // log::debug!("Render is successful");
+            dbg!(key);
+
+            dbg!(&scope.wip_frame().nodes.borrow_mut());
+            // let mut old = scope.old_root.borrow_mut();
+            // let mut new = scope.new_root.borrow_mut();
+
+            // let new_old = new.clone();
+            // *old = new_old;
+            // *new = Some(key);
+
+            // dbg!(&old);
+            // dbg!(&new);
 
             // the user's component succeeded. We can safely cycle to the next frame
             // scope.frames.wip_frame_mut().head_node = unsafe { std::mem::transmute(new_head) };
-            // scope.frames.cycle_frame();
+            scope.cycle_frame();
 
             true
         } else {
             false
         }
     }
+
+    pub fn wip_head(&self, id: &ScopeId) -> &VNode {
+        let scope = self.get_scope(id).unwrap();
+        let wip_frame = scope.wip_frame();
+        let nodes = wip_frame.nodes.borrow();
+        let node = nodes.get(0).unwrap();
+        unsafe { std::mem::transmute(node) }
+        // let root = scope.old_root.borrow();
+        // let link = root.as_ref().unwrap();
+        // dbg!(link);
+
+        // // for now, components cannot pass portals through each other
+        // assert_eq!(link.scope_id, *id);
+        // // assert_eq!(link.gen_id, scope.generation.get() - 1);
+
+        // // let items = scope.items.borrow();
+        // let nodes = scope.wip_frame().nodes.borrow();
+        // let node = nodes.get(link.link_idx).unwrap();
+        // unsafe { std::mem::transmute(node) }
+    }
+
+    pub fn fin_head(&self, id: &ScopeId) -> &VNode {
+        let scope = self.get_scope(id).unwrap();
+        let wip_frame = scope.fin_frame();
+        let nodes = wip_frame.nodes.borrow();
+        let node = nodes.get(0).unwrap();
+        unsafe { std::mem::transmute(node) }
+        // let scope = self.get_scope(id).unwrap();
+        // let root = scope.new_root.borrow();
+        // let link = root.as_ref().unwrap();
+
+        // // for now, components cannot pass portals through each other
+        // assert_eq!(link.scope_id, *id);
+        // // assert_eq!(link.gen_id, scope.generation.get());
+
+        // let nodes = scope.fin_frame().nodes.borrow();
+        // let node = nodes.get(link.link_idx).unwrap();
+        // unsafe { std::mem::transmute(node) }
+    }
+    pub fn root_node(&self, id: &ScopeId) -> &VNode {
+        self.wip_head(id)
+    }
 }

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

@@ -384,8 +384,10 @@ impl VirtualDom {
                     log::debug!("about to run scope {:?}", scopeid);
 
                     if self.scopes.run_scope(&scopeid) {
-                        let scope = self.scopes.get_scope(&scopeid).unwrap();
-                        let (old, new) = (scope.wip_head(), scope.fin_head());
+                        let (old, new) = (
+                            self.scopes.wip_head(&scopeid),
+                            self.scopes.fin_head(&scopeid),
+                        );
                         diff_state.stack.scope_stack.push(scopeid);
                         diff_state.stack.push(DiffInstruction::Diff { new, old });
                     }
@@ -438,13 +440,20 @@ impl VirtualDom {
     /// apply_edits(edits);
     /// ```
     pub fn rebuild(&mut self) -> Mutations {
-        // todo: I think we need to append a node or something
-        //     diff_machine
-        //         .stack
-        //         .create_node(cur_component.frames.fin_head(), MountType::Append);
+        let mut diff_machine = DiffState::new(Mutations::new());
 
-        let scope = self.base_scope;
-        self.hard_diff(&scope).unwrap()
+        let scope_id = self.base_scope;
+        if self.scopes.run_scope(&scope_id) {
+            diff_machine
+                .stack
+                .create_node(self.scopes.fin_head(&scope_id), MountType::Append);
+
+            diff_machine.stack.scope_stack.push(scope_id);
+
+            self.scopes.work(&mut diff_machine, || false);
+        }
+
+        diff_machine.mutations
     }
 
     /// Compute a manual diff of the VirtualDOM between states.
@@ -491,6 +500,8 @@ impl VirtualDom {
 
             self.scopes.diff_scope(&mut diff_machine, scope_id);
 
+            dbg!(&diff_machine.mutations);
+
             Some(diff_machine.mutations)
         } else {
             None