From fac2e56ed63fc708a89e14df15f1fc32f58391a9 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 1 Sep 2021 15:45:53 -0400 Subject: [PATCH] feat: update root props --- packages/core/.vscode/settings.json | 2 +- packages/core/src/bumpframe.rs | 6 ----- packages/core/src/events.rs | 40 ++++++++++++++--------------- packages/core/src/hooklist.rs | 4 ++- packages/core/src/hooks.rs | 31 +++++++++++----------- packages/core/src/lib.rs | 2 +- packages/core/src/nodes.rs | 11 +++----- packages/core/src/scope.rs | 25 +++++++++++++++--- packages/core/src/virtual_dom.rs | 30 ++++++++++++++++++++++ 9 files changed, 96 insertions(+), 55 deletions(-) diff --git a/packages/core/.vscode/settings.json b/packages/core/.vscode/settings.json index 79505b018..1c68f97cc 100644 --- a/packages/core/.vscode/settings.json +++ b/packages/core/.vscode/settings.json @@ -1,3 +1,3 @@ { - "rust-analyzer.inlayHints.enable": false + "rust-analyzer.inlayHints.enable": true } diff --git a/packages/core/src/bumpframe.rs b/packages/core/src/bumpframe.rs index 8696d4e8e..b2b54107e 100644 --- a/packages/core/src/bumpframe.rs +++ b/packages/core/src/bumpframe.rs @@ -67,12 +67,6 @@ impl ActiveFrame { } } - pub fn finished_frame_mut(&mut self) -> &mut BumpFrame { - match self.generation.get() & 1 == 1 { - true => &mut self.frames[0], - false => &mut self.frames[1], - } - } /// Give out our self-referential item with our own borrowed lifetime pub fn fin_head<'b>(&'b self) -> &'b VNode<'b> { let cur_head = &self.finished_frame().head_node; diff --git a/packages/core/src/events.rs b/packages/core/src/events.rs index 7360d87e4..952c3b2e3 100644 --- a/packages/core/src/events.rs +++ b/packages/core/src/events.rs @@ -633,16 +633,16 @@ pub mod on { DownArrow = 40, Insert = 45, Delete = 46, - _0 = 48, - _1 = 49, - _2 = 50, - _3 = 51, - _4 = 52, - _5 = 53, - _6 = 54, - _7 = 55, - _8 = 56, - _9 = 57, + Num0 = 48, + Num1 = 49, + Num2 = 50, + Num3 = 51, + Num4 = 52, + Num5 = 53, + Num6 = 54, + Num7 = 55, + Num8 = 56, + Num9 = 57, A = 65, B = 66, C = 67, @@ -738,16 +738,16 @@ pub mod on { 40 => DownArrow, 45 => Insert, 46 => Delete, - 48 => _0, - 49 => _1, - 50 => _2, - 51 => _3, - 52 => _4, - 53 => _5, - 54 => _6, - 55 => _7, - 56 => _8, - 57 => _9, + 48 => Num0, + 49 => Num1, + 50 => Num2, + 51 => Num3, + 52 => Num4, + 53 => Num5, + 54 => Num6, + 55 => Num7, + 56 => Num8, + 57 => Num9, 65 => A, 66 => B, 67 => C, diff --git a/packages/core/src/hooklist.rs b/packages/core/src/hooklist.rs index 3781fc2ed..ebfe5723a 100644 --- a/packages/core/src/hooklist.rs +++ b/packages/core/src/hooklist.rs @@ -3,7 +3,7 @@ use std::{ cell::{Cell, RefCell, UnsafeCell}, }; -pub struct HookList { +pub(crate) struct HookList { vals: RefCell>>>, idx: Cell, } @@ -20,6 +20,7 @@ impl Default for HookList { struct InnerHook { cell: UnsafeCell, } + impl InnerHook { fn new(new: T) -> Self { Self { @@ -27,6 +28,7 @@ impl InnerHook { } } } + impl HookList { pub(crate) fn next(&self) -> Option<&mut T> { self.vals.borrow().get(self.idx.get()).and_then(|inn| { diff --git a/packages/core/src/hooks.rs b/packages/core/src/hooks.rs index 5bddaca12..47e7fcda9 100644 --- a/packages/core/src/hooks.rs +++ b/packages/core/src/hooks.rs @@ -3,11 +3,11 @@ //! This module contains all the low-level built-in hooks that require 1st party support to work. //! //! Hooks: -//! - use_hook -//! - use_state_provider -//! - use_state_consumer -//! - use_task -//! - use_suspense +//! - [`use_hook`] +//! - [`use_state_provider`] +//! - [`use_state_consumer`] +//! - [`use_task`] +//! - [`use_suspense`] use crate::innerlude::*; use futures_util::FutureExt; @@ -53,15 +53,7 @@ where (true, true) => {} // Needs to be initialized - (false, false) => { - log::debug!("Initializing context..."); - cx.add_shared_state(init()); - log::info!( - "There are now {} shared contexts for scope {:?}", - cx.scope.shared_contexts.borrow().len(), - cx.scope.our_arena_idx, - ); - } + (false, false) => cx.add_shared_state(init()), _ => debug_assert!(false, "Cannot initialize two contexts of the same type"), }; @@ -205,7 +197,10 @@ where cx.render(LazyNodes::new(|f| { let bump = f.bump(); - let g: &dyn FnOnce(SuspendedContext<'src>) -> DomTree<'src> = + + use bumpalo::boxed::Box as BumpBox; + + let f: &mut dyn FnMut(SuspendedContext<'src>) -> DomTree<'src> = bump.alloc(move |sus| { let val = value.borrow(); @@ -218,11 +213,12 @@ where user_callback(sus, out) }); + let callback = unsafe { BumpBox::from_raw(f) }; VNode::Suspended(bump.alloc(VSuspended { dom_id: empty_cell(), task_id: hook.handle.our_id, - callback: RefCell::new(Some(g)), + callback: RefCell::new(Some(callback)), })) })) } @@ -235,10 +231,13 @@ pub(crate) struct SuspenseHook { pub handle: TaskHandle, pub value: Rc>>>, } + type SuspendedCallback = Box Fn(SuspendedContext<'a>) -> DomTree<'a>>; + pub struct SuspendedContext<'a> { pub(crate) inner: Context<'a, ()>, } + impl<'src> SuspendedContext<'src> { pub fn render) -> VNode<'src>>( self, diff --git a/packages/core/src/lib.rs b/packages/core/src/lib.rs index 077a282e3..f01838e23 100644 --- a/packages/core/src/lib.rs +++ b/packages/core/src/lib.rs @@ -38,7 +38,7 @@ pub(crate) mod innerlude { pub use crate::diff_stack::*; pub use crate::events::*; pub use crate::heuristics::*; - pub use crate::hooklist::*; + pub(crate) use crate::hooklist::*; pub use crate::hooks::*; pub use crate::mutations::*; pub use crate::nodes::*; diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index 77d4112a1..58f259c72 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -283,7 +283,8 @@ pub struct VComponent<'src> { pub struct VSuspended<'a> { pub task_id: u64, pub dom_id: Cell>, - pub(crate) callback: RefCell) -> DomTree<'a>>>, + pub(crate) callback: + RefCell) -> DomTree<'a>>>>, } /// This struct provides an ergonomic API to quickly build VNodes. @@ -518,7 +519,7 @@ impl<'a> NodeFactory<'a> { })) } - pub fn create_component_caller<'g, P: 'g>( + pub(crate) fn create_component_caller<'g, P: 'g>( component: FC

, raw_props: *const (), ) -> Rc Fn(&'r Scope) -> DomTree<'r>> { @@ -530,12 +531,8 @@ impl<'a> NodeFactory<'a> { props: safe_props, scope: scp, }; - let res = component(cx); - - let g2 = unsafe { std::mem::transmute(res) }; - - g2 + unsafe { std::mem::transmute(res) } }); unsafe { std::mem::transmute::<_, Captured<'static>>(caller) } } diff --git a/packages/core/src/scope.rs b/packages/core/src/scope.rs index d854690a2..9b154f097 100644 --- a/packages/core/src/scope.rs +++ b/packages/core/src/scope.rs @@ -36,8 +36,7 @@ pub struct Scope { // Listeners pub(crate) listeners: RefCell>>, pub(crate) borrowed_props: RefCell>>, - - pub(crate) suspended_nodes: RefCell>>, + pub(crate) suspended_nodes: RefCell>>, // State pub(crate) hooks: HookList, @@ -146,7 +145,7 @@ impl Scope { /// /// Refrences to hook data can only be stored in listeners and component props. During diffing, we make sure to log /// all listeners and borrowed props so we can clear them here. - fn ensure_drop_safety(&mut self, pool: &ResourcePool) { + pub(crate) fn ensure_drop_safety(&mut self, pool: &ResourcePool) { // make sure all garabge is collected before trying to proceed with anything else debug_assert!( self.pending_garbage.borrow().is_empty(), @@ -220,6 +219,26 @@ impl Scope { unsafe { self.child_nodes.shorten_lifetime() } } + pub fn call_suspended_node<'a>(&'a self, task: u64) { + let g = self.suspended_nodes.borrow_mut(); + + if let Some(suspended) = g.get(&task) { + let sus: &'a VSuspended<'static> = unsafe { &**suspended }; + let sus: &'a VSuspended<'a> = unsafe { std::mem::transmute(sus) }; + + let bump = self.frames.wip_frame(); + let mut cb = sus.callback.borrow_mut(); + let mut _cb = cb.take().unwrap(); + let cx: SuspendedContext<'a> = SuspendedContext { + inner: Context { + props: &(), + scope: &self, + }, + }; + let n: DomTree<'a> = (_cb)(cx); + } + } + pub fn consume_garbage(&self) -> Vec<&VNode> { self.pending_garbage .borrow_mut() diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index 3b30f37e4..7b50f2fe5 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -23,6 +23,7 @@ use futures_util::{Future, FutureExt}; use std::{ any::{Any, TypeId}, pin::Pin, + rc::Rc, }; /// An integrated virtual node system that progresses events and diffs UI trees. @@ -39,6 +40,8 @@ pub struct VirtualDom { base_scope: ScopeId, + root_fc: Box, + root_prop_type: std::any::TypeId, root_props: Pin>, @@ -120,6 +123,7 @@ impl VirtualDom { }); Self { + root_fc: Box::new(root), base_scope, scheduler, root_props: _root_props, @@ -135,6 +139,32 @@ impl VirtualDom { self.scheduler.pool.get_scope(id) } + /// Update the root props of this VirtualDOM. + /// + /// This method retuns None if the old props could not be removed. The entire VirtualDOM will be rebuilt immediately, + /// so calling this method will block the main thread until computation is done. + pub fn update_root_props<'s, P: 'static>(&'s mut self, root_props: P) -> Option> { + let root_scope = self.scheduler.pool.get_scope_mut(self.base_scope).unwrap(); + root_scope.ensure_drop_safety(&self.scheduler.pool); + + let mut _root_props: Pin> = Box::pin(root_props); + let _root_prop_type = TypeId::of::

(); + + if let Some(props_ptr) = _root_props.downcast_ref::

().map(|p| p as *const P) { + std::mem::swap(&mut self.root_props, &mut _root_props); + + let root = *self.root_fc.downcast_ref::>().unwrap(); + + let new_caller = NodeFactory::create_component_caller(root, props_ptr as *const _); + + root_scope.update_scope_dependencies(new_caller, ScopeChildren(&[])); + + Some(self.rebuild()) + } else { + None + } + } + /// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom rom scratch /// /// The diff machine expects the RealDom's stack to be the root of the application