From fa106be1f5a45fa5707e66542e52c9f09e8cea7a Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Tue, 21 Dec 2021 00:58:14 -0500 Subject: [PATCH] wip: clean it up a bit --- packages/core/README.md | 39 ++++++++++++++++++++++++++++++++ packages/core/src/diff.rs | 2 -- packages/core/src/scopes.rs | 23 +++++++------------ packages/core/src/virtual_dom.rs | 25 ++++++++------------ packages/desktop/src/lib.rs | 2 +- packages/web/src/dom.rs | 2 +- 6 files changed, 59 insertions(+), 34 deletions(-) diff --git a/packages/core/README.md b/packages/core/README.md index 71ee252cb..ff60c905a 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -4,6 +4,45 @@ This is the core crate for the Dioxus Virtual DOM. This README will focus on the To build new apps with Dioxus or to extend the ecosystem with new hooks or components, use the higher-level `dioxus` crate with the appropriate feature flags. + +```rust +fn app(cx: Scope<()>) -> Element { + cx.render(rsx!( + div { "hello world" } + )) +} + + +fn main() { + let mut renderer = SomeRenderer::new(); + + + // Creating a new virtualdom from a component + let mut dom = VirtualDom::new(app); + + // Patching the renderer with the changes to draw the screen + let edits = dom.rebuild(); + renderer.apply(edits); + + // Injecting events + dom.handle_message(SchedulerMsg::UserEvent()); + +} + + + + + + + + + + + + + + +``` ## Internals Dioxus-core builds off the many frameworks that came before it. Notably, Dioxus borrows these concepts: diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index 2480652dd..2de598796 100644 --- a/packages/core/src/diff.rs +++ b/packages/core/src/diff.rs @@ -1295,8 +1295,6 @@ impl<'bump> DiffState<'bump> { let scope_id = c.scope.get().unwrap(); let root = self.scopes.root_node(scope_id); self.remove_nodes(Some(root), gen_muts); - - log::debug!("Destroying scope {:?}", scope_id); self.scopes.try_remove(scope_id).unwrap(); } } diff --git a/packages/core/src/scopes.rs b/packages/core/src/scopes.rs index c11f3a996..0b024f011 100644 --- a/packages/core/src/scopes.rs +++ b/packages/core/src/scopes.rs @@ -123,13 +123,6 @@ impl ScopeArena { debug_assert!(any_item.is_none()); } else { // else create a new scope - let (node_capacity, hook_capacity) = self - .heuristics - .borrow() - .get(&fc_ptr) - .map(|h| (h.node_arena_size, h.hook_arena_size)) - .unwrap_or_default(); - self.scopes.borrow_mut().insert( new_scope_id, self.bump.alloc(ScopeState::new( @@ -139,8 +132,11 @@ impl ScopeArena { self.sender.clone(), parent_scope, vcomp, - node_capacity, - hook_capacity, + self.heuristics + .borrow() + .get(&fc_ptr) + .map(|h| (h.node_arena_size, h.hook_arena_size)) + .unwrap_or_default(), )), ); } @@ -437,19 +433,16 @@ impl ScopeState { sender: UnboundedSender, parent_scope: Option<*mut ScopeState>, vcomp: Box, - node_capacity: usize, - hook_capacity: usize, + (node_capacity, hook_capacity): (usize, usize), ) -> Self { - let frames = [BumpFrame::new(node_capacity), BumpFrame::new(node_capacity)]; - ScopeState { sender, container, our_arena_idx, parent_scope, - props: RefCell::new(Some(vcomp)), height, - frames, + props: RefCell::new(Some(vcomp)), + frames: [BumpFrame::new(node_capacity), BumpFrame::new(node_capacity)], // todo: subtrees subtree: Cell::new(0), diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index 40205d9ca..d652ba326 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -345,11 +345,10 @@ impl VirtualDom { pub fn process_message(&mut self, msg: SchedulerMsg) { match msg { - // just keep looping, the task is now saved but we should actually poll it SchedulerMsg::NewTask(id) => { self.scopes.pending_futures.borrow_mut().insert(id); } - SchedulerMsg::UiEvent(event) => { + SchedulerMsg::Event(event) => { if let Some(element) = event.element { self.scopes.call_listener_with_bubbling(event, element); } @@ -640,6 +639,7 @@ impl VirtualDom { (create.mutations, edit.mutations) } } + /* Scopes and ScopeArenas are never dropped internally. An app will always occupy as much memory as its biggest form. @@ -662,9 +662,6 @@ should we build a vcomponent for the root? - 1: Use remove_nodes to use the ensure_drop_safety pathway to safely drop the tree - 2: Drop the ScopeState itself - - - */ impl Drop for VirtualDom { fn drop(&mut self) { @@ -684,27 +681,26 @@ impl Drop for VirtualDom { // make sure there are no "live" components for (_, scopeptr) in self.scopes.scopes.get_mut().drain() { + // safety: all scopes were made in the bump's allocator + // They are never dropped until now. The only way to drop is through Box. let scope = unsafe { bumpalo::boxed::Box::from_raw(scopeptr) }; drop(scope); } for scopeptr in self.scopes.free_scopes.get_mut().drain(..) { + // safety: all scopes were made in the bump's allocator + // They are never dropped until now. The only way to drop is through Box. let mut scope = unsafe { bumpalo::boxed::Box::from_raw(scopeptr) }; scope.reset(); drop(scope); } - - // Safety: all refereneces to this caller have been dropped - // the pointer is still valid since we haven't moved it since the virtualdom was created - // ensure the root component's caller is dropped - // drop(unsafe { Box::from_raw(self.caller) }); } } #[derive(Debug)] pub enum SchedulerMsg { // events from the host - UiEvent(UserEvent), + Event(UserEvent), // setstate Immediate(ScopeId), @@ -746,20 +742,19 @@ pub enum SchedulerMsg { /// ``` #[derive(Debug)] pub struct UserEvent { - /// The originator of the event trigger + /// The originator of the event trigger if available pub scope_id: Option, + /// The priority of the event to be scheduled around ongoing work pub priority: EventPriority, /// The optional real node associated with the trigger pub element: Option, /// The event type IE "onclick" or "onmouseover" - /// - /// The name that the renderer will use to mount the listener. pub name: &'static str, - /// Event Data + /// The event data to be passed onto the event handler pub data: Arc, } diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index 5a1591fff..a4eee7b33 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -210,7 +210,7 @@ impl DesktopController { "user_event" => { let event = events::trigger_from_serialized(req.params.unwrap()); log::debug!("User event: {:?}", event); - sender.unbounded_send(SchedulerMsg::UiEvent(event)).unwrap(); + sender.unbounded_send(SchedulerMsg::Event(event)).unwrap(); } "initialize" => { is_ready.store(true, std::sync::atomic::Ordering::Relaxed); diff --git a/packages/web/src/dom.rs b/packages/web/src/dom.rs index c92dd3400..40568807b 100644 --- a/packages/web/src/dom.rs +++ b/packages/web/src/dom.rs @@ -300,7 +300,7 @@ impl WebsysDom { // "Result" cannot be received from JS // Instead, we just build and immediately execute a closure that returns result match decode_trigger(event) { - Ok(synthetic_event) => trigger.as_ref()(SchedulerMsg::UiEvent(synthetic_event)), + Ok(synthetic_event) => trigger.as_ref()(SchedulerMsg::Event(synthetic_event)), Err(e) => log::error!("Error decoding Dioxus event attribute. {:#?}", e), }; });