polish: tests and documentation

This commit is contained in:
Jonathan Kelley committed 2021-09-09 20:58:48 -04:00
1 parent 0896f34493
commit da8159190b
27 files changed
+467 -569

No files matched your search

+59 -117
View File
@@ -124,6 +124,7 @@ impl<'a> SavedDiffWork<'a> {
pub unsafe fn extend(self: SavedDiffWork<'a>) -> SavedDiffWork<'static> {
std::mem::transmute(self)
}
pub unsafe fn promote<'b>(self, vdom: &'b mut ResourcePool) -> DiffMachine<'b> {
let extended: SavedDiffWork<'b> = std::mem::transmute(self);
DiffMachine {
@@ -136,10 +137,10 @@ impl<'a> SavedDiffWork<'a> {
}
impl<'bump> DiffMachine<'bump> {
pub(crate) fn new(edits: Mutations<'bump>, shared: &'bump ResourcePool) -> Self {
pub(crate) fn new(mutations: Mutations<'bump>, shared: &'bump ResourcePool) -> Self {
Self {
mutations,
stack: DiffStack::new(),
mutations: edits,
vdom: shared,
seen_scopes: FxHashSet::default(),
}
@@ -156,7 +157,7 @@ impl<'bump> DiffMachine<'bump> {
pub fn diff_scope(&mut self, id: ScopeId) {
if let Some(component) = self.vdom.get_scope_mut(id) {
let (old, new) = (component.frames.wip_head(), component.frames.fin_head());
self.stack.push(DiffInstruction::DiffNode { new, old });
self.stack.push(DiffInstruction::Diff { new, old });
}
}
@@ -167,24 +168,14 @@ impl<'bump> DiffMachine<'bump> {
/// We do depth-first to maintain high cache locality (nodes were originally generated recursively).
///
/// Returns a `bool` indicating that the work completed properly.
pub fn work(&mut self, deadline_expired: &mut impl FnMut() -> bool) -> bool {
pub fn work(&mut self, mut deadline_expired: impl FnMut() -> bool) -> bool {
while let Some(instruction) = self.stack.pop() {
// defer to individual functions so the compiler produces better code
// large functions tend to be difficult for the compiler to work with
match instruction {
DiffInstruction::PopScope => {
self.stack.pop_scope();
}
DiffInstruction::DiffNode { old, new, .. } => self.diff_node(old, new),
DiffInstruction::DiffChildren { old, new } => self.diff_children(old, new),
DiffInstruction::Diff { old, new } => self.diff_node(old, new),
DiffInstruction::Create { node } => self.create_node(node),
DiffInstruction::Mount { and } => self.mount(and),
DiffInstruction::PrepareMoveNode { node } => self.prepare_move_node(node),
DiffInstruction::PrepareMove { node } => self.prepare_move_node(node),
DiffInstruction::PopScope => self.stack.pop_off_scope(),
};
if deadline_expired() {
@@ -217,15 +208,21 @@ impl<'bump> DiffMachine<'bump> {
}
MountType::Replace { old } => {
let mut iter = RealChildIterator::new(old, self.vdom);
let first = iter.next().unwrap();
self.mutations
.replace_with(first.mounted_id(), nodes_created as u32);
self.remove_nodes(iter);
if let Some(old_id) = old.try_mounted_id() {
self.mutations.replace_with(old_id, nodes_created as u32);
} else {
let mut iter = RealChildIterator::new(old, self.vdom);
let first = iter.next().unwrap();
self.mutations
.replace_with(first.mounted_id(), nodes_created as u32);
self.remove_nodes(iter);
}
}
MountType::ReplaceByElementId { el: old } => {
self.mutations.replace_with(old, nodes_created as u32);
MountType::ReplaceByElementId { el } => {
if let Some(old) = el {
self.mutations.replace_with(old, nodes_created as u32);
}
}
MountType::InsertAfter { other_node } => {
@@ -326,6 +323,7 @@ impl<'bump> DiffMachine<'bump> {
let parent_idx = self.stack.current_scope().unwrap();
let shared = self.vdom.channel.clone();
// Insert a new scope into our component list
let parent_scope = self.vdom.get_scope(parent_idx).unwrap();
let new_idx = self.vdom.insert_scope_with_key(|new_idx| {
@@ -376,7 +374,9 @@ impl<'bump> DiffMachine<'bump> {
match (old_node, new_node) {
// Check the most common cases first
(Text(old), Text(new)) => self.diff_text_nodes(old, new),
(Component(old), Component(new)) => self.diff_component_nodes(old, new),
(Component(old), Component(new)) => {
self.diff_component_nodes(old_node, new_node, 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.dom_id.set(old.dom_id.get()),
@@ -393,19 +393,19 @@ impl<'bump> DiffMachine<'bump> {
}
fn diff_text_nodes(&mut self, old: &'bump VText<'bump>, new: &'bump VText<'bump>) {
let root = old.dom_id.get().unwrap();
if let Some(root) = old.dom_id.get() {
if old.text != new.text {
self.mutations.push_root(root);
self.mutations.set_text(new.text);
self.mutations.pop();
}
if old.text != new.text {
self.mutations.push_root(root);
self.mutations.set_text(new.text);
self.mutations.pop();
new.dom_id.set(Some(root));
}
new.dom_id.set(Some(root));
}
fn diff_element_nodes(&mut self, old: &'bump VElement<'bump>, new: &'bump VElement<'bump>) {
let root = old.dom_id.get().unwrap();
let root = old.dom_id.get();
// If the element type is completely different, the element needs to be re-rendered completely
// This is an optimization React makes due to how users structure their code
@@ -417,21 +417,23 @@ impl<'bump> DiffMachine<'bump> {
self.stack.push_nodes_created(0);
self.stack.push(DiffInstruction::Mount {
and: MountType::ReplaceByElementId {
el: old.dom_id.get().unwrap(),
el: old.dom_id.get(),
},
});
self.create_element_node(new);
return;
}
new.dom_id.set(Some(root));
new.dom_id.set(root);
// Don't push the root if we don't have to
let mut has_comitted = false;
let mut please_commit = |edits: &mut Vec<DomEdit>| {
if !has_comitted {
has_comitted = true;
edits.push(PushRoot { id: root.as_u64() });
if let Some(root) = root {
edits.push(PushRoot { id: root.as_u64() });
}
}
};
@@ -449,7 +451,6 @@ impl<'bump> DiffMachine<'bump> {
}
}
} 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.mutations.remove_attribute(attribute);
@@ -467,7 +468,6 @@ impl<'bump> DiffMachine<'bump> {
// We also need to make sure that all listeners are properly attached to the parent scope (fix_listener)
//
// TODO: take a more efficient path than this
if let Some(cur_scope_id) = self.stack.current_scope() {
let scope = self.vdom.get_scope(cur_scope_id).unwrap();
@@ -487,22 +487,21 @@ impl<'bump> DiffMachine<'bump> {
self.mutations.remove_event_listener(listener.event);
}
for listener in new.listeners {
listener.mounted_node.set(Some(root));
listener.mounted_node.set(root);
self.mutations.new_event_listener(listener, cur_scope_id);
self.attach_listener_to_scope(listener, scope);
}
}
}
if has_comitted {
self.mutations.pop();
}
self.diff_children(old.children, new.children);
}
fn diff_component_nodes(
&mut self,
old_node: &'bump VNode<'bump>,
new_node: &'bump VNode<'bump>,
old: &'bump VComponent<'bump>,
new: &'bump VComponent<'bump>,
) {
@@ -526,7 +525,7 @@ impl<'bump> DiffMachine<'bump> {
match compare(new) {
true => {
// the props are the same...
// the props are the same... do nothing
}
false => {
// the props are different...
@@ -540,25 +539,8 @@ impl<'bump> DiffMachine<'bump> {
self.seen_scopes.insert(scope_addr);
} else {
todo!();
// let mut old_iter = RealChildIterator::new(old_node, &self.vdom);
// let first = old_iter
// .next()
// .expect("Components should generate a placeholder root");
// // remove any leftovers
// for to_remove in old_iter {
// self.mutations.push_root(to_remove.direct_id());
// self.mutations.remove();
// }
// // seems like we could combine this into a single instruction....
// self.replace_node_with_node(first.direct_id(), old_node, new_node);
// // Wipe the old one and plant the new one
// let old_scope = old.ass_scope.get().unwrap();
// self.destroy_scopes(old_scope);
self.stack
.create_node(new_node, MountType::Replace { old: old_node });
}
}
@@ -608,7 +590,7 @@ impl<'bump> DiffMachine<'bump> {
old_anchor.dom_id.set(new_anchor.dom_id.get());
}
([VNode::Anchor(anchor)], _) => {
let el = anchor.dom_id.get().unwrap();
let el = anchor.dom_id.get();
self.stack
.create_children(new, MountType::ReplaceByElementId { el });
}
@@ -652,7 +634,7 @@ impl<'bump> DiffMachine<'bump> {
debug_assert!(!old.is_empty());
for (new, old) in new.iter().zip(old.iter()).rev() {
self.stack.push(DiffInstruction::DiffNode { new, old });
self.stack.push(DiffInstruction::Diff { new, old });
}
if old.len() > new.len() {
@@ -748,7 +730,7 @@ impl<'bump> DiffMachine<'bump> {
if old.key() != new.key() {
break;
}
self.stack.push(DiffInstruction::DiffNode { old, new });
self.stack.push(DiffInstruction::Diff { old, new });
left_offset += 1;
}
@@ -887,8 +869,8 @@ impl<'bump> DiffMachine<'bump> {
stack.create_node(new_node, MountType::Absorb);
} else {
// this funciton should never take LIS indicies
stack.push(DiffInstruction::PrepareMoveNode { node: new_node });
stack.push(DiffInstruction::DiffNode {
stack.push(DiffInstruction::PrepareMove { node: new_node });
stack.push(DiffInstruction::Diff {
new: new_node,
old: &old[old_index],
});
@@ -943,7 +925,7 @@ impl<'bump> DiffMachine<'bump> {
}
for idx in lis_sequence.iter().rev() {
self.stack.push(DiffInstruction::DiffNode {
self.stack.push(DiffInstruction::Diff {
new: &new[*idx],
old: &old[new_index_to_old_index[*idx]],
});
@@ -998,6 +980,14 @@ impl<'bump> DiffMachine<'bump> {
}
}
fn replace_and_create_one_with_many(
&mut self,
old: &'bump VNode<'bump>,
new: &'bump [VNode<'bump>],
) {
//
}
fn replace_and_create_many_with_one(
&mut self,
old: &'bump [VNode<'bump>],
@@ -1070,60 +1060,12 @@ impl<'bump> DiffMachine<'bump> {
}
}
fn create_garbage(&mut self, node: &'bump VNode<'bump>) {
match self
.stack
.current_scope()
.and_then(|id| self.vdom.get_scope(id))
{
Some(scope) => {
let garbage: &'bump VNode<'static> = unsafe { std::mem::transmute(node) };
scope.pending_garbage.borrow_mut().push(garbage);
}
None => {
log::info!("No scope to collect garbage into")
}
}
}
/// Adds a listener closure to a scope during diff.
fn attach_listener_to_scope<'a>(&mut self, listener: &'a Listener<'a>, scope: &Scope) {
let mut queue = scope.listeners.borrow_mut();
let long_listener: &'a Listener<'static> = unsafe { std::mem::transmute(listener) };
queue.push(long_listener as *const _)
}
/// Destroy a scope and all of its descendents.
///
/// Calling this will run the destuctors on all hooks in the tree.
/// It will also add the destroyed nodes to the `seen_nodes` cache to prevent them from being renderered.
fn destroy_scopes(&mut self, old_scope: ScopeId) {
let mut nodes_to_delete = vec![old_scope];
let mut scopes_to_explore = vec![old_scope];
// explore the scope tree breadth first
while let Some(scope_id) = scopes_to_explore.pop() {
// If we're planning on deleting this node, then we don't need to both rendering it
self.seen_scopes.insert(scope_id);
let scope = self.vdom.get_scope(scope_id).unwrap();
for child in scope.descendents.borrow().iter() {
// Add this node to be explored
scopes_to_explore.push(child.clone());
// Also add it for deletion
nodes_to_delete.push(child.clone());
}
}
// Delete all scopes that we found as part of this subtree
for node in nodes_to_delete {
log::debug!("Removing scope {:#?}", node);
let _scope = self.vdom.try_remove(node).unwrap();
// do anything we need to do to delete the scope
// I think we need to run the destructors on the hooks
// TODO
}
}
}
fn compare_strs(a: &str, b: &str) -> bool {