wip: adjust memoization

This commit is contained in:
Jonathan Kelley committed 2021-12-20 22:33:13 -05:00
1 parent 934de21dd6
commit e2e4d431e1
15 files changed
+932 -703

No files matched your search

+85 -108
View File
@@ -239,15 +239,6 @@ impl<'bump> DiffStack<'bump> {
}
impl<'bump> DiffState<'bump> {
pub fn diff_scope(&mut self, id: ScopeId) {
let (old, new) = (self.scopes.wip_head(id), self.scopes.fin_head(id));
self.stack.push(DiffInstruction::Diff { old, new });
self.stack.scope_stack.push(id);
let scope = self.scopes.get_scope(id).unwrap();
self.stack.element_stack.push(scope.container);
self.work(|| false);
}
/// Progress the diffing for this "fiber"
///
/// This method implements a depth-first iterative tree traversal.
@@ -415,7 +406,7 @@ impl<'bump> DiffState<'bump> {
}
// todo: the settext optimization
//
// if children.len() == 1 {
// if let VNode::Text(vtext) = children[0] {
// self.mutations.set_text(vtext.text, real_id.as_u64());
@@ -436,40 +427,32 @@ impl<'bump> DiffState<'bump> {
let parent_idx = self.stack.current_scope().unwrap();
// Insert a new scope into our component list
let parent_scope = self.scopes.get_scope(parent_idx).unwrap();
let height = parent_scope.height + 1;
let subtree = parent_scope.subtree.get();
let parent_scope = self.scopes.get_scope_raw(parent_idx);
let caller = unsafe { std::mem::transmute(vcomponent.caller as *const _) };
let fc_ptr = vcomponent.user_fc;
let container = *self.stack.element_stack.last().unwrap();
let new_idx =
self.scopes
.new_with_key(fc_ptr, caller, parent_scope, container, height, subtree);
let caller = unsafe { std::mem::transmute(vcomponent.props as *const _) };
let new_idx = self.scopes.new_with_key(
vcomponent.user_fc,
caller,
Some(parent_idx),
self.stack.element_stack.last().copied().unwrap(),
0,
);
// Actually initialize the caller's slot with the right address
vcomponent.associated_scope.set(Some(new_idx));
vcomponent.scope.set(Some(new_idx));
if !vcomponent.can_memoize {
let cur_scope = self.scopes.get_scope(parent_idx).unwrap();
let extended = unsafe { std::mem::transmute(vcomponent) };
cur_scope.items.borrow_mut().borrowed_props.push(extended);
} else {
// the props are currently bump allocated but we need to move them to the heap
match vcomponent.can_memoize {
true => {
// promote the props onto the heap
// when we diff these nodes, we'll want to move it over
vcomponent.props.promote();
}
false => {
// track this component internally so we know the right drop order
let cur_scope = self.scopes.get_scope(parent_idx).unwrap();
let extended = unsafe { std::mem::transmute(vcomponent) };
cur_scope.items.borrow_mut().borrowed_props.push(extended);
}
}
// TODO: add noderefs to current noderef list Noderefs
let _new_component = self.scopes.get_scope(new_idx).unwrap();
log::debug!(
"initializing component {:?} with height {:?}",
new_idx,
height + 1
);
// Run the scope for one iteration to initialize it
self.scopes.run_scope(new_idx);
@@ -477,11 +460,6 @@ impl<'bump> DiffState<'bump> {
let nextnode = self.scopes.fin_head(new_idx);
self.stack.create_component(new_idx, nextnode);
// todo: subtrees
// if new_component.is_subtree_root.get() {
// self.stack.push_subtree();
// }
// Finally, insert this scope as a seen node.
self.mutations.dirty_scopes.insert(new_idx);
}
@@ -494,15 +472,23 @@ impl<'bump> DiffState<'bump> {
use VNode::*;
match (old_node, new_node) {
// Check the most common cases first
// these are *actual* elements, not wrappers around lists
(Text(old), Text(new)) => {
self.diff_text_nodes(old, new, old_node, new_node);
}
(Element(old), Element(new)) => self.diff_element_nodes(old, new, old_node, new_node),
(Placeholder(old), Placeholder(new)) => {
if let Some(root) = old.dom_id.get() {
self.scopes.update_node(new_node, root);
new.dom_id.set(Some(root))
}
}
// These two sets are pointers to nodes but are not actually nodes themselves
(Component(old), Component(new)) => {
self.diff_component_nodes(old_node, new_node, *old, *new)
}
(Fragment(old), Fragment(new)) => self.diff_fragment_nodes(old, new),
(Placeholder(old), Placeholder(new)) => self.diff_placeholder_nodes(old, new),
(Element(old), Element(new)) => self.diff_element_nodes(old, new, old_node, new_node),
// The normal pathway still works, but generates slightly weird instructions
// This pathway just ensures we get create and replace
@@ -708,21 +694,14 @@ impl<'bump> DiffState<'bump> {
old: &'bump VComponent<'bump>,
new: &'bump VComponent<'bump>,
) {
let scope_addr = old.associated_scope.get().unwrap();
log::debug!(
"Diffing components. old_scope: {:?}, old_addr: {:?}, new_addr: {:?}",
scope_addr,
old.user_fc,
new.user_fc
);
let scope_addr = old.scope.get().unwrap();
// Make sure we're dealing with the same component (by function pointer)
if old.user_fc == new.user_fc {
self.stack.scope_stack.push(scope_addr);
// Make sure the new component vnode is referencing the right scope id
new.associated_scope.set(Some(scope_addr));
new.scope.set(Some(scope_addr));
// make sure the component's caller function is up to date
let scope = self
@@ -730,12 +709,45 @@ impl<'bump> DiffState<'bump> {
.get_scope(scope_addr)
.unwrap_or_else(|| panic!("could not find {:?}", scope_addr));
scope.caller.set(unsafe { std::mem::transmute(new.caller) });
/*
old.props is the previous vcomponent
existing_props is the current ScopeState's
*/
let existing_props = unsafe { &*scope.render.get() };
if old.can_memoize {
// let props_are_the_same = unsafe { existing_props.memoize(new.props) };
// React doesn't automatically memoize, but we do.
let props_are_the_same = old.comparator.unwrap();
if !props_are_the_same || self.force_diff {
// release the old props since they will be overwritten next frame
// existing_props.release();
if self.force_diff || !props_are_the_same(new) {
// move the old props onto the heap
// todo: maybe we can reuse the old props box?
new.props.promote();
scope
.render
.set(unsafe { std::mem::transmute(new.props as *const dyn AnyProps) });
// this should auto drop the previous props
self.scopes.run_scope(scope_addr);
self.diff_node(
self.scopes.wip_head(scope_addr),
self.scopes.fin_head(scope_addr),
);
} else {
// release the old props since they will be overwritten next frame
if existing_props.as_ptr() != old.props.as_ptr() {
old.props.release();
}
}
} else {
scope
.render
.set(unsafe { std::mem::transmute(new.props as *const dyn AnyProps) });
// this should auto drop the previous props
self.scopes.run_scope(scope_addr);
self.diff_node(
self.scopes.wip_head(scope_addr),
@@ -764,11 +776,6 @@ impl<'bump> DiffState<'bump> {
self.diff_children(old.children, new.children);
}
// probably will get inlined
fn diff_placeholder_nodes(&mut self, old: &'bump VPlaceholder, new: &'bump VPlaceholder) {
new.dom_id.set(old.dom_id.get())
}
// =============================================
// Utilities for creating new diff instructions
// =============================================
@@ -792,33 +799,8 @@ impl<'bump> DiffState<'bump> {
// Remember, fragments can never be empty (they always have a single child)
match (old, new) {
([], []) => {}
([], _) => {
// we need to push the
self.stack.create_children(new, MountType::Append);
}
(_, []) => {
self.remove_nodes(old, true);
}
// todo: placeholders explicitly replace fragments
//
// ([VNode::Placeholder(old_anchor)], [VNode::Placeholder(new_anchor)]) => {
// old_anchor.dom_id.set(new_anchor.dom_id.get());
// }
// ([VNode::Placeholder(_)], _) => {
// self.stack
// .create_children(new, MountType::Replace { old: &old[0] });
// }
// (_, [VNode::Placeholder(_)]) => {
// let new: &'bump VNode<'bump> = &new[0];
// if let Some(first_old) = old.get(0) {
// self.remove_nodes(&old[1..], true);
// self.stack
// .create_node(new, MountType::Replace { old: first_old });
// } else {
// self.stack.create_node(new, MountType::Append {});
// }
// }
([], _) => self.stack.create_children(new, MountType::Append),
(_, []) => self.remove_nodes(old, true),
_ => {
let new_is_keyed = new[0].key().is_some();
let old_is_keyed = old[0].key().is_some();
@@ -1216,7 +1198,7 @@ impl<'bump> DiffState<'bump> {
search_node = frag.children.last();
}
VNode::Component(el) => {
let scope_id = el.associated_scope.get().unwrap();
let scope_id = el.scope.get().unwrap();
search_node = Some(self.scopes.root_node(scope_id));
}
}
@@ -1233,7 +1215,7 @@ impl<'bump> DiffState<'bump> {
search_node = Some(&frag.children[0]);
}
VNode::Component(el) => {
let scope_id = el.associated_scope.get().unwrap();
let scope_id = el.scope.get().unwrap();
search_node = Some(self.scopes.root_node(scope_id));
}
VNode::Text(t) => break t.dom_id.get(),
@@ -1250,8 +1232,6 @@ impl<'bump> DiffState<'bump> {
.try_mounted_id()
.unwrap_or_else(|| panic!("broke on {:?}", old));
log::debug!("element parent is {:?}", el.parent_id.get());
self.mutations.replace_with(id, nodes_created as u32);
self.remove_nodes(el.children, false);
}
@@ -1270,11 +1250,11 @@ impl<'bump> DiffState<'bump> {
}
VNode::Component(c) => {
let node = self.scopes.fin_head(c.associated_scope.get().unwrap());
let node = self.scopes.fin_head(c.scope.get().unwrap());
self.replace_node(node, nodes_created);
let scope_id = c.associated_scope.get().unwrap();
log::debug!("Destroying scope {:?}", scope_id);
let scope_id = c.scope.get().unwrap();
self.scopes.try_remove(scope_id).unwrap();
}
}
@@ -1282,7 +1262,7 @@ impl<'bump> DiffState<'bump> {
/// schedules nodes for garbage collection and pushes "remove" to the mutation stack
/// remove can happen whenever
fn remove_nodes(
pub(crate) fn remove_nodes(
&mut self,
nodes: impl IntoIterator<Item = &'bump VNode<'bump>>,
gen_muts: bool,
@@ -1323,20 +1303,17 @@ impl<'bump> DiffState<'bump> {
}
VNode::Component(c) => {
self.destroy_vomponent(c, gen_muts);
let scope_id = c.scope.get().unwrap();
let root = self.scopes.root_node(scope_id);
self.remove_nodes(Some(root), gen_muts);
log::debug!("Destroying scope {:?}", scope_id);
self.scopes.try_remove(scope_id).unwrap();
}
}
}
}
fn destroy_vomponent(&mut self, vc: &VComponent, gen_muts: bool) {
let scope_id = vc.associated_scope.get().unwrap();
let root = self.scopes.root_node(scope_id);
self.remove_nodes(Some(root), gen_muts);
log::debug!("Destroying scope {:?}", scope_id);
self.scopes.try_remove(scope_id).unwrap();
}
/// Adds a listener closure to a scope during diff.
fn attach_listener_to_scope(&mut self, listener: &'bump Listener<'bump>, scope: &ScopeState) {
let long_listener = unsafe { std::mem::transmute(listener) };