From 15c31d545a84f05fe17254009dc275aac93e7574 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sun, 7 Nov 2021 22:36:57 -0500 Subject: [PATCH] wip: fix rebuild --- packages/core/src/diff.rs | 24 +++--- packages/core/src/nodes.rs | 19 +++-- packages/core/src/scope.rs | 115 ++++++++++++------------- packages/core/src/scopearena.rs | 142 ++++++++++++++++++++++++------- packages/core/src/virtual_dom.rs | 27 ++++-- 5 files changed, 207 insertions(+), 120 deletions(-) diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index 2fd66d014..4f923af24 100644 --- a/packages/core/src/diff.rs +++ b/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); diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index 0e245ff81..9a240f63b 100644 --- a/packages/core/src/nodes.rs +++ b/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 ), } diff --git a/packages/core/src/scope.rs b/packages/core/src/scope.rs index 3a826b8c7..789946016 100644 --- a/packages/core/src/scope.rs +++ b/packages/core/src/scope.rs @@ -59,12 +59,16 @@ pub struct Scope { pub(crate) is_subtree_root: Cell, + pub(crate) generation: Cell, + // The double-buffering situation that we will use - pub(crate) frames: [Bump; 2], + pub(crate) frames: [BumpFrame; 2], pub(crate) old_root: RefCell>, pub(crate) new_root: RefCell>, + 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>, - pub(crate) cached_nodes_new: Vec>, - - pub(crate) caller: &'a dyn Fn(&Scope) -> Element, - - pub(crate) generation: Cell, - pub(crate) listeners: Vec<&'a Listener<'a>>, pub(crate) borrowed_props: Vec<&'a VComponent<'a>>, pub(crate) suspended_nodes: FxHashMap>, @@ -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>) -> Option { - 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::().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. "###; + runner(self.hooks.next::().expect(HOOK_ERR_MSG)) + } +} + // 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) } - } - /// 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>>, +} +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 + } +} diff --git a/packages/core/src/scopearena.rs b/packages/core/src/scopearena.rs index a19600504..8f9050159 100644 --- a/packages/core/src/scopearena.rs +++ b/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, + nodes: RefCell>>, pub(crate) sender: UnboundedSender, } @@ -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) + } } diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index 9b164d128..6eb620d9e 100644 --- a/packages/core/src/virtual_dom.rs +++ b/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