|
@@ -54,7 +54,7 @@
|
|
|
//! We also employ "subtree memoization" which saves us from having to check trees which hold no dynamic content. We can
|
|
|
//! detect if a subtree is "static" by checking if its children are "static". Since we dive into the tree depth-first, the
|
|
|
//! calls to "create" propagate this information upwards. Structures like the one below are entirely static:
|
|
|
-//! ```rust
|
|
|
+//! ```rust, ignore
|
|
|
//! rsx!( div { class: "hello world", "this node is entirely static" } )
|
|
|
//! ```
|
|
|
//! Because the subtrees won't be diffed, their "real node" data will be stale (invalid), so it's up to the reconciler to
|
|
@@ -147,6 +147,7 @@ pub(crate) enum DiffInstruction<'a> {
|
|
|
},
|
|
|
|
|
|
PopScope,
|
|
|
+ PopElement,
|
|
|
}
|
|
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
@@ -162,30 +163,32 @@ pub(crate) struct DiffStack<'bump> {
|
|
|
pub(crate) instructions: Vec<DiffInstruction<'bump>>,
|
|
|
nodes_created_stack: SmallVec<[usize; 10]>,
|
|
|
pub scope_stack: SmallVec<[ScopeId; 5]>,
|
|
|
+ pub element_stack: SmallVec<[ElementId; 10]>,
|
|
|
}
|
|
|
|
|
|
impl<'bump> DiffStack<'bump> {
|
|
|
- pub fn new() -> Self {
|
|
|
+ fn new() -> Self {
|
|
|
Self {
|
|
|
instructions: Vec::with_capacity(1000),
|
|
|
nodes_created_stack: smallvec![],
|
|
|
scope_stack: smallvec![],
|
|
|
+ element_stack: smallvec![],
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- pub fn pop(&mut self) -> Option<DiffInstruction<'bump>> {
|
|
|
+ fn pop(&mut self) -> Option<DiffInstruction<'bump>> {
|
|
|
self.instructions.pop()
|
|
|
}
|
|
|
|
|
|
- pub fn pop_off_scope(&mut self) {
|
|
|
+ fn pop_off_scope(&mut self) {
|
|
|
self.scope_stack.pop();
|
|
|
}
|
|
|
|
|
|
- pub fn push(&mut self, instruction: DiffInstruction<'bump>) {
|
|
|
+ pub(crate) fn push(&mut self, instruction: DiffInstruction<'bump>) {
|
|
|
self.instructions.push(instruction)
|
|
|
}
|
|
|
|
|
|
- pub fn create_children(&mut self, children: &'bump [VNode<'bump>], and: MountType<'bump>) {
|
|
|
+ fn create_children(&mut self, children: &'bump [VNode<'bump>], and: MountType<'bump>) {
|
|
|
self.nodes_created_stack.push(0);
|
|
|
self.instructions.push(DiffInstruction::Mount { and });
|
|
|
|
|
@@ -195,36 +198,36 @@ impl<'bump> DiffStack<'bump> {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- pub fn push_subtree(&mut self) {
|
|
|
+ fn push_subtree(&mut self) {
|
|
|
self.nodes_created_stack.push(0);
|
|
|
self.instructions.push(DiffInstruction::Mount {
|
|
|
and: MountType::Append,
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- pub fn push_nodes_created(&mut self, count: usize) {
|
|
|
+ fn push_nodes_created(&mut self, count: usize) {
|
|
|
self.nodes_created_stack.push(count);
|
|
|
}
|
|
|
|
|
|
- pub fn create_node(&mut self, node: &'bump VNode<'bump>, and: MountType<'bump>) {
|
|
|
+ pub(crate) fn create_node(&mut self, node: &'bump VNode<'bump>, and: MountType<'bump>) {
|
|
|
self.nodes_created_stack.push(0);
|
|
|
self.instructions.push(DiffInstruction::Mount { and });
|
|
|
self.instructions.push(DiffInstruction::Create { node });
|
|
|
}
|
|
|
|
|
|
- pub fn add_child_count(&mut self, count: usize) {
|
|
|
+ fn add_child_count(&mut self, count: usize) {
|
|
|
*self.nodes_created_stack.last_mut().unwrap() += count;
|
|
|
}
|
|
|
|
|
|
- pub fn pop_nodes_created(&mut self) -> usize {
|
|
|
+ fn pop_nodes_created(&mut self) -> usize {
|
|
|
self.nodes_created_stack.pop().unwrap()
|
|
|
}
|
|
|
|
|
|
- pub fn current_scope(&self) -> Option<ScopeId> {
|
|
|
+ fn current_scope(&self) -> Option<ScopeId> {
|
|
|
self.scope_stack.last().copied()
|
|
|
}
|
|
|
|
|
|
- pub fn create_component(&mut self, idx: ScopeId, node: &'bump VNode<'bump>) {
|
|
|
+ fn create_component(&mut self, idx: ScopeId, node: &'bump VNode<'bump>) {
|
|
|
// Push the new scope onto the stack
|
|
|
self.scope_stack.push(idx);
|
|
|
|
|
@@ -263,10 +266,13 @@ impl<'bump> DiffState<'bump> {
|
|
|
self.stack.add_child_count(num_on_stack);
|
|
|
}
|
|
|
DiffInstruction::PopScope => self.stack.pop_off_scope(),
|
|
|
+ DiffInstruction::PopElement => {
|
|
|
+ self.stack.element_stack.pop();
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
if deadline_expired() {
|
|
|
- log::debug!("Deadline expired before we could finished!");
|
|
|
+ log::debug!("Deadline expired before we could finish!");
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
@@ -390,11 +396,18 @@ impl<'bump> DiffState<'bump> {
|
|
|
children,
|
|
|
namespace,
|
|
|
dom_id,
|
|
|
+ parent_id,
|
|
|
..
|
|
|
} = element;
|
|
|
|
|
|
- let real_id = self.scopes.reserve_node(node);
|
|
|
+ // set the parent ID for event bubbling
|
|
|
+ self.stack.instructions.push(DiffInstruction::PopElement);
|
|
|
+ let parent = self.stack.element_stack.last().unwrap();
|
|
|
+ parent_id.set(Some(*parent));
|
|
|
|
|
|
+ // set the id of the element
|
|
|
+ let real_id = self.scopes.reserve_node(node);
|
|
|
+ self.stack.element_stack.push(real_id);
|
|
|
dom_id.set(Some(real_id));
|
|
|
|
|
|
self.mutations.create_element(tag_name, *namespace, real_id);
|
|
@@ -438,9 +451,11 @@ impl<'bump> DiffState<'bump> {
|
|
|
let caller = unsafe { std::mem::transmute(vcomponent.caller as *const _) };
|
|
|
let fc_ptr = vcomponent.user_fc;
|
|
|
|
|
|
- let new_idx = self
|
|
|
- .scopes
|
|
|
- .new_with_key(fc_ptr, caller, parent_scope, height, subtree);
|
|
|
+ let container = *self.stack.element_stack.last().unwrap();
|
|
|
+
|
|
|
+ let new_idx =
|
|
|
+ self.scopes
|
|
|
+ .new_with_key(fc_ptr, caller, parent_scope, container, height, subtree);
|
|
|
|
|
|
// Actually initialize the caller's slot with the right address
|
|
|
vcomponent.associated_scope.set(Some(new_idx));
|
|
@@ -650,20 +665,18 @@ impl<'bump> DiffState<'bump> {
|
|
|
let scope = unsafe {
|
|
|
self.scopes
|
|
|
.get_scope_mut(&scope_addr)
|
|
|
- .expect(&format!("could not find {:?}", scope_addr))
|
|
|
+ .unwrap_or_else(|| panic!("could not find {:?}", scope_addr))
|
|
|
};
|
|
|
scope.caller = unsafe { std::mem::transmute(new.caller) };
|
|
|
|
|
|
// React doesn't automatically memoize, but we do.
|
|
|
let props_are_the_same = old.comparator.unwrap();
|
|
|
|
|
|
- if self.force_diff || !props_are_the_same(new) {
|
|
|
- if self.scopes.run_scope(&scope_addr) {
|
|
|
- self.diff_node(
|
|
|
- self.scopes.wip_head(&scope_addr),
|
|
|
- self.scopes.fin_head(&scope_addr),
|
|
|
- );
|
|
|
- }
|
|
|
+ if (self.force_diff || !props_are_the_same(new)) && self.scopes.run_scope(&scope_addr) {
|
|
|
+ self.diff_node(
|
|
|
+ self.scopes.wip_head(&scope_addr),
|
|
|
+ self.scopes.fin_head(&scope_addr),
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
self.stack.scope_stack.pop();
|
|
@@ -1206,13 +1219,19 @@ impl<'bump> DiffState<'bump> {
|
|
|
fn replace_node(&mut self, old: &'bump VNode<'bump>, nodes_created: usize) {
|
|
|
match old {
|
|
|
VNode::Element(el) => {
|
|
|
- let id = old.try_mounted_id().expect(&format!("broke on {:?}", old));
|
|
|
+ let id = old
|
|
|
+ .try_mounted_id()
|
|
|
+ .unwrap_or_else(|| panic!("broke on {:?}", old));
|
|
|
+
|
|
|
self.mutations.replace_with(id, nodes_created as u32);
|
|
|
self.remove_nodes(el.children, false);
|
|
|
}
|
|
|
|
|
|
VNode::Text(_) | VNode::Anchor(_) | VNode::Suspended(_) => {
|
|
|
- let id = old.try_mounted_id().expect(&format!("broke on {:?}", old));
|
|
|
+ let id = old
|
|
|
+ .try_mounted_id()
|
|
|
+ .unwrap_or_else(|| panic!("broke on {:?}", old));
|
|
|
+
|
|
|
self.mutations.replace_with(id, nodes_created as u32);
|
|
|
}
|
|
|
|
|
@@ -1226,7 +1245,6 @@ impl<'bump> DiffState<'bump> {
|
|
|
self.replace_node(node, nodes_created);
|
|
|
|
|
|
let scope_id = c.associated_scope.get().unwrap();
|
|
|
- println!("replacing c {:?} ", scope_id);
|
|
|
log::debug!("Destroying scope {:?}", scope_id);
|
|
|
self.scopes.try_remove(&scope_id).unwrap();
|
|
|
}
|