Просмотр исходного кода

feat: dedicated mutations module

Jonathan Kelley 3 лет назад
Родитель
Сommit
db6d0184aa

+ 8 - 6
packages/core/Cargo.toml

@@ -28,11 +28,6 @@ longest-increasing-subsequence = "0.1.0"
 # internall used
 log = "0.4"
 
-# # Serialize the Edits for use in Webview/Liveview instances
-serde = { version = "1", features = ["derive"], optional = true }
-
-appendlist = "1.4.0"
-
 futures-util = "0.3.15"
 
 smallvec = "1.6.1"
@@ -43,12 +38,15 @@ futures-channel = "0.3.16"
 
 # used for noderefs
 once_cell = "1.8.0"
-indexmap = "1.7.0"
+
+# # Serialize the Edits for use in Webview/Liveview instances
+serde = { version = "1", features = ["derive"], optional = true }
 
 
 [dev-dependencies]
 anyhow = "1.0.42"
 async-std = { version = "1.9.0", features = ["attributes"] }
+criterion = "0.3.5"
 dioxus-html = { path = "../html" }
 fern = { version = "0.6.0", features = ["colored"] }
 simple_logger = "1.13.0"
@@ -57,3 +55,7 @@ simple_logger = "1.13.0"
 [features]
 default = ["serialize"]
 serialize = ["serde"]
+
+[[bench]]
+name = "create"
+harness = false

+ 22 - 0
packages/core/benches/create.rs

@@ -0,0 +1,22 @@
+use criterion::{black_box, criterion_group, criterion_main, Criterion};
+
+fn fibonacci(n: u64) -> u64 {
+    match n {
+        0 => 1,
+        1 => 1,
+        n => fibonacci(n - 1) + fibonacci(n - 2),
+    }
+}
+
+fn criterion_benchmark(c: &mut Criterion) {
+    c.bench_function("fib 20", |b| b.iter(|| fibonacci(black_box(20))));
+}
+
+criterion_group!(benches, criterion_benchmark);
+
+fn main() {
+    benches();
+    Criterion::default().configure_from_args().final_summary();
+    // $crate::__warn_about_html_reports_feature();
+    // $crate::__warn_about_cargo_bench_support_feature();
+}

+ 208 - 364
packages/core/src/diff.rs

@@ -84,9 +84,8 @@
 //!  - https://hacks.mozilla.org/2019/03/fast-bump-allocated-virtual-doms-with-rust-and-wasm/
 
 use crate::{arena::SharedResources, innerlude::*};
-use futures_util::Future;
+use futures_util::{Future, FutureExt};
 use fxhash::{FxBuildHasher, FxHashMap, FxHashSet};
-use indexmap::IndexSet;
 use smallvec::{smallvec, SmallVec};
 
 use std::{
@@ -113,7 +112,7 @@ pub struct DiffMachine<'bump> {
 
     pub nodes_created_stack: SmallVec<[usize; 10]>,
 
-    pub instructions: SmallVec<[DiffInstruction<'bump>; 10]>,
+    pub instructions: Vec<DiffInstruction<'bump>>,
 
     pub scope_stack: SmallVec<[ScopeId; 5]>,
 
@@ -124,8 +123,7 @@ pub struct DiffMachine<'bump> {
 
 /// The stack instructions we use to diff and create new nodes.
 ///
-/// Right now, we insert an instruction for every child node we want to create and diff. This can be less efficient than
-/// a custom iterator type - but this is current easier to implement. In the future, let's try interact with the stack less.
+/// These instructions are essentially mini state machines that stay on top of the stack until they are finished.
 #[derive(Debug)]
 pub enum DiffInstruction<'a> {
     DiffNode {
@@ -138,28 +136,25 @@ pub enum DiffInstruction<'a> {
         new: &'a [VNode<'a>],
     },
 
-    // diff two lists of equally sized children
-    DiffEqual {
-        progress: usize,
-        old: &'a [VNode<'a>],
-        new: &'a [VNode<'a>],
-    },
-
     Create {
         node: &'a VNode<'a>,
         and: MountType<'a>,
     },
 
-    CreateChildren {
-        progress: usize,
+    Remove {
+        child: &'a VNode<'a>,
+    },
+
+    RemoveChildren {
         children: &'a [VNode<'a>],
-        and: MountType<'a>,
     },
 
     Mount {
         and: MountType<'a>,
     },
 
+    PopElement,
+
     PopScope,
 }
 
@@ -168,8 +163,8 @@ pub enum MountType<'a> {
     Absorb,
     Append,
     Replace { old: &'a VNode<'a> },
-    InsertAfter { other_node: &'a VNode<'a> },
-    InsertBefore { other_node: &'a VNode<'a> },
+    InsertAfter { other_node: Option<&'a VNode<'a>> },
+    InsertBefore { other_node: Option<&'a VNode<'a>> },
 }
 
 impl<'bump> DiffMachine<'bump> {
@@ -179,7 +174,7 @@ impl<'bump> DiffMachine<'bump> {
         shared: &'bump SharedResources,
     ) -> Self {
         Self {
-            instructions: smallvec![],
+            instructions: Vec::with_capacity(1000),
             nodes_created_stack: smallvec![],
             mutations: edits,
             scope_stack: smallvec![cur_scope],
@@ -209,10 +204,9 @@ impl<'bump> DiffMachine<'bump> {
     ///
     /// We do depth-first to maintain high cache locality (nodes were originally generated recursively).
     pub async fn work(&mut self) -> Result<()> {
-        // todo: don't move the reused instructions around
         // defer to individual functions so the compiler produces better code
         // large functions tend to be difficult for the compiler to work with
-        while let Some(instruction) = self.instructions.last_mut() {
+        while let Some(instruction) = self.instructions.pop() {
             log::debug!("Handling diff instruction: {:?}", instruction);
 
             // todo: call this less frequently, there is a bit of overhead involved
@@ -220,97 +214,42 @@ impl<'bump> DiffMachine<'bump> {
 
             match instruction {
                 DiffInstruction::PopScope => {
-                    self.instructions.pop();
                     self.scope_stack.pop();
                 }
+                DiffInstruction::PopElement => {
+                    self.mutations.pop();
+                }
 
                 DiffInstruction::DiffNode { old, new, .. } => {
-                    let (old, new) = (*old, *new);
-                    self.instructions.pop();
-
                     self.diff_node(old, new);
                 }
 
-                DiffInstruction::DiffEqual { progress, old, new } => {
-                    debug_assert_eq!(old.len(), new.len());
-
-                    if let (Some(old_child), Some(new_child)) =
-                        (old.get(*progress), new.get(*progress))
-                    {
-                        *progress += 1;
-                        self.diff_node(old_child, new_child);
-                    } else {
-                        self.instructions.pop();
-                    }
-                }
-
-                // this is slightly more complicated, we need to find a way to pause our LIS code
                 DiffInstruction::DiffChildren { old, new } => {
-                    let (old, new) = (*old, *new);
-                    self.instructions.pop();
-
                     self.diff_children(old, new);
                 }
 
                 DiffInstruction::Create { node, and } => {
-                    let (node, and) = (*node, *and);
-                    self.instructions.pop();
-
                     self.nodes_created_stack.push(0);
                     self.instructions.push(DiffInstruction::Mount { and });
-
                     self.create_node(node);
                 }
 
-                DiffInstruction::CreateChildren {
-                    progress,
-                    children,
-                    and,
-                } => {
-                    let and = *and;
-
-                    if *progress == 0 {
-                        self.nodes_created_stack.push(0);
+                DiffInstruction::Remove { child } => {
+                    for child in RealChildIterator::new(child, self.vdom) {
+                        self.mutations.push_root(child.direct_id());
+                        self.mutations.remove();
                     }
+                }
 
-                    if let Some(child) = children.get(*progress) {
-                        *progress += 1;
-
-                        if *progress == children.len() {
-                            self.instructions.pop();
-                            self.instructions.push(DiffInstruction::Mount { and });
-                        }
-
-                        self.create_node(child);
-                    } else if children.len() == 0 {
-                        self.instructions.pop();
+                DiffInstruction::RemoveChildren { children } => {
+                    for child in RealChildIterator::new_from_slice(children, self.vdom) {
+                        self.mutations.push_root(child.direct_id());
+                        self.mutations.remove();
                     }
                 }
 
                 DiffInstruction::Mount { and } => {
-                    let nodes_created = self.nodes_created_stack.pop().unwrap();
-                    match and {
-                        // add the nodes from this virtual list to the parent
-                        // used by fragments and components
-                        MountType::Absorb => {
-                            *self.nodes_created_stack.last_mut().unwrap() += nodes_created;
-                        }
-                        MountType::Append => {
-                            self.edit_append_children(nodes_created as u32);
-                        }
-                        MountType::Replace { old } => {
-                            todo!()
-                            // self.edit_replace_with(with as u32, many as u32);
-                        }
-                        MountType::InsertAfter { other_node } => {
-                            self.edit_insert_after(nodes_created as u32);
-                        }
-                        MountType::InsertBefore { other_node } => {
-                            self.edit_insert_before(nodes_created as u32);
-                        }
-                    }
-
-                    self.instructions.pop();
+                    self.mount(and);
                 }
             };
         }
@@ -318,6 +257,33 @@ impl<'bump> DiffMachine<'bump> {
         Ok(())
     }
 
+    fn mount(&mut self, and: MountType) {
+        let nodes_created = self.nodes_created_stack.pop().unwrap();
+        match and {
+            // add the nodes from this virtual list to the parent
+            // used by fragments and components
+            MountType::Absorb => {
+                *self.nodes_created_stack.last_mut().unwrap() += nodes_created;
+            }
+            MountType::Append => {
+                self.mutations.edits.push(AppendChildren {
+                    many: nodes_created as u32,
+                });
+            }
+            MountType::Replace { old } => {
+                todo!()
+                // self.mutations.replace_with(with as u32, many as u32);
+            }
+            MountType::InsertAfter { other_node } => {
+                self.mutations.insert_after(nodes_created as u32);
+            }
+
+            MountType::InsertBefore { other_node } => {
+                self.mutations.insert_before(nodes_created as u32);
+            }
+        }
+    }
+
     // =================================
     //  Tools for creating new nodes
     // =================================
@@ -335,21 +301,21 @@ impl<'bump> DiffMachine<'bump> {
 
     fn create_text_node(&mut self, vtext: &'bump VText<'bump>) {
         let real_id = self.vdom.reserve_node();
-        self.edit_create_text_node(vtext.text, real_id);
+        self.mutations.create_text_node(vtext.text, real_id);
         vtext.dom_id.set(Some(real_id));
         *self.nodes_created_stack.last_mut().unwrap() += 1;
     }
 
     fn create_suspended_node(&mut self, suspended: &'bump VSuspended) {
         let real_id = self.vdom.reserve_node();
-        self.edit_create_placeholder(real_id);
+        self.mutations.create_placeholder(real_id);
         suspended.node.set(Some(real_id));
         *self.nodes_created_stack.last_mut().unwrap() += 1;
     }
 
     fn create_anchor_node(&mut self, anchor: &'bump VAnchor) {
         let real_id = self.vdom.reserve_node();
-        self.edit_create_placeholder(real_id);
+        self.mutations.create_placeholder(real_id);
         anchor.dom_id.set(Some(real_id));
         *self.nodes_created_stack.last_mut().unwrap() += 1;
     }
@@ -366,7 +332,7 @@ impl<'bump> DiffMachine<'bump> {
         } = element;
 
         let real_id = self.vdom.reserve_node();
-        self.edit_create_element(tag_name, *namespace, real_id);
+        self.mutations.create_element(tag_name, *namespace, real_id);
 
         *self.nodes_created_stack.last_mut().unwrap() += 1;
 
@@ -377,28 +343,21 @@ impl<'bump> DiffMachine<'bump> {
         listeners.iter().for_each(|listener| {
             self.fix_listener(listener);
             listener.mounted_node.set(Some(real_id));
-            self.edit_new_event_listener(listener, cur_scope.clone());
+            self.mutations
+                .new_event_listener(listener, cur_scope.clone());
         });
 
         for attr in *attributes {
-            self.edit_set_attribute(attr);
+            self.mutations.set_attribute(attr);
         }
 
         if children.len() > 0 {
-            self.instructions.push(DiffInstruction::CreateChildren {
-                children,
-                progress: 0,
-                and: MountType::Append,
-            });
+            self.create_children_instructions(children, MountType::Append);
         }
     }
 
     fn create_fragment_node(&mut self, frag: &'bump VFragment<'bump>) {
-        self.instructions.push(DiffInstruction::CreateChildren {
-            children: frag.children,
-            progress: 0,
-            and: MountType::Absorb,
-        });
+        self.create_children_instructions(frag.children, MountType::Absorb);
     }
 
     fn create_component_node(&mut self, vcomponent: &'bump VComponent<'bump>) {
@@ -417,6 +376,7 @@ impl<'bump> DiffMachine<'bump> {
                 height,
                 ScopeChildren(vcomponent.children),
                 self.vdom.clone(),
+                vcomponent.name,
             )
         });
 
@@ -479,23 +439,13 @@ impl<'bump> DiffMachine<'bump> {
             (Component(old), Component(new)) => self.diff_component_nodes(old, new),
             (Fragment(old), Fragment(new)) => self.diff_fragment_nodes(old, new),
             (Anchor(old), Anchor(new)) => new.dom_id.set(old.dom_id.get()),
+            (Suspended(old), Suspended(new)) => new.node.set(old.node.get()),
 
+            // Anything else is just a basic replace and create
             (
-                Component(_) | Fragment(_) | Text(_) | Element(_) | Anchor(_),
-                Component(_) | Fragment(_) | Text(_) | Element(_) | Anchor(_),
-            ) => {
-                self.replace_and_create_many_with_many([old_node], [new_node]);
-            }
-
-            // TODO: these don't properly clean up any data
-            (Suspended(old), new) => {
-                self.replace_and_create_many_with_many([old_node], [new_node]);
-            }
-
-            // a node that was once real is now suspended
-            (old, Suspended(_)) => {
-                self.replace_and_create_many_with_many([old_node], [new_node]);
-            }
+                Component(_) | Fragment(_) | Text(_) | Element(_) | Anchor(_) | Suspended(_),
+                Component(_) | Fragment(_) | Text(_) | Element(_) | Anchor(_) | Suspended(_),
+            ) => self.replace_and_create_many_with_many(old_node, new_node),
         }
     }
 
@@ -503,9 +453,9 @@ impl<'bump> DiffMachine<'bump> {
         let root = old.dom_id.get().unwrap();
 
         if old.text != new.text {
-            self.edit_push_root(root);
-            self.edit_set_text(new.text);
-            self.edit_pop();
+            self.mutations.push_root(root);
+            self.mutations.set_text(new.text);
+            self.mutations.pop();
         }
 
         new.dom_id.set(Some(root));
@@ -545,17 +495,17 @@ impl<'bump> DiffMachine<'bump> {
             for (old_attr, new_attr) in old.attributes.iter().zip(new.attributes.iter()) {
                 if old_attr.value != new_attr.value {
                     please_commit(&mut self.mutations.edits);
-                    self.edit_set_attribute(new_attr);
+                    self.mutations.set_attribute(new_attr);
                 }
             }
         } else {
             // TODO: provide some sort of report on how "good" the diffing was
             please_commit(&mut self.mutations.edits);
             for attribute in old.attributes {
-                self.edit_remove_attribute(attribute);
+                self.mutations.remove_attribute(attribute);
             }
             for attribute in new.attributes {
-                self.edit_set_attribute(attribute)
+                self.mutations.set_attribute(attribute)
             }
         }
 
@@ -572,8 +522,8 @@ impl<'bump> DiffMachine<'bump> {
             for (old_l, new_l) in old.listeners.iter().zip(new.listeners.iter()) {
                 if old_l.event != new_l.event {
                     please_commit(&mut self.mutations.edits);
-                    self.edit_remove_event_listener(old_l.event);
-                    self.edit_new_event_listener(new_l, cur_scope);
+                    self.mutations.remove_event_listener(old_l.event);
+                    self.mutations.new_event_listener(new_l, cur_scope);
                 }
                 new_l.mounted_node.set(old_l.mounted_node.get());
                 self.fix_listener(new_l);
@@ -581,11 +531,11 @@ impl<'bump> DiffMachine<'bump> {
         } else {
             please_commit(&mut self.mutations.edits);
             for listener in old.listeners {
-                self.edit_remove_event_listener(listener.event);
+                self.mutations.remove_event_listener(listener.event);
             }
             for listener in new.listeners {
                 listener.mounted_node.set(Some(root));
-                self.edit_new_event_listener(listener, cur_scope);
+                self.mutations.new_event_listener(listener, cur_scope);
 
                 // Make sure the listener gets attached to the scope list
                 self.fix_listener(listener);
@@ -593,7 +543,7 @@ impl<'bump> DiffMachine<'bump> {
         }
 
         if has_comitted {
-            self.edit_pop();
+            self.mutations.pop();
         }
 
         self.diff_children(old.children, new.children);
@@ -646,8 +596,8 @@ impl<'bump> DiffMachine<'bump> {
 
             // // remove any leftovers
             // for to_remove in old_iter {
-            //     self.edit_push_root(to_remove.direct_id());
-            //     self.edit_remove();
+            //     self.mutations.push_root(to_remove.direct_id());
+            //     self.mutations.remove();
             // }
 
             // // seems like we could combine this into a single instruction....
@@ -670,6 +620,26 @@ impl<'bump> DiffMachine<'bump> {
         self.diff_children(old.children, new.children);
     }
 
+    // =============================================
+    //  Utilites for creating new diff instructions
+    // =============================================
+
+    fn create_children_instructions(
+        &mut self,
+        children: &'bump [VNode<'bump>],
+        and: MountType<'bump>,
+    ) {
+        self.nodes_created_stack.push(0);
+        self.instructions.push(DiffInstruction::Mount { and });
+
+        for child in children.into_iter().rev() {
+            self.instructions.push(DiffInstruction::Create {
+                and: MountType::Absorb,
+                node: child,
+            });
+        }
+    }
+
     /// Destroy a scope and all of its descendents.
     ///
     /// Calling this will run the destuctors on all hooks in the tree.
@@ -727,11 +697,7 @@ impl<'bump> DiffMachine<'bump> {
 
             // Completely adding new nodes, removing any placeholder if it exists
             (IS_EMPTY, IS_NOT_EMPTY) => {
-                self.instructions.push(DiffInstruction::CreateChildren {
-                    children: new,
-                    progress: 0,
-                    and: MountType::Append,
-                });
+                self.create_children_instructions(new, MountType::Append);
             }
 
             // Completely removing old nodes and putting an anchor in its place
@@ -755,11 +721,10 @@ impl<'bump> DiffMachine<'bump> {
 
                     // Replace the anchor with whatever new nodes are coming down the pipe
                     (VNode::Anchor(anchor), _) => {
-                        self.instructions.push(DiffInstruction::CreateChildren {
-                            children: new,
-                            progress: 0,
-                            and: MountType::Replace { old: first_old },
-                        });
+                        self.create_children_instructions(
+                            new,
+                            MountType::Replace { old: first_old },
+                        );
                     }
 
                     // Replace whatever nodes are sitting there with the anchor
@@ -905,13 +870,13 @@ impl<'bump> DiffMachine<'bump> {
         if shared_prefix_count == old.len() {
             // Load the last element
             let last_node = self.find_last_element(new.last().unwrap()).direct_id();
-            self.edit_push_root(last_node);
+            self.mutations.push_root(last_node);
 
             // Create the new children and insert them after
             //
             todo!();
             // let meta = self.create_children(&new[shared_prefix_count..]);
-            // self.edit_insert_after(meta.added_to_stack);
+            // self.mutations.insert_after(meta.added_to_stack);
 
             return KeyedPrefixResult::Finished;
         }
@@ -937,7 +902,7 @@ impl<'bump> DiffMachine<'bump> {
         for child in new {
             todo!();
             // let meta = self.create_vnode(child);
-            // self.edit_append_children(meta.added_to_stack);
+            // self.mutations.append_children(meta.added_to_stack);
         }
     }
 
@@ -1077,7 +1042,7 @@ impl<'bump> DiffMachine<'bump> {
             root = Some(new_anchor);
 
             // let anchor_el = self.find_first_element(new_anchor);
-            // self.edit_push_root(anchor_el.direct_id());
+            // self.mutations.push_root(anchor_el.direct_id());
             // // let mut pushed = false;
 
             'inner: loop {
@@ -1100,27 +1065,27 @@ impl<'bump> DiffMachine<'bump> {
                         // now move all the nodes into the right spot
                         for child in RealChildIterator::new(next_new, self.vdom) {
                             let el = child.direct_id();
-                            self.edit_push_root(el);
-                            self.edit_insert_before(1);
+                            self.mutations.push_root(el);
+                            self.mutations.insert_before(1);
                         }
                     } else {
                         self.instructions.push(DiffInstruction::Create {
                             node: next_new,
                             and: MountType::InsertBefore {
-                                other_node: new_anchor,
+                                other_node: Some(new_anchor),
                             },
                         });
                     }
                 }
             }
 
-            self.edit_pop();
+            self.mutations.pop();
         }
 
         let final_lis_node = root.unwrap();
         let final_el_node = self.find_last_element(final_lis_node);
         let final_el = final_el_node.direct_id();
-        self.edit_push_root(final_el);
+        self.mutations.push_root(final_el);
 
         let mut last_iter = new.iter().rev().enumerate();
         let last_key = final_lis_node.key().unwrap();
@@ -1145,8 +1110,8 @@ impl<'bump> DiffMachine<'bump> {
                 // now move all the nodes into the right spot
                 for child in RealChildIterator::new(last_node, self.vdom) {
                     let el = child.direct_id();
-                    self.edit_push_root(el);
-                    self.edit_insert_after(1);
+                    self.mutations.push_root(el);
+                    self.mutations.insert_after(1);
                 }
             } else {
                 eprintln!("key is not contained {:?}", key);
@@ -1154,10 +1119,10 @@ impl<'bump> DiffMachine<'bump> {
                 // insert it before the current milestone
                 todo!();
                 // let meta = self.create_vnode(last_node);
-                // self.edit_insert_after(meta.added_to_stack);
+                // self.mutations.insert_after(meta.added_to_stack);
             }
         }
-        self.edit_pop();
+        self.mutations.pop();
     }
 
     // Diff the suffix of keyed children that share the same keys in the same order.
@@ -1198,19 +1163,17 @@ impl<'bump> DiffMachine<'bump> {
         match old.len().cmp(&new.len()) {
             // old.len > new.len -> removing some nodes
             Ordering::Greater => {
-                // diff them together
-                for (new_child, old_child) in new.iter().zip(old.iter()) {
-                    self.diff_node(old_child, new_child);
+                // Generate instructions to diff the existing elements
+                for (new_child, old_child) in new.iter().zip(old.iter()).rev() {
+                    self.instructions.push(DiffInstruction::DiffNode {
+                        new: new_child,
+                        old: old_child,
+                    });
                 }
 
-                // todo: we would emit fewer instructions if we just did a replace many
-                // remove whatever is still dangling
-                for item in &old[new.len()..] {
-                    for i in RealChildIterator::new(item, self.vdom) {
-                        self.edit_push_root(i.direct_id());
-                        self.edit_remove();
-                    }
-                }
+                self.instructions.push(DiffInstruction::RemoveChildren {
+                    children: &old[new.len()..],
+                });
             }
 
             // old.len < new.len -> adding some nodes
@@ -1218,27 +1181,30 @@ impl<'bump> DiffMachine<'bump> {
             //
             // we need to save the last old element and then replace it with all the new ones
             Ordering::Less => {
-                // Add the new elements to the last old element while it still exists
-                let last = self.find_last_element(old.last().unwrap());
-                self.edit_push_root(last.direct_id());
-
-                // create the rest and insert them
-                todo!();
-                // let meta = self.create_children(&new[old.len()..]);
-                // self.edit_insert_after(meta.added_to_stack);
-
-                self.edit_pop();
-
-                // diff the rest
-                for (new_child, old_child) in new.iter().zip(old.iter()) {
-                    self.diff_node(old_child, new_child)
+                // Generate instructions to diff the existing elements
+                for (new_child, old_child) in new.iter().zip(old.iter()).rev() {
+                    self.instructions.push(DiffInstruction::DiffNode {
+                        new: new_child,
+                        old: old_child,
+                    });
                 }
+
+                // Generate instructions to add in the new elements
+                self.create_children_instructions(
+                    &new[old.len()..],
+                    MountType::InsertAfter {
+                        other_node: old.last(),
+                    },
+                );
             }
 
             // old.len == new.len -> no nodes added/removed, but perhaps changed
             Ordering::Equal => {
-                for (new_child, old_child) in new.iter().zip(old.iter()) {
-                    self.diff_node(old_child, new_child);
+                for (new_child, old_child) in new.iter().zip(old.iter()).rev() {
+                    self.instructions.push(DiffInstruction::DiffNode {
+                        new: new_child,
+                        old: old_child,
+                    });
                 }
             }
         }
@@ -1247,24 +1213,24 @@ impl<'bump> DiffMachine<'bump> {
     // ======================
     // Support methods
     // ======================
-    // Remove all of a node's children.
-    //
-    // The change list stack must have this shape upon entry to this function:
-    //
-    //     [... parent]
-    //
-    // When this function returns, the change list stack is in the same state.
-    fn remove_all_children(&mut self, old: &'bump [VNode<'bump>]) {
-        // debug_assert!(self.traversal_is_committed());
-        log::debug!("REMOVING CHILDREN");
-        for _child in old {
-            // registry.remove_subtree(child);
-        }
-        // Fast way to remove all children: set the node's textContent to an empty
-        // string.
-        todo!()
-        // self.set_inner_text("");
-    }
+    // // Remove all of a node's children.
+    // //
+    // // The change list stack must have this shape upon entry to this function:
+    // //
+    // //     [... parent]
+    // //
+    // // When this function returns, the change list stack is in the same state.
+    // fn remove_all_children(&mut self, old: &'bump [VNode<'bump>]) {
+    //     // debug_assert!(self.traversal_is_committed());
+    //     log::debug!("REMOVING CHILDREN");
+    //     for _child in old {
+    //         // registry.remove_subtree(child);
+    //     }
+    //     // Fast way to remove all children: set the node's textContent to an empty
+    //     // string.
+    //     todo!()
+    //     // self.set_inner_text("");
+    // }
     // Remove the current child and all of its following siblings.
     //
     // The change list stack must have this shape upon entry to this function:
@@ -1324,8 +1290,16 @@ impl<'bump> DiffMachine<'bump> {
         }
     }
 
-    fn remove_child(&mut self, node: &'bump VNode<'bump>) {
-        self.replace_and_create_many_with_many(Some(node), None);
+    // fn remove_child(&mut self, node: &'bump VNode<'bump>) {
+    //     self.replace_and_create_many_with_many(Some(node), None);
+    // }
+
+    fn replace_many_with_many(&mut self, old: &'bump [VNode<'bump>], new: &'bump [VNode<'bump>]) {
+        //
+    }
+
+    fn replace_one_with_many(&mut self, old: &'bump VNode<'bump>, new: &'bump [VNode<'bump>]) {
+        //
     }
 
     /// Remove all the old nodes and replace them with newly created new nodes.
@@ -1333,11 +1307,13 @@ impl<'bump> DiffMachine<'bump> {
     /// The new nodes *will* be created - don't create them yourself!
     fn replace_and_create_many_with_many(
         &mut self,
-        old_nodes: impl IntoIterator<Item = &'bump VNode<'bump>>,
-        new_nodes: impl IntoIterator<Item = &'bump VNode<'bump>>,
+        old_node: &'bump VNode<'bump>,
+        new_node: &'bump VNode<'bump>,
+        // old_nodes: impl IntoIterator<Item = &'bump VNode<'bump>>,
+        // new_nodes: impl IntoIterator<Item = &'bump VNode<'bump>>,
     ) {
         let mut nodes_to_replace = Vec::new();
-        let mut nodes_to_search = old_nodes.into_iter().collect::<Vec<_>>();
+        let mut nodes_to_search = vec![old_node];
         let mut scopes_obliterated = Vec::new();
         while let Some(node) = nodes_to_search.pop() {
             match &node {
@@ -1368,20 +1344,25 @@ impl<'bump> DiffMachine<'bump> {
             // self.create_garbage(node);
         }
 
-        let n = nodes_to_replace.len();
-        for node in nodes_to_replace {
-            self.edit_push_root(node);
-        }
+        // let n = nodes_to_replace.len();
+        // for node in nodes_to_replace {
+        //     self.mutations.push_root(node);
+        // }
 
-        let mut nodes_created = 0;
-        for node in new_nodes {
-            todo!();
-            // let meta = self.create_vnode(node);
-            // nodes_created += meta.added_to_stack;
-        }
+        // let mut nodes_created = 0;
+        // for node in new_nodes {
+        //     todo!();
+        // let meta = self.create_vnode(node);
+        // nodes_created += meta.added_to_stack;
+        // }
 
         // if 0 nodes are created, then it gets interperted as a deletion
-        self.edit_replace_with(n as u32, nodes_created);
+        // self.mutations.replace_with(n as u32, nodes_created);
+
+        // self.instructions.push(DiffInstruction::CreateChildren {
+        //     and: MountType::Replace { old: None },
+        //     children:
+        // });
 
         // obliterate!
         for scope in scopes_obliterated {
@@ -1411,12 +1392,12 @@ impl<'bump> DiffMachine<'bump> {
         old_node: &'bump VNode<'bump>,
         new_node: &'bump VNode<'bump>,
     ) {
-        self.edit_push_root(anchor);
+        self.mutations.push_root(anchor);
         todo!();
         // let meta = self.create_vnode(new_node);
-        // self.edit_replace_with(1, meta.added_to_stack);
+        // self.mutations.replace_with(1, meta.added_to_stack);
         // self.create_garbage(old_node);
-        self.edit_pop();
+        self.mutations.pop();
     }
 
     fn remove_vnode(&mut self, node: &'bump VNode<'bump>) {
@@ -1470,138 +1451,6 @@ impl<'bump> DiffMachine<'bump> {
         // if we have, then we're trying to alias it, which is not allowed
         unsafe { self.vdom.get_scope(*id) }
     }
-
-    // Navigation
-    pub(crate) fn edit_push_root(&mut self, root: ElementId) {
-        let id = root.as_u64();
-        self.mutations.edits.push(PushRoot { id });
-    }
-
-    pub(crate) fn edit_pop(&mut self) {
-        self.mutations.edits.push(PopRoot {});
-    }
-
-    // Add Nodes to the dom
-    // add m nodes from the stack
-    pub(crate) fn edit_append_children(&mut self, many: u32) {
-        self.mutations.edits.push(AppendChildren { many });
-    }
-
-    // replace the n-m node on the stack with the m nodes
-    // ends with the last element of the chain on the top of the stack
-    pub(crate) fn edit_replace_with(&mut self, n: u32, m: u32) {
-        self.mutations.edits.push(ReplaceWith { n, m });
-    }
-
-    pub(crate) fn edit_insert_after(&mut self, n: u32) {
-        self.mutations.edits.push(InsertAfter { n });
-    }
-
-    pub(crate) fn edit_insert_before(&mut self, n: u32) {
-        self.mutations.edits.push(InsertBefore { n });
-    }
-
-    // Remove Nodesfrom the dom
-    pub(crate) fn edit_remove(&mut self) {
-        self.mutations.edits.push(Remove);
-    }
-
-    // Create
-    pub(crate) fn edit_create_text_node(&mut self, text: &'bump str, id: ElementId) {
-        let id = id.as_u64();
-        self.mutations.edits.push(CreateTextNode { text, id });
-    }
-
-    pub(crate) fn edit_create_element(
-        &mut self,
-        tag: &'static str,
-        ns: Option<&'static str>,
-        id: ElementId,
-    ) {
-        let id = id.as_u64();
-        match ns {
-            Some(ns) => self.mutations.edits.push(CreateElementNs { id, ns, tag }),
-            None => self.mutations.edits.push(CreateElement { id, tag }),
-        }
-    }
-
-    // placeholders are nodes that don't get rendered but still exist as an "anchor" in the real dom
-    pub(crate) fn edit_create_placeholder(&mut self, id: ElementId) {
-        let id = id.as_u64();
-        self.mutations.edits.push(CreatePlaceholder { id });
-    }
-
-    // events
-    pub(crate) fn edit_new_event_listener(&mut self, listener: &Listener, scope: ScopeId) {
-        let Listener {
-            event,
-            mounted_node,
-            ..
-        } = listener;
-
-        let element_id = mounted_node.get().unwrap().as_u64();
-
-        self.mutations.edits.push(NewEventListener {
-            scope,
-            event_name: event,
-            mounted_node_id: element_id,
-        });
-    }
-
-    pub(crate) fn edit_remove_event_listener(&mut self, event: &'static str) {
-        self.mutations.edits.push(RemoveEventListener { event });
-    }
-
-    // modify
-    pub(crate) fn edit_set_text(&mut self, text: &'bump str) {
-        self.mutations.edits.push(SetText { text });
-    }
-
-    pub(crate) fn edit_set_attribute(&mut self, attribute: &'bump Attribute) {
-        let Attribute {
-            name,
-            value,
-            is_static,
-            is_volatile,
-            namespace,
-        } = attribute;
-        // field: &'static str,
-        // value: &'bump str,
-        // ns: Option<&'static str>,
-        self.mutations.edits.push(SetAttribute {
-            field: name,
-            value,
-            ns: *namespace,
-        });
-    }
-
-    pub(crate) fn edit_set_attribute_ns(
-        &mut self,
-        attribute: &'bump Attribute,
-        namespace: &'bump str,
-    ) {
-        let Attribute {
-            name,
-            value,
-            is_static,
-            is_volatile,
-            // namespace,
-            ..
-        } = attribute;
-        // field: &'static str,
-        // value: &'bump str,
-        // ns: Option<&'static str>,
-        self.mutations.edits.push(SetAttribute {
-            field: name,
-            value,
-            ns: Some(namespace),
-        });
-    }
-
-    pub(crate) fn edit_remove_attribute(&mut self, attribute: &Attribute) {
-        let name = attribute.name;
-        self.mutations.edits.push(RemoveAttribute { name });
-    }
 }
 
 // When we create new nodes, we need to propagate some information back up the call chain.
@@ -1664,6 +1513,14 @@ impl<'a> RealChildIterator<'a> {
             stack: smallvec::smallvec![(0, starter)],
         }
     }
+
+    pub fn new_from_slice(nodes: &'a [VNode<'a>], scopes: &'a SharedResources) -> Self {
+        let mut stack = smallvec::smallvec![];
+        for node in nodes {
+            stack.push((0, node));
+        }
+        Self { scopes, stack }
+    }
     // keep the memory around
     pub fn reset_with(&mut self, node: &'a VNode<'a>) {
         self.stack.clear();
@@ -1774,16 +1631,3 @@ fn compare_strs(a: &str, b: &str) -> bool {
         true
     }
 }
-
-struct DfsIterator<'a> {
-    idx: usize,
-    node: Option<(&'a VNode<'a>, &'a VNode<'a>)>,
-    nodes: Option<(&'a [VNode<'a>], &'a [VNode<'a>])>,
-}
-impl<'a> Iterator for DfsIterator<'a> {
-    type Item = (&'a VNode<'a>, &'a VNode<'a>);
-
-    fn next(&mut self) -> Option<Self::Item> {
-        todo!()
-    }
-}

+ 1 - 1
packages/core/src/editor.rs

@@ -9,7 +9,7 @@ use crate::innerlude::ScopeId;
 
 /// A `DomEdit` represents a serialzied form of the VirtualDom's trait-based API. This allows streaming edits across the
 /// network or through FFI boundaries.
-#[derive(Debug)]
+#[derive(Debug, PartialEq)]
 #[cfg_attr(
     feature = "serialize",
     derive(serde::Serialize, serde::Deserialize),

+ 5 - 23
packages/core/src/hooklist.rs

@@ -1,10 +1,10 @@
 use std::{
     any::Any,
-    cell::{Cell, UnsafeCell},
+    cell::{Cell, RefCell, UnsafeCell},
 };
 
 pub struct HookList {
-    vals: appendlist::AppendList<InnerHook<Box<dyn Any>>>,
+    vals: RefCell<Vec<InnerHook<Box<dyn Any>>>>,
     idx: Cell<usize>,
 }
 
@@ -27,35 +27,17 @@ impl<T> InnerHook<T> {
         }
     }
 }
-
 impl HookList {
-    /// Unsafely get a mutable reference to any of the hooks
-    ///
-    /// This is unsafe because an &mut T might be aliased if the hook data is already borrowed/in use in the component
-    ///
-    /// This method should be reserved for internal methods that are guaranteed that this hook is not aliased anyhwere
-    /// inside the component body, or outside into children components.
-    ///
-    /// This method is currently used only by the suspense system whose hook implementation guarantees that all &T is dropped
-    /// before the suspense handler is ran.
-    pub(crate) unsafe fn get_mut<T: 'static>(&self, idx: usize) -> Option<&mut T> {
-        self.vals.get(idx).and_then(|inn| {
-            let raw_box = unsafe { &mut *inn.cell.get() };
-            raw_box.downcast_mut::<T>()
-        })
-    }
-
     pub(crate) fn next<T: 'static>(&self) -> Option<&mut T> {
-        self.vals.get(self.idx.get()).and_then(|inn| {
+        self.vals.borrow().get(self.idx.get()).and_then(|inn| {
             self.idx.set(self.idx.get() + 1);
             let raw_box = unsafe { &mut *inn.cell.get() };
             raw_box.downcast_mut::<T>()
         })
     }
 
-    #[inline]
     pub(crate) fn push<T: 'static>(&self, new: T) {
-        self.vals.push(InnerHook::new(Box::new(new)))
+        self.vals.borrow_mut().push(InnerHook::new(Box::new(new)))
     }
 
     /// This resets the internal iterator count
@@ -70,7 +52,7 @@ impl HookList {
 
     #[inline]
     pub(crate) fn len(&self) -> usize {
-        self.vals.len()
+        self.vals.borrow().len()
     }
 
     #[inline]

+ 5 - 3
packages/core/src/lib.rs

@@ -12,15 +12,15 @@
 
 pub use crate::innerlude::{
     format_args_f, html, rsx, Context, DioxusElement, DomEdit, DomTree, ElementId, EventPriority,
-    EventTrigger, LazyNodes, NodeFactory, Properties, ScopeId, SuspendedContext, VNode, VirtualDom,
-    VirtualEvent, FC,
+    EventTrigger, LazyNodes, Mutations, NodeFactory, Properties, ScopeId, SuspendedContext, VNode,
+    VirtualDom, VirtualEvent, FC,
 };
 
 pub mod prelude {
     pub use crate::component::{fc_to_builder, Fragment, Properties};
     pub use crate::context::Context;
     pub use crate::hooks::*;
-    pub use crate::innerlude::{DioxusElement, DomTree, LazyNodes, NodeFactory, FC};
+    pub use crate::innerlude::{DioxusElement, DomTree, LazyNodes, Mutations, NodeFactory, FC};
     pub use crate::nodes::VNode;
     pub use crate::VirtualDom;
     pub use dioxus_core_macro::{format_args_f, html, rsx, Props};
@@ -39,6 +39,7 @@ pub(crate) mod innerlude {
     pub use crate::heuristics::*;
     pub use crate::hooklist::*;
     pub use crate::hooks::*;
+    pub use crate::mutations::*;
     pub use crate::nodes::*;
     pub use crate::scheduler::*;
     pub use crate::scope::*;
@@ -68,6 +69,7 @@ pub mod events;
 pub mod heuristics;
 pub mod hooklist;
 pub mod hooks;
+pub mod mutations;
 pub mod nodes;
 pub mod scheduler;
 pub mod scope;

+ 154 - 0
packages/core/src/mutations.rs

@@ -0,0 +1,154 @@
+use std::any::Any;
+
+use crate::innerlude::*;
+
+/// The "Mutations" object holds the changes that need to be made to the DOM.
+///
+#[derive(Debug)]
+pub struct Mutations<'s> {
+    pub edits: Vec<DomEdit<'s>>,
+    pub noderefs: Vec<NodeRefMutation<'s>>,
+}
+use DomEdit::*;
+
+impl<'bump> Mutations<'bump> {
+    pub fn new() -> Self {
+        let edits = Vec::new();
+        let noderefs = Vec::new();
+        Self { edits, noderefs }
+    }
+
+    pub fn extend(&mut self, other: &mut Mutations) {}
+
+    // Navigation
+    pub(crate) fn push_root(&mut self, root: ElementId) {
+        let id = root.as_u64();
+        self.edits.push(PushRoot { id });
+    }
+    pub(crate) fn pop(&mut self) {
+        self.edits.push(PopRoot {});
+    }
+    // replace the n-m node on the stack with the m nodes
+    // ends with the last element of the chain on the top of the stack
+    pub(crate) fn replace_with(&mut self, n: u32, m: u32) {
+        self.edits.push(ReplaceWith { n, m });
+    }
+    pub(crate) fn insert_after(&mut self, n: u32) {
+        self.edits.push(InsertAfter { n });
+    }
+    pub(crate) fn insert_before(&mut self, n: u32) {
+        self.edits.push(InsertBefore { n });
+    }
+    // Remove Nodesfrom the dom
+    pub(crate) fn remove(&mut self) {
+        self.edits.push(Remove);
+    }
+    // Create
+    pub(crate) fn create_text_node(&mut self, text: &'bump str, id: ElementId) {
+        let id = id.as_u64();
+        self.edits.push(CreateTextNode { text, id });
+    }
+    pub(crate) fn create_element(
+        &mut self,
+        tag: &'static str,
+        ns: Option<&'static str>,
+        id: ElementId,
+    ) {
+        let id = id.as_u64();
+        match ns {
+            Some(ns) => self.edits.push(CreateElementNs { id, ns, tag }),
+            None => self.edits.push(CreateElement { id, tag }),
+        }
+    }
+    // placeholders are nodes that don't get rendered but still exist as an "anchor" in the real dom
+    pub(crate) fn create_placeholder(&mut self, id: ElementId) {
+        let id = id.as_u64();
+        self.edits.push(CreatePlaceholder { id });
+    }
+    // events
+    pub(crate) fn new_event_listener(&mut self, listener: &Listener, scope: ScopeId) {
+        let Listener {
+            event,
+            mounted_node,
+            ..
+        } = listener;
+
+        let element_id = mounted_node.get().unwrap().as_u64();
+
+        self.edits.push(NewEventListener {
+            scope,
+            event_name: event,
+            mounted_node_id: element_id,
+        });
+    }
+    pub(crate) fn remove_event_listener(&mut self, event: &'static str) {
+        self.edits.push(RemoveEventListener { event });
+    }
+    // modify
+    pub(crate) fn set_text(&mut self, text: &'bump str) {
+        self.edits.push(SetText { text });
+    }
+    pub(crate) fn set_attribute(&mut self, attribute: &'bump Attribute) {
+        let Attribute {
+            name,
+            value,
+            is_static,
+            is_volatile,
+            namespace,
+        } = attribute;
+
+        self.edits.push(SetAttribute {
+            field: name,
+            value,
+            ns: *namespace,
+        });
+    }
+    pub(crate) fn set_attribute_ns(&mut self, attribute: &'bump Attribute, namespace: &'bump str) {
+        let Attribute {
+            name,
+            value,
+            is_static,
+            is_volatile,
+            ..
+        } = attribute;
+
+        self.edits.push(SetAttribute {
+            field: name,
+            value,
+            ns: Some(namespace),
+        });
+    }
+    pub(crate) fn remove_attribute(&mut self, attribute: &Attribute) {
+        let name = attribute.name;
+        self.edits.push(RemoveAttribute { name });
+    }
+}
+
+// refs are only assigned once
+pub struct NodeRefMutation<'a> {
+    element: &'a mut Option<once_cell::sync::OnceCell<Box<dyn Any>>>,
+    element_id: ElementId,
+}
+
+impl<'a> std::fmt::Debug for NodeRefMutation<'a> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("NodeRefMutation")
+            .field("element_id", &self.element_id)
+            .finish()
+    }
+}
+
+impl<'a> NodeRefMutation<'a> {
+    pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
+        self.element
+            .as_ref()
+            .and_then(|f| f.get())
+            .and_then(|f| f.downcast_ref::<T>())
+    }
+    pub fn downcast_mut<T: 'static>(&mut self) -> Option<&mut T> {
+        self.element
+            .as_mut()
+            .and_then(|f| f.get_mut())
+            .and_then(|f| f.downcast_mut::<T>())
+    }
+}

+ 31 - 19
packages/core/src/nodes.rs

@@ -164,6 +164,8 @@ pub struct VComponent<'src> {
 
     pub can_memoize: bool,
 
+    pub name: &'static str,
+
     // a pointer into the bump arena (given by the 'src lifetime)
     pub(crate) raw_props: *const (),
 
@@ -361,6 +363,8 @@ impl<'a> NodeFactory<'a> {
     {
         let bump = self.bump();
 
+        let name = crate::util::type_name_of(component);
+
         // We don't want the fat part of the fat pointer
         // This function does static dispatch so we don't need any VTable stuff
         let children: &'a V = bump.alloc(children);
@@ -426,6 +430,7 @@ impl<'a> NodeFactory<'a> {
             can_memoize: P::IS_STATIC,
             ass_scope: Cell::new(None),
             key,
+            name,
         }))
     }
 
@@ -454,6 +459,25 @@ impl<'a> NodeFactory<'a> {
     pub fn fragment_from_iter(self, node_iter: impl IntoVNodeList<'a>) -> VNode<'a> {
         let children = node_iter.into_vnode_list(self);
 
+        // TODO
+        // We need a dedicated path in the rsx! macro that will trigger the "you need keys" warning
+        //
+        // if cfg!(debug_assertions) {
+        //     if children.len() > 1 {
+        //         if children.last().unwrap().key().is_none() {
+        //             log::error!(
+        //                 r#"
+        // Warning: Each child in an array or iterator should have a unique "key" prop.
+        // Not providing a key will lead to poor performance with lists.
+        // See docs.rs/dioxus for more information.
+        // ---
+        // To help you identify where this error is coming from, we've generated a backtrace.
+        //                         "#,
+        //             );
+        //         }
+        //     }
+        // }
+
         VNode::Fragment(VFragment {
             children,
             key: None,
@@ -497,22 +521,6 @@ where
             nodes.push(node.into_vnode(cx));
         }
 
-        if cfg!(debug_assertions) {
-            if nodes.len() > 1 {
-                if nodes.last().unwrap().key().is_none() {
-                    log::error!(
-                        r#"
-        Warning: Each child in an array or iterator should have a unique "key" prop.
-        Not providing a key will lead to poor performance with lists.
-        See docs.rs/dioxus for more information.
-        ---
-        To help you identify where this error is coming from, we've generated a backtrace.
-                                "#,
-                    );
-                }
-            }
-        }
-
         if nodes.len() == 0 {
             nodes.push(VNode::Anchor(VAnchor {
                 dom_id: empty_cell(),
@@ -657,9 +665,13 @@ impl Debug for VNode<'_> {
             VNode::Text(t) => write!(s, "VText {{ text: {} }}", t.text),
             VNode::Anchor(a) => write!(s, "VAnchor"),
 
-            VNode::Fragment(_) => write!(s, "fragment"),
-            VNode::Suspended { .. } => write!(s, "suspended"),
-            VNode::Component(_) => write!(s, "component"),
+            VNode::Fragment(frag) => write!(s, "VFragment {{ children: {:?} }}", frag.children),
+            VNode::Suspended { .. } => write!(s, "VSuspended"),
+            VNode::Component(comp) => write!(
+                s,
+                "VComponent {{ fc: {:?}, children: {:?} }}",
+                comp.name, comp.children
+            ),
         }
     }
 }

+ 1 - 49
packages/core/src/scheduler.rs

@@ -33,58 +33,10 @@ use futures_util::pin_mut;
 use futures_util::Future;
 use futures_util::FutureExt;
 use futures_util::StreamExt;
-use indexmap::IndexSet;
 use smallvec::SmallVec;
 
 use crate::innerlude::*;
 
-/// The "Mutations" object holds the changes that need to be made to the DOM.
-///
-#[derive(Debug)]
-pub struct Mutations<'s> {
-    pub edits: Vec<DomEdit<'s>>,
-    pub noderefs: Vec<NodeRefMutation<'s>>,
-}
-
-impl<'s> Mutations<'s> {
-    pub fn new() -> Self {
-        let edits = Vec::new();
-        let noderefs = Vec::new();
-        Self { edits, noderefs }
-    }
-
-    pub fn extend(&mut self, other: &mut Mutations) {}
-}
-
-// refs are only assigned once
-pub struct NodeRefMutation<'a> {
-    element: &'a mut Option<once_cell::sync::OnceCell<Box<dyn Any>>>,
-    element_id: ElementId,
-}
-
-impl<'a> std::fmt::Debug for NodeRefMutation<'a> {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.debug_struct("NodeRefMutation")
-            .field("element_id", &self.element_id)
-            .finish()
-    }
-}
-
-impl<'a> NodeRefMutation<'a> {
-    pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
-        self.element
-            .as_ref()
-            .and_then(|f| f.get())
-            .and_then(|f| f.downcast_ref::<T>())
-    }
-    pub fn downcast_mut<T: 'static>(&mut self) -> Option<&mut T> {
-        self.element
-            .as_mut()
-            .and_then(|f| f.get_mut())
-            .and_then(|f| f.downcast_mut::<T>())
-    }
-}
-
 pub struct Scheduler {
     current_priority: EventPriority,
 
@@ -432,7 +384,7 @@ pub struct Waypoint {
 
 pub struct PriortySystem {
     pub pending_scopes: Vec<ScopeId>,
-    pub dirty_scopes: IndexSet<ScopeId>,
+    pub dirty_scopes: HashSet<ScopeId>,
 }
 
 impl PriortySystem {

+ 7 - 1
packages/core/src/scope.rs

@@ -43,6 +43,9 @@ pub struct Scope {
     pub(crate) hooks: HookList,
     pub(crate) shared_contexts: RefCell<HashMap<TypeId, Rc<dyn Any>>>,
 
+    // meta
+    pub(crate) function_name: &'static str,
+
     // A reference to the resources shared by all the comonents
     pub(crate) vdom: SharedResources,
 }
@@ -73,6 +76,8 @@ impl Scope {
         child_nodes: ScopeChildren,
 
         vdom: SharedResources,
+
+        function_name: &'static str,
     ) -> Self {
         let child_nodes = unsafe { child_nodes.extend_lifetime() };
 
@@ -84,6 +89,7 @@ impl Scope {
         }
 
         Self {
+            function_name,
             child_nodes,
             caller,
             parent_idx: parent,
@@ -141,7 +147,7 @@ impl Scope {
                 // the user's component succeeded. We can safely cycle to the next frame
                 self.frames.wip_frame_mut().head_node = unsafe { std::mem::transmute(new_head) };
                 self.frames.cycle_frame();
-                log::debug!("Cycle okay");
+                log::debug!("Successfully rendered component");
                 Ok(())
             }
         }

+ 4 - 0
packages/core/src/util.rs

@@ -8,6 +8,10 @@ pub fn empty_cell() -> Cell<Option<ElementId>> {
     Cell::new(None)
 }
 
+pub fn type_name_of<T>(_: T) -> &'static str {
+    std::any::type_name::<T>()
+}
+
 // /// A helper type that lets scopes be ordered by their height
 // #[derive(Debug, Clone, Copy, PartialEq, Eq)]
 // pub struct HeightMarker {

+ 26 - 23
packages/core/src/virtual_dom.rs

@@ -40,7 +40,7 @@ pub struct VirtualDom {
     ///
     /// This is wrapped in an UnsafeCell because we will need to get mutable access to unique values in unique bump arenas
     /// and rusts's guartnees cannot prove that this is safe. We will need to maintain the safety guarantees manually.
-    shared: SharedResources,
+    pub shared: SharedResources,
 
     /// The index of the root component
     /// Should always be the first (gen=0, id=0)
@@ -111,7 +111,8 @@ impl VirtualDom {
 
         let base_scope = components.insert_scope_with_key(move |myidx| {
             let caller = NodeFactory::create_component_caller(root, props_ptr as *const _);
-            Scope::new(caller, myidx, None, 0, ScopeChildren(&[]), link)
+            let name = type_name_of(root);
+            Scope::new(caller, myidx, None, 0, ScopeChildren(&[]), link, name)
         });
 
         Self {
@@ -150,23 +151,32 @@ impl VirtualDom {
     /// The diff machine expects the RealDom's stack to be the root of the application
     ///
     /// Events like garabge collection, application of refs, etc are not handled by this method and can only be progressed
-    /// through "run"
-    ///
+    /// through "run". We completely avoid the task scheduler infrastructure.
     pub fn rebuild<'s>(&'s mut self) -> Result<Mutations<'s>> {
+        let mut fut = self.rebuild_async().boxed_local();
+
+        loop {
+            if let Some(edits) = (&mut fut).now_or_never() {
+                break edits;
+            }
+        }
+    }
+
+    /// Rebuild the dom from the ground up
+    pub async fn rebuild_async<'s>(&'s mut self) -> Result<Mutations<'s>> {
         let mut diff_machine = DiffMachine::new(Mutations::new(), self.base_scope, &self.shared);
 
         let cur_component = diff_machine
             .get_scope_mut(&self.base_scope)
             .expect("The base scope should never be moved");
 
-        // todo!();
-
         // // We run the component. If it succeeds, then we can diff it and add the changes to the dom.
         if cur_component.run_scope().is_ok() {
             diff_machine.instructions.push(DiffInstruction::Create {
                 node: cur_component.frames.fin_head(),
                 and: MountType::Append,
             });
+            diff_machine.work().await.unwrap();
         } else {
             // todo: should this be a hard error?
             log::warn!(
@@ -178,33 +188,26 @@ impl VirtualDom {
         Ok(diff_machine.mutations)
     }
 
-    /// Rebuild the dom
-    pub async fn rebuild_async<'s>(&'s mut self) -> Result<Mutations<'s>> {
+    // diff the dom with itself
+    pub async fn diff_async<'s>(&'s mut self) -> Result<Mutations<'s>> {
         let mut diff_machine = DiffMachine::new(Mutations::new(), self.base_scope, &self.shared);
 
         let cur_component = diff_machine
             .get_scope_mut(&self.base_scope)
             .expect("The base scope should never be moved");
 
-        // todo!();
+        cur_component.run_scope().unwrap();
 
-        // // We run the component. If it succeeds, then we can diff it and add the changes to the dom.
-        if cur_component.run_scope().is_ok() {
-            diff_machine.instructions.push(DiffInstruction::Create {
-                node: cur_component.frames.fin_head(),
-                and: MountType::Append,
-            });
-            diff_machine.work().await.unwrap();
-        } else {
-            // todo: should this be a hard error?
-            log::warn!(
-                "Component failed to run succesfully during rebuild.
-                This does not result in a failed rebuild, but indicates a logic failure within your app."
-            );
-        }
+        diff_machine.instructions.push(DiffInstruction::DiffNode {
+            old: cur_component.frames.wip_head(),
+            new: cur_component.frames.fin_head(),
+        });
+
+        diff_machine.work().await.unwrap();
 
         Ok(diff_machine.mutations)
     }
+
     /// Runs the virtualdom immediately, not waiting for any suspended nodes to complete.
     ///
     /// This method will not wait for any suspended nodes to complete.

+ 1 - 3
packages/core/tests/iterative.rs → packages/core/tests/create_iterative.rs

@@ -104,9 +104,7 @@ async fn test_iterative_create_components() {
     static Child: FC<()> = |cx| {
         cx.render(rsx! {
             h1 {}
-            div {
-                {cx.children()}
-            }
+            div { {cx.children()} }
             p {}
         })
     };

+ 2 - 0
packages/core/tests/debugdiff.rs

@@ -0,0 +1,2 @@
+/// A virtualdom wrapper used for testing purposes.
+pub struct DebugDiff {}

+ 46 - 0
packages/core/tests/diff_iterative.rs

@@ -0,0 +1,46 @@
+//! tests to prove that the iterative implementation works
+
+use dioxus::prelude::*;
+
+mod test_logging;
+use dioxus_core as dioxus;
+use dioxus_html as dioxus_elements;
+
+#[async_std::test]
+async fn test_iterative_create_components() {
+    static App: FC<()> = |cx| {
+        // test root fragments
+        cx.render(rsx! {
+            Child { "abc1" }
+            Child { "abc2" }
+            Child { "abc3" }
+        })
+    };
+
+    fn Child(cx: Context<()>) -> DomTree {
+        // test root fragments, anchors, and ChildNode type
+        cx.render(rsx! {
+            h1 {}
+            div { {cx.children()} }
+            Fragment {
+                Fragment {
+                    Fragment {
+                        "wozza"
+                    }
+                }
+            }
+            {(0..0).map(|f| rsx!{ div { "walalla"}})}
+            p {}
+        })
+    }
+
+    test_logging::set_up_logging();
+
+    let mut dom = VirtualDom::new(App);
+
+    let mutations = dom.rebuild_async().await.unwrap();
+    dbg!(mutations);
+
+    let mutations = dom.diff_async().await.unwrap();
+    dbg!(mutations);
+}

+ 49 - 18
packages/core/tests/diffing.rs

@@ -9,12 +9,13 @@ use bumpalo::Bump;
 use anyhow::{Context, Result};
 use dioxus::{
     arena::SharedResources,
-    diff::{CreateMeta, DiffMachine},
+    diff::{CreateMeta, DiffInstruction, DiffMachine},
     prelude::*,
     DomEdit,
 };
 use dioxus_core as dioxus;
 use dioxus_html as dioxus_elements;
+use futures_util::FutureExt;
 
 struct TestDom {
     bump: Bump,
@@ -38,30 +39,41 @@ impl TestDom {
         lazy_nodes.into_vnode(NodeFactory::new(&self.bump))
     }
 
-    fn diff<'a>(&'a self, old: &'a VNode<'a>, new: &'a VNode<'a>) -> Vec<DomEdit<'a>> {
-        let mut edits = Vec::new();
+    fn diff<'a>(&'a self, old: &'a VNode<'a>, new: &'a VNode<'a>) -> Mutations<'a> {
+        // let mut edits = Vec::new();
         let mut machine = DiffMachine::new_headless(&self.resources);
-        machine.diff_node(old, new);
-        edits
+
+        machine
+            .instructions
+            .push(dioxus::diff::DiffInstruction::DiffNode { new, old });
+
+        machine.mutations
     }
 
-    fn create<'a, F1>(&'a self, left: LazyNodes<'a, F1>) -> (CreateMeta, Vec<DomEdit<'a>>)
+    fn create<'a, F1>(&'a self, left: LazyNodes<'a, F1>) -> Mutations<'a>
     where
         F1: FnOnce(NodeFactory<'a>) -> VNode<'a>,
     {
         let old = self.bump.alloc(self.render(left));
-        let mut edits = Vec::new();
 
         let mut machine = DiffMachine::new_headless(&self.resources);
-        let meta = machine.create_vnode(old);
-        (meta, edits)
+
+        machine
+            .instructions
+            .push(dioxus::diff::DiffInstruction::Create {
+                node: old,
+                and: dioxus::diff::MountType::Append,
+            });
+        work_sync(&mut machine);
+
+        machine.mutations
     }
 
     fn lazy_diff<'a, F1, F2>(
         &'a self,
         left: LazyNodes<'a, F1>,
         right: LazyNodes<'a, F2>,
-    ) -> (Vec<DomEdit<'a>>, Vec<DomEdit<'a>>)
+    ) -> (Mutations<'a>, Mutations<'a>)
     where
         F1: FnOnce(NodeFactory<'a>) -> VNode<'a>,
         F2: FnOnce(NodeFactory<'a>) -> VNode<'a>,
@@ -70,18 +82,37 @@ impl TestDom {
 
         let new = self.bump.alloc(self.render(right));
 
-        let mut create_edits = Vec::new();
+        // let mut create_edits = Vec::new();
 
         let mut machine = DiffMachine::new_headless(&self.resources);
-        machine.create_vnode(old);
+        machine
+            .instructions
+            .push(dioxus::diff::DiffInstruction::Create {
+                and: dioxus::diff::MountType::Append,
+                node: old,
+            });
+        work_sync(&mut machine);
+        let create_edits = machine.mutations;
 
-        let mut edits = Vec::new();
         let mut machine = DiffMachine::new_headless(&self.resources);
-        machine.diff_node(old, new);
+        machine
+            .instructions
+            .push(DiffInstruction::DiffNode { old, new });
+        work_sync(&mut machine);
+        let edits = machine.mutations;
+
         (create_edits, edits)
     }
 }
 
+fn work_sync(machine: &mut DiffMachine) {
+    let mut fut = machine.work().boxed_local();
+
+    while let None = (&mut fut).now_or_never() {
+        //
+    }
+}
+
 #[test]
 fn diffing_works() {}
 
@@ -100,7 +131,7 @@ fn html_and_rsx_generate_the_same_output() {
 #[test]
 fn fragments_create_properly() {
     let dom = TestDom::new();
-    let (meta, edits) = dom.create(rsx! {
+    let Mutations { edits, noderefs } = dom.create(rsx! {
         div { "Hello a" }
         div { "Hello b" }
         div { "Hello c" }
@@ -109,7 +140,7 @@ fn fragments_create_properly() {
     assert!(&edits[3].is("CreateElement"));
     assert!(&edits[6].is("CreateElement"));
 
-    assert_eq!(meta.added_to_stack, 3);
+    assert_eq!(*edits.last().unwrap(), DomEdit::AppendChildren { many: 3 });
     dbg!(edits);
 }
 
@@ -152,7 +183,7 @@ fn empty_fragments_create_anchors_with_many_children() {
 
     let edits = dom.lazy_diff(left, right);
     dbg!(&edits);
-    let last_edit = edits.1.last().unwrap();
+    let last_edit = edits.1.edits.last().unwrap();
     assert!(last_edit.is("ReplaceWith"));
 }
 
@@ -190,7 +221,7 @@ fn two_equal_fragments_are_equal() {
 
     let edits = dom.lazy_diff(left, right);
     dbg!(&edits);
-    assert!(edits.1.is_empty());
+    assert!(edits.1.edits.is_empty());
 }
 
 /// Should result the creation of more nodes appended after the old last node

+ 35 - 0
packages/core/tests/work_sync.rs

@@ -0,0 +1,35 @@
+//! Diffing is interruptible, but uses yield_now which is loop-pollable
+//!
+//! This means you can actually call it synchronously if you want.
+
+use anyhow::{Context, Result};
+use dioxus::{
+    arena::SharedResources,
+    diff::{CreateMeta, DiffInstruction, DiffMachine},
+    prelude::*,
+    scope::Scope,
+};
+use dioxus_core as dioxus;
+use dioxus_html as dioxus_elements;
+use futures_util::FutureExt;
+
+#[test]
+fn worksync() {
+    static App: FC<()> = |cx| {
+        cx.render(rsx! {
+            div {"hello"}
+        })
+    };
+    let mut dom = VirtualDom::new(App);
+
+    let mut fut = dom.rebuild_async().boxed_local();
+
+    let mutations = loop {
+        let g = (&mut fut).now_or_never();
+        if g.is_some() {
+            break g.unwrap().unwrap();
+        }
+    };
+
+    dbg!(mutations);
+}