فهرست منبع

wip: debugging

Jonathan Kelley 3 سال پیش
والد
کامیت
3edf3e367f

+ 53 - 6
packages/core/src/diff.rs

@@ -443,6 +443,13 @@ impl<'bump> DiffState<'bump> {
             new_idx
         };
 
+        log::info!(
+            "created component {:?} with parent {:?} and originator {:?}",
+            new_idx,
+            parent_idx,
+            vcomponent.originator
+        );
+
         // Actually initialize the caller's slot with the right address
         vcomponent.scope.set(Some(new_idx));
 
@@ -476,6 +483,11 @@ impl<'bump> DiffState<'bump> {
             // Check the most common cases first
             // these are *actual* elements, not wrappers around lists
             (Text(old), Text(new)) => {
+                if std::ptr::eq(old, new) {
+                    log::trace!("skipping node diff - text are the sames");
+                    return;
+                }
+
                 if let Some(root) = old.id.get() {
                     if old.text != new.text {
                         self.mutations.set_text(new.text, root.as_u64());
@@ -487,24 +499,46 @@ impl<'bump> DiffState<'bump> {
             }
 
             (Placeholder(old), Placeholder(new)) => {
+                if std::ptr::eq(old, new) {
+                    log::trace!("skipping node diff - placeholder are the sames");
+                    return;
+                }
+
                 if let Some(root) = old.id.get() {
                     self.scopes.update_node(new_node, root);
                     new.id.set(Some(root))
                 }
             }
 
-            (Element(old), Element(new)) => self.diff_element_nodes(old, new, old_node, new_node),
+            (Element(old), Element(new)) => {
+                if std::ptr::eq(old, new) {
+                    log::trace!("skipping node diff - element are the sames");
+                    return;
+                }
+                self.diff_element_nodes(old, new, old_node, new_node)
+            }
 
             // These two sets are pointers to nodes but are not actually nodes themselves
             (Component(old), Component(new)) => {
+                if std::ptr::eq(old, new) {
+                    log::trace!("skipping node diff - placeholder are the sames");
+                    return;
+                }
                 self.diff_component_nodes(old_node, new_node, *old, *new)
             }
 
-            (Fragment(old), Fragment(new)) => self.diff_fragment_nodes(old, new),
+            (Fragment(old), Fragment(new)) => {
+                if std::ptr::eq(old, new) {
+                    log::trace!("skipping node diff - fragment are the sames");
+                    return;
+                }
+                self.diff_fragment_nodes(old, new)
+            }
 
             // The normal pathway still works, but generates slightly weird instructions
             // This pathway ensures uses the ReplaceAll, not the InsertAfter and remove
             (Placeholder(_), Fragment(new)) => {
+                log::debug!("replacing placeholder with fragment {:?}", new_node);
                 self.stack
                     .create_children(new.children, MountType::Replace { old: old_node });
             }
@@ -686,7 +720,11 @@ impl<'bump> DiffState<'bump> {
         new: &'bump VComponent<'bump>,
     ) {
         let scope_addr = old.scope.get().unwrap();
-        log::trace!("diff_component_nodes: {:?}", scope_addr);
+        log::trace!(
+            "diff_component_nodes. old:  {:#?} new: {:#?}",
+            old_node,
+            new_node
+        );
 
         if std::ptr::eq(old, new) {
             log::trace!("skipping component diff - component is the sames");
@@ -747,6 +785,8 @@ impl<'bump> DiffState<'bump> {
 
             self.stack.scope_stack.pop();
         } else {
+            //
+            log::debug!("scope stack is {:#?}", self.stack.scope_stack);
             self.stack
                 .create_node(new_node, MountType::Replace { old: old_node });
         }
@@ -790,7 +830,10 @@ impl<'bump> DiffState<'bump> {
         match (old, new) {
             ([], []) => {}
             ([], _) => self.stack.create_children(new, MountType::Append),
-            (_, []) => self.remove_nodes(old, true),
+            (_, []) => {
+                log::debug!("removing nodes {:?}", old);
+                self.remove_nodes(old, true)
+            }
             _ => {
                 let new_is_keyed = new[0].key().is_some();
                 let old_is_keyed = old[0].key().is_some();
@@ -1216,6 +1259,7 @@ impl<'bump> DiffState<'bump> {
     }
 
     fn replace_node(&mut self, old: &'bump VNode<'bump>, nodes_created: usize) {
+        log::debug!("Replacing node {:?}", old);
         match old {
             VNode::Element(el) => {
                 let id = old
@@ -1262,11 +1306,12 @@ impl<'bump> DiffState<'bump> {
     ) {
         // or cache the vec on the diff machine
         for node in nodes {
+            log::debug!("removing {:?}", node);
             match node {
                 VNode::Text(t) => {
                     // this check exists because our null node will be removed but does not have an ID
                     if let Some(id) = t.id.get() {
-                        self.scopes.collect_garbage(id);
+                        // self.scopes.collect_garbage(id);
 
                         if gen_muts {
                             self.mutations.remove(id.as_u64());
@@ -1275,7 +1320,7 @@ impl<'bump> DiffState<'bump> {
                 }
                 VNode::Placeholder(a) => {
                     let id = a.id.get().unwrap();
-                    self.scopes.collect_garbage(id);
+                    // self.scopes.collect_garbage(id);
 
                     if gen_muts {
                         self.mutations.remove(id.as_u64());
@@ -1289,6 +1334,8 @@ impl<'bump> DiffState<'bump> {
                     }
 
                     self.remove_nodes(e.children, false);
+
+                    // self.scopes.collect_garbage(id);
                 }
 
                 VNode::Fragment(f) => {

+ 13 - 5
packages/core/src/nodes.rs

@@ -4,7 +4,7 @@
 //! cheap and *very* fast to construct - building a full tree should be quick.
 
 use crate::{
-    innerlude::{Element, Properties, Scope, ScopeId, ScopeState},
+    innerlude::{Element, FcSlot, Properties, Scope, ScopeId, ScopeState},
     lazynodes::LazyNodes,
     AnyEvent, Component,
 };
@@ -177,11 +177,19 @@ impl Debug for VNode<'_> {
                 .field("children", &el.children)
                 .finish(),
             VNode::Text(t) => write!(s, "VNode::VText {{ text: {} }}", t.text),
-            VNode::Placeholder(_) => write!(s, "VNode::VPlaceholder"),
+            VNode::Placeholder(t) => write!(s, "VNode::VPlaceholder {{ id: {:?} }}", t.id),
             VNode::Fragment(frag) => {
                 write!(s, "VNode::VFragment {{ children: {:?} }}", frag.children)
             }
-            VNode::Component(comp) => write!(s, "VNode::VComponent {{ fc: {:?}}}", comp.user_fc),
+            VNode::Component(comp) => {
+                s.debug_struct("VNode::VComponent")
+                    .field("fnptr", &comp.user_fc)
+                    .field("key", &comp.key)
+                    .field("scope", &comp.scope)
+                    .field("originator", &comp.originator)
+                    .finish()
+                //write!(s, "VNode::VComponent {{ fc: {:?}}}", comp.user_fc)
+            }
         }
     }
 }
@@ -384,7 +392,7 @@ pub struct VComponent<'src> {
     pub originator: ScopeId,
     pub scope: Cell<Option<ScopeId>>,
     pub can_memoize: bool,
-    pub user_fc: *const (),
+    pub user_fc: FcSlot,
     pub props: RefCell<Option<Box<dyn AnyProps + 'src>>>,
 }
 
@@ -557,7 +565,7 @@ impl<'a> NodeFactory<'a> {
             key: key.map(|f| self.raw_text(f).0),
             scope: Default::default(),
             can_memoize: P::IS_STATIC,
-            user_fc: component as *const (),
+            user_fc: component as *mut std::os::raw::c_void,
             originator: self.scope.scope_id(),
             props: RefCell::new(Some(Box::new(VComponentProps {
                 // local_props: RefCell::new(Some(props)),

+ 60 - 53
packages/core/src/scopes.rs

@@ -13,7 +13,9 @@ use std::{
     rc::Rc,
 };
 
-pub(crate) type FcSlot = *const ();
+/// for traceability, we use the raw fn pointer to identify the function
+/// we can use this with the traceback crate to resolve funciton names
+pub(crate) type FcSlot = *mut std::os::raw::c_void;
 
 pub(crate) struct Heuristic {
     hook_arena_size: usize,
@@ -82,7 +84,7 @@ impl ScopeArena {
 
     pub(crate) fn new_with_key(
         &self,
-        fc_ptr: *const (),
+        fc_ptr: FcSlot,
         vcomp: Box<dyn AnyProps>,
         parent_scope: Option<ScopeId>,
         container: ElementId,
@@ -116,25 +118,47 @@ impl ScopeArena {
             scope.subtree.set(subtree);
             scope.our_arena_idx = new_scope_id;
             scope.container = container;
+            scope.fnptr = fc_ptr;
             let any_item = self.scopes.borrow_mut().insert(new_scope_id, scope);
             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(
-                    height,
+                self.bump.alloc(ScopeState {
                     container,
-                    new_scope_id,
+                    our_arena_idx: new_scope_id,
                     parent_scope,
-                    vcomp,
-                    self.tasks.clone(),
-                    self.heuristics
-                        .borrow()
-                        .get(&fc_ptr)
-                        .map(|h| (h.node_arena_size, h.hook_arena_size))
-                        .unwrap_or_default(),
-                )),
+                    height,
+                    fnptr: fc_ptr,
+                    props: RefCell::new(Some(vcomp)),
+                    frames: [BumpFrame::new(node_capacity), BumpFrame::new(node_capacity)],
+
+                    // todo: subtrees
+                    subtree: Cell::new(0),
+                    is_subtree_root: Cell::new(false),
+
+                    generation: 0.into(),
+
+                    tasks: self.tasks.clone(),
+                    shared_contexts: Default::default(),
+
+                    items: RefCell::new(SelfReferentialItems {
+                        listeners: Default::default(),
+                        borrowed_props: Default::default(),
+                    }),
+
+                    hook_arena: Bump::new(),
+                    hook_vals: RefCell::new(Vec::with_capacity(hook_capacity)),
+                    hook_idx: Default::default(),
+                }),
             );
         }
 
@@ -169,10 +193,17 @@ impl ScopeArena {
 
     pub fn update_node<'a>(&self, node: &'a VNode<'a>, id: ElementId) {
         let node = unsafe { extend_vnode(node) };
-        *self.nodes.borrow_mut().get_mut(id.0).unwrap() = node;
+
+        let mut nodes = self.nodes.borrow_mut();
+        let entry = nodes.get_mut(id.0);
+        match entry {
+            Some(_node) => *_node = node,
+            None => panic!("cannot update node {}", id),
+        }
     }
 
     pub fn collect_garbage(&self, id: ElementId) {
+        log::debug!("collecting garbage for {:?}", id);
         self.nodes.borrow_mut().remove(id.0);
     }
 
@@ -189,7 +220,7 @@ impl ScopeArena {
     /// This also makes sure that drop order is consistent and predictable. All resources that rely on being dropped will
     /// be dropped.
     pub(crate) fn ensure_drop_safety(&self, scope_id: ScopeId) {
-        log::trace!("Ensuring drop safety for scope {:?}", scope_id);
+        // log::trace!("Ensuring drop safety for scope {:?}", scope_id);
 
         if let Some(scope) = self.get_scope(scope_id) {
             let mut items = scope.items.borrow_mut();
@@ -217,12 +248,23 @@ impl ScopeArena {
         // Cycle to the next frame and then reset it
         // This breaks any latent references, invalidating every pointer referencing into it.
         // Remove all the outdated listeners
-        log::trace!("Running scope {:?}", id);
         self.ensure_drop_safety(id);
 
         // todo: we *know* that this is aliased by the contents of the scope itself
         let scope = unsafe { &mut *self.get_scope_raw(id).expect("could not find scope") };
 
+        // if cfg!(debug_assertions) {
+        log::debug!("running scope {:?} symbol: {:?}", id, scope.fnptr);
+
+        // todo: resolve frames properly
+        backtrace::resolve(scope.fnptr, |symbol| {
+            // backtrace::resolve(scope.fnptr as *mut std::os::raw::c_void, |symbol| {
+            // panic!("asd");
+            // log::trace!("Running scope {:?}, ptr {:?}", id, scope.fnptr);
+            log::debug!("running scope {:?} symbol: {:?}", id, symbol.name());
+        });
+        // }
+
         // Safety:
         // - We dropped the listeners, so no more &mut T can be used while these are held
         // - All children nodes that rely on &mut T are replaced with a new reference
@@ -421,6 +463,7 @@ pub struct ScopeState {
     pub(crate) container: ElementId,
     pub(crate) our_arena_idx: ScopeId,
     pub(crate) height: u32,
+    pub(crate) fnptr: FcSlot,
 
     // todo: subtrees
     pub(crate) is_subtree_root: Cell<bool>,
@@ -449,43 +492,6 @@ pub struct SelfReferentialItems<'a> {
 
 // Public methods exposed to libraries and components
 impl ScopeState {
-    fn new(
-        height: u32,
-        container: ElementId,
-        our_arena_idx: ScopeId,
-        parent_scope: Option<*mut ScopeState>,
-        vcomp: Box<dyn AnyProps>,
-        tasks: Rc<TaskQueue>,
-        (node_capacity, hook_capacity): (usize, usize),
-    ) -> Self {
-        ScopeState {
-            container,
-            our_arena_idx,
-            parent_scope,
-            height,
-            props: RefCell::new(Some(vcomp)),
-            frames: [BumpFrame::new(node_capacity), BumpFrame::new(node_capacity)],
-
-            // todo: subtrees
-            subtree: Cell::new(0),
-            is_subtree_root: Cell::new(false),
-
-            generation: 0.into(),
-
-            tasks,
-            shared_contexts: Default::default(),
-
-            items: RefCell::new(SelfReferentialItems {
-                listeners: Default::default(),
-                borrowed_props: Default::default(),
-            }),
-
-            hook_arena: Bump::new(),
-            hook_vals: RefCell::new(Vec::with_capacity(hook_capacity)),
-            hook_idx: Default::default(),
-        }
-    }
-
     /// 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
@@ -731,6 +737,7 @@ impl ScopeState {
             while let Some(parent_ptr) = search_parent {
                 // safety: all parent pointers are valid thanks to the bump arena
                 let parent = unsafe { &*parent_ptr };
+                log::trace!("Searching parent scope {:?}", parent.scope_id());
                 if let Some(shared) = parent.shared_contexts.borrow().get(&TypeId::of::<T>()) {
                     return Some(shared.clone().downcast::<T>().unwrap());
                 }

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

@@ -212,7 +212,7 @@ impl VirtualDom {
         let scopes = ScopeArena::new(channel.0.clone());
 
         scopes.new_with_key(
-            root as *const _,
+            root as *mut std::os::raw::c_void,
             Box::new(VComponentProps {
                 props: root_props,
                 memo: |_a, _b| unreachable!("memo on root will neve be run"),
@@ -475,6 +475,8 @@ impl VirtualDom {
 
                     let (old, new) = (self.scopes.wip_head(scopeid), self.scopes.fin_head(scopeid));
                     diff_state.stack.push(DiffInstruction::Diff { new, old });
+
+                    log::debug!("pushing scope {:?} onto scope stack", scopeid);
                     diff_state.stack.scope_stack.push(scopeid);
 
                     let scope = scopes.get_scope(scopeid).unwrap();

+ 99 - 18
packages/core/tests/miri_stress.rs

@@ -319,36 +319,117 @@ fn test_pass_thru() {
     #[inline_props]
     fn Router<'a>(cx: Scope, children: Element<'a>) -> Element {
         cx.render(rsx! {
-            &cx.props.children
+            div {
+                &cx.props.children
+            }
         })
     }
 
-    fn MemoizedThing(cx: Scope) -> Element {
-        rsx!(cx, div { "memoized" })
+    #[inline_props]
+    fn NavContainer<'a>(cx: Scope, children: Element<'a>) -> Element {
+        cx.render(rsx! {
+            header {
+                nav {
+                    &cx.props.children
+                }
+            }
+        })
     }
 
-    fn app(cx: Scope) -> Element {
-        let thing = cx.use_hook(|_| "asd");
+    fn NavMenu(cx: Scope) -> Element {
         rsx!(cx,
-            Router {
-                MemoizedThing {
-                }
+            NavBrand {}
+            div {
+                NavStart {}
+                NavEnd {}
             }
         )
     }
 
-    let mut dom = new_dom(app, ());
-    let _ = dom.rebuild();
+    fn NavBrand(cx: Scope) -> Element {
+        rsx!(cx, div {})
+    }
 
-    dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
-    dom.work_with_deadline(|| false);
+    fn NavStart(cx: Scope) -> Element {
+        rsx!(cx, div {})
+    }
 
-    dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
-    dom.work_with_deadline(|| false);
+    fn NavEnd(cx: Scope) -> Element {
+        rsx!(cx, div {})
+    }
 
-    dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
-    dom.work_with_deadline(|| false);
+    #[inline_props]
+    fn MainContainer<'a>(
+        cx: Scope,
+        nav: Element<'a>,
+        body: Element<'a>,
+        footer: Element<'a>,
+    ) -> Element {
+        cx.render(rsx! {
+            div {
+                class: "columns is-mobile",
+                div {
+                    class: "column is-full",
+                    &cx.props.nav,
+                    &cx.props.body,
+                    &cx.props.footer,
+                }
+            }
+        })
+    }
 
-    dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
-    dom.work_with_deadline(|| false);
+    fn app(cx: Scope) -> Element {
+        let nav = cx.render(rsx! {
+            NavContainer {
+                NavMenu {}
+            }
+        });
+        let body = cx.render(rsx! {
+            div {}
+        });
+        let footer = cx.render(rsx! {
+            div {}
+        });
+
+        cx.render(rsx! {
+            MainContainer {
+                nav: nav,
+                body: body,
+                footer: footer,
+            }
+        })
+    }
+
+    let mut dom = new_dom(app, ());
+    let _ = dom.rebuild();
+
+    for x in 0..40 {
+        dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
+        dom.work_with_deadline(|| false);
+        dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
+        dom.work_with_deadline(|| false);
+        dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
+        dom.work_with_deadline(|| false);
+
+        dom.handle_message(SchedulerMsg::Immediate(ScopeId(1)));
+        dom.work_with_deadline(|| false);
+        dom.handle_message(SchedulerMsg::Immediate(ScopeId(1)));
+        dom.work_with_deadline(|| false);
+        dom.handle_message(SchedulerMsg::Immediate(ScopeId(1)));
+        dom.work_with_deadline(|| false);
+
+        dom.handle_message(SchedulerMsg::Immediate(ScopeId(2)));
+        dom.work_with_deadline(|| false);
+        dom.handle_message(SchedulerMsg::Immediate(ScopeId(2)));
+        dom.work_with_deadline(|| false);
+        dom.handle_message(SchedulerMsg::Immediate(ScopeId(2)));
+        dom.work_with_deadline(|| false);
+
+        dom.handle_message(SchedulerMsg::Immediate(ScopeId(3)));
+        dom.work_with_deadline(|| false);
+        dom.handle_message(SchedulerMsg::Immediate(ScopeId(3)));
+        dom.work_with_deadline(|| false);
+        dom.handle_message(SchedulerMsg::Immediate(ScopeId(3)));
+        dom.work_with_deadline(|| false);
+    }
 }

+ 95 - 1
packages/core/tests/sharedstate.rs

@@ -1,6 +1,6 @@
 #![allow(unused, non_upper_case_globals)]
 
-use dioxus::{prelude::*, DomEdit, Mutations};
+use dioxus::{prelude::*, DomEdit, Mutations, SchedulerMsg, ScopeId};
 use dioxus_core as dioxus;
 use dioxus_core_macro::*;
 use dioxus_html as dioxus_elements;
@@ -37,3 +37,97 @@ fn shared_state_test() {
         ]
     );
 }
+
+#[test]
+fn swap_test() {
+    struct MySharedState(&'static str);
+
+    fn app(cx: Scope) -> Element {
+        let val = cx.use_hook(|_| 0);
+        *val += 1;
+
+        cx.provide_context(MySharedState("world!"));
+
+        let child = match *val % 2 {
+            0 => rsx!(
+                Child1 {
+                    Child1 { }
+                    Child2 { }
+                }
+            ),
+            _ => rsx!(
+                Child2 {
+                    Child2 { }
+                    Child2 { }
+                }
+            ),
+        };
+
+        cx.render(rsx!(
+            Router {
+                div { child }
+            }
+        ))
+    }
+
+    #[inline_props]
+    fn Router<'a>(cx: Scope, children: Element<'a>) -> Element<'a> {
+        cx.render(rsx!(div { children }))
+    }
+
+    #[inline_props]
+    fn Child1<'a>(cx: Scope, children: Element<'a>) -> Element {
+        let shared = cx.consume_context::<MySharedState>().unwrap();
+        println!("Child1: {}", shared.0);
+        cx.render(rsx! {
+            div {
+                "{shared.0}",
+                children
+            }
+        })
+    }
+
+    #[inline_props]
+    fn Child2<'a>(cx: Scope, children: Element<'a>) -> Element {
+        let shared = cx.consume_context::<MySharedState>().unwrap();
+        println!("Child2: {}", shared.0);
+        cx.render(rsx! {
+            h1 {
+                "{shared.0}",
+                children
+            }
+        })
+    }
+
+    let mut dom = VirtualDom::new(app);
+    let Mutations { edits, .. } = dom.rebuild();
+
+    dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
+    dom.work_with_deadline(|| false);
+    dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
+    dom.work_with_deadline(|| false);
+    dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
+    dom.work_with_deadline(|| false);
+    dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
+    dom.work_with_deadline(|| false);
+    dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
+    dom.work_with_deadline(|| false);
+    dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
+    dom.work_with_deadline(|| false);
+
+    // dom.handle_message(SchedulerMsg::Immediate(ScopeId(1)));
+    // dom.work_with_deadline(|| false);
+
+    // dom.handle_message(SchedulerMsg::Immediate(ScopeId(2)));
+    // dom.work_with_deadline(|| false);
+
+    // dom.handle_message(SchedulerMsg::Immediate(ScopeId(3)));
+    // dom.work_with_deadline(|| false);
+
+    // dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
+    // dom.work_with_deadline(|| false);
+    // dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
+    // dom.work_with_deadline(|| false);
+    // dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
+    // dom.work_with_deadline(|| false);
+}

+ 1 - 1
packages/router/src/components/link.rs

@@ -41,7 +41,7 @@ pub struct LinkProps<'a> {
 }
 
 pub fn Link<'a>(cx: Scope<'a, LinkProps<'a>>) -> Element {
-    log::trace!("render Link to {}", cx.props.to);
+    // log::trace!("render Link to {}", cx.props.to);
     if let Some(service) = cx.consume_context::<RouterService>() {
         return cx.render(rsx! {
             a {

+ 2 - 2
packages/router/src/components/route.rs

@@ -30,7 +30,7 @@ pub fn Route<'a>(cx: Scope<'a, RouteProps<'a>>) -> Element {
             Some(ctx) => ctx.total_route.to_string(),
             None => cx.props.to.to_string(),
         };
-        log::trace!("total route for {} is {}", cx.props.to, total_route);
+        // log::trace!("total route for {} is {}", cx.props.to, total_route);
 
         // provide our route context
         let route_context = cx.provide_context(RouteContext {
@@ -48,7 +48,7 @@ pub fn Route<'a>(cx: Scope<'a, RouteProps<'a>>) -> Element {
         Some(RouteInner {})
     });
 
-    log::trace!("Checking route {}", cx.props.to);
+    // log::trace!("Checking route {}", cx.props.to);
 
     if router_root.should_render(cx.scope_id()) {
         cx.render(rsx!(&cx.props.children))

+ 1 - 0
packages/router/src/components/router.rs

@@ -17,6 +17,7 @@ pub struct RouterProps<'a> {
 
 #[allow(non_snake_case)]
 pub fn Router<'a>(cx: Scope<'a, RouterProps<'a>>) -> Element {
+    log::debug!("running router {:?}", cx.scope_id());
     let svc = cx.use_hook(|_| {
         let update = cx.schedule_update_any();
         cx.provide_context(RouterService::new(update, cx.scope_id()))

+ 1 - 6
packages/router/src/platform/mod.rs

@@ -1,9 +1,4 @@
 pub trait RouterProvider {
     fn get_current_route(&self) -> String;
-    fn subscribe_to_route_changes(&self, callback: Box<dyn Fn(String)>);
-}
-
-enum RouteChange {
-    LinkTo(String),
-    Back(String),
+    fn listen(&self, callback: Box<dyn Fn()>);
 }

+ 40 - 36
packages/router/src/service.rs

@@ -7,14 +7,18 @@ use std::{
 
 use dioxus_core::ScopeId;
 
+use crate::platform::RouterProvider;
+
 pub struct RouterService {
     pub(crate) regen_route: Rc<dyn Fn(ScopeId)>,
     pub(crate) pending_events: Rc<RefCell<Vec<RouteEvent>>>,
-    history: Rc<RefCell<BrowserHistory>>,
     slots: Rc<RefCell<Vec<(ScopeId, String)>>>,
     onchange_listeners: Rc<RefCell<HashSet<ScopeId>>>,
     root_found: Rc<Cell<Option<ScopeId>>>,
     cur_path_params: Rc<RefCell<HashMap<String, String>>>,
+
+    // history: Rc<dyn RouterProvider>,
+    history: Rc<RefCell<BrowserHistory>>,
     listener: HistoryListener,
 }
 
@@ -58,12 +62,12 @@ impl RouterService {
                 root_found.set(None);
                 // checking if the route is valid is cheap, so we do it
                 for (slot, root) in slots.borrow_mut().iter().rev() {
-                    log::trace!("regenerating slot {:?} for root '{}'", slot, root);
+                    // log::trace!("regenerating slot {:?} for root '{}'", slot, root);
                     regen_route(*slot);
                 }
 
                 for listener in onchange_listeners.borrow_mut().iter() {
-                    log::trace!("regenerating listener {:?}", listener);
+                    // log::trace!("regenerating listener {:?}", listener);
                     regen_route(*listener);
                 }
 
@@ -87,31 +91,31 @@ impl RouterService {
     }
 
     pub fn push_route(&self, route: &str) {
-        log::trace!("Pushing route: {}", route);
+        // log::trace!("Pushing route: {}", route);
         self.history.borrow_mut().push(route);
     }
 
     pub fn register_total_route(&self, route: String, scope: ScopeId, fallback: bool) {
         let clean = clean_route(route);
-        log::trace!("Registered route '{}' with scope id {:?}", clean, scope);
+        // log::trace!("Registered route '{}' with scope id {:?}", clean, scope);
         self.slots.borrow_mut().push((scope, clean));
     }
 
     pub fn should_render(&self, scope: ScopeId) -> bool {
-        log::trace!("Should render scope id {:?}?", scope);
+        // log::trace!("Should render scope id {:?}?", scope);
         if let Some(root_id) = self.root_found.get() {
-            log::trace!("  we already found a root with scope id {:?}", root_id);
+            // log::trace!("  we already found a root with scope id {:?}", root_id);
             if root_id == scope {
-                log::trace!("    yes - it's a match");
+                // log::trace!("    yes - it's a match");
                 return true;
             }
-            log::trace!("    no - it's not a match");
+            // log::trace!("    no - it's not a match");
             return false;
         }
 
         let location = self.history.borrow().location();
         let path = location.path();
-        log::trace!("  current path is '{}'", path);
+        // log::trace!("  current path is '{}'", path);
 
         let roots = self.slots.borrow();
 
@@ -120,23 +124,23 @@ impl RouterService {
         // fallback logic
         match root {
             Some((id, route)) => {
-                log::trace!(
-                    "  matched given scope id {:?} with route root '{}'",
-                    scope,
-                    route,
-                );
+                // log::trace!(
+                //     "  matched given scope id {:?} with route root '{}'",
+                //     scope,
+                //     route,
+                // );
                 if let Some(params) = route_matches_path(route, path) {
-                    log::trace!("    and it matches the current path '{}'", path);
+                    // log::trace!("    and it matches the current path '{}'", path);
                     self.root_found.set(Some(*id));
                     *self.cur_path_params.borrow_mut() = params;
                     true
                 } else {
                     if route == "" {
-                        log::trace!("    and the route is the root, so we will use that without a better match");
+                        // log::trace!("    and the route is the root, so we will use that without a better match");
                         self.root_found.set(Some(*id));
                         true
                     } else {
-                        log::trace!("    and the route '{}' is not the root nor does it match the current path", route);
+                        // log::trace!("    and the route '{}' is not the root nor does it match the current path", route);
                         false
                     }
                 }
@@ -154,12 +158,12 @@ impl RouterService {
     }
 
     pub fn subscribe_onchange(&self, id: ScopeId) {
-        log::trace!("Subscribing onchange for scope id {:?}", id);
+        // log::trace!("Subscribing onchange for scope id {:?}", id);
         self.onchange_listeners.borrow_mut().insert(id);
     }
 
     pub fn unsubscribe_onchange(&self, id: ScopeId) {
-        log::trace!("Subscribing onchange for scope id {:?}", id);
+        // log::trace!("Subscribing onchange for scope id {:?}", id);
         self.onchange_listeners.borrow_mut().remove(&id);
     }
 }
@@ -182,36 +186,36 @@ fn route_matches_path(route: &str, path: &str) -> Option<HashMap<String, String>
     let route_pieces = route.split('/').collect::<Vec<_>>();
     let path_pieces = clean_path(path).split('/').collect::<Vec<_>>();
 
-    log::trace!(
-        "  checking route pieces {:?} vs path pieces {:?}",
-        route_pieces,
-        path_pieces,
-    );
+    // log::trace!(
+    //     "  checking route pieces {:?} vs path pieces {:?}",
+    //     route_pieces,
+    //     path_pieces,
+    // );
 
     if route_pieces.len() != path_pieces.len() {
-        log::trace!("    the routes are different lengths");
+        // log::trace!("    the routes are different lengths");
         return None;
     }
 
     let mut matches = HashMap::new();
     for (i, r) in route_pieces.iter().enumerate() {
-        log::trace!("    checking route piece '{}' vs path", r);
+        // log::trace!("    checking route piece '{}' vs path", r);
         // If this is a parameter then it matches as long as there's
         // _any_thing in that spot in the path.
         if r.starts_with(':') {
-            log::trace!(
-                "      route piece '{}' starts with a colon so it matches anything",
-                r,
-            );
+            // log::trace!(
+            //     "      route piece '{}' starts with a colon so it matches anything",
+            //     r,
+            // );
             let param = &r[1..];
             matches.insert(param.to_string(), path_pieces[i].to_string());
             continue;
         }
-        log::trace!(
-            "      route piece '{}' must be an exact match for path piece '{}'",
-            r,
-            path_pieces[i],
-        );
+        // log::trace!(
+        //     "      route piece '{}' must be an exact match for path piece '{}'",
+        //     r,
+        //     path_pieces[i],
+        // );
         if path_pieces[i] != *r {
             return None;
         }