浏览代码

feat: update root props

Jonathan Kelley 3 年之前
父节点
当前提交
fac2e56

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

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

+ 0 - 6
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;

+ 20 - 20
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,

+ 3 - 1
packages/core/src/hooklist.rs

@@ -3,7 +3,7 @@ use std::{
     cell::{Cell, RefCell, UnsafeCell},
 };
 
-pub struct HookList {
+pub(crate) struct HookList {
     vals: RefCell<Vec<InnerHook<Box<dyn Any>>>>,
     idx: Cell<usize>,
 }
@@ -20,6 +20,7 @@ impl Default for HookList {
 struct InnerHook<T> {
     cell: UnsafeCell<T>,
 }
+
 impl<T> InnerHook<T> {
     fn new(new: T) -> Self {
         Self {
@@ -27,6 +28,7 @@ impl<T> InnerHook<T> {
         }
     }
 }
+
 impl HookList {
     pub(crate) fn next<T: 'static>(&self) -> Option<&mut T> {
         self.vals.borrow().get(self.idx.get()).and_then(|inn| {

+ 15 - 16
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<RefCell<Option<Box<dyn Any>>>>,
 }
+
 type SuspendedCallback = Box<dyn for<'a> Fn(SuspendedContext<'a>) -> DomTree<'a>>;
+
 pub struct SuspendedContext<'a> {
     pub(crate) inner: Context<'a, ()>,
 }
+
 impl<'src> SuspendedContext<'src> {
     pub fn render<F: FnOnce(NodeFactory<'src>) -> VNode<'src>>(
         self,

+ 1 - 1
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::*;

+ 4 - 7
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<Option<ElementId>>,
-    pub(crate) callback: RefCell<Option<&'a dyn FnOnce(SuspendedContext<'a>) -> DomTree<'a>>>,
+    pub(crate) callback:
+        RefCell<Option<BumpBox<'a, dyn FnMut(SuspendedContext<'a>) -> 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<P>,
         raw_props: *const (),
     ) -> Rc<dyn for<'r> 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) }
     }

+ 22 - 3
packages/core/src/scope.rs

@@ -36,8 +36,7 @@ pub struct Scope {
     // Listeners
     pub(crate) listeners: RefCell<Vec<*const Listener<'static>>>,
     pub(crate) borrowed_props: RefCell<Vec<*const VComponent<'static>>>,
-
-    pub(crate) suspended_nodes: RefCell<HashMap<u64, *const VNode<'static>>>,
+    pub(crate) suspended_nodes: RefCell<HashMap<u64, *const VSuspended<'static>>>,
 
     // 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()

+ 30 - 0
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<dyn Any>,
+
     root_prop_type: std::any::TypeId,
 
     root_props: Pin<Box<dyn std::any::Any>>,
@@ -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<Mutations<'s>> {
+        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<dyn Any>> = Box::pin(root_props);
+        let _root_prop_type = TypeId::of::<P>();
+
+        if let Some(props_ptr) = _root_props.downcast_ref::<P>().map(|p| p as *const P) {
+            std::mem::swap(&mut self.root_props, &mut _root_props);
+
+            let root = *self.root_fc.downcast_ref::<FC<P>>().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