Merge branch 'upstream' into fix-non-str-attributes

This commit is contained in:
Evan Almloff committed 2022-12-23 15:53:39 -06:00
commit afd024bcb6
106 files changed
+1415 -2115

No files matched your search

+16 -9
View File
@@ -81,12 +81,12 @@ impl VirtualDom {
self.ensure_drop_safety(id);
if let Some(root) = self.scopes[id.0].as_ref().try_root_node() {
if let RenderReturn::Sync(Ok(node)) = unsafe { root.extend_lifetime_ref() } {
if let RenderReturn::Sync(Some(node)) = unsafe { root.extend_lifetime_ref() } {
self.drop_scope_inner(node)
}
}
if let Some(root) = unsafe { self.scopes[id.0].as_ref().previous_frame().try_load_node() } {
if let RenderReturn::Sync(Ok(node)) = unsafe { root.extend_lifetime_ref() } {
if let RenderReturn::Sync(Some(node)) = unsafe { root.extend_lifetime_ref() } {
self.drop_scope_inner(node)
}
}
@@ -115,10 +115,14 @@ impl VirtualDom {
nodes.iter().for_each(|node| self.drop_scope_inner(node))
}
DynamicNode::Placeholder(t) => {
self.try_reclaim(t.id.get().unwrap());
if let Some(id) = t.id.get() {
self.try_reclaim(id);
}
}
DynamicNode::Text(t) => {
self.try_reclaim(t.id.get().unwrap());
if let Some(id) = t.id.get() {
self.try_reclaim(id);
}
}
});
@@ -132,8 +136,8 @@ impl VirtualDom {
}
/// Descend through the tree, removing any borrowed props and listeners
pub(crate) fn ensure_drop_safety(&self, scope: ScopeId) {
let scope = &self.scopes[scope.0];
pub(crate) fn ensure_drop_safety(&self, scope_id: ScopeId) {
let scope = &self.scopes[scope_id.0];
// make sure we drop all borrowed props manually to guarantee that their drop implementation is called before we
// run the hooks (which hold an &mut Reference)
@@ -141,10 +145,13 @@ impl VirtualDom {
let mut props = scope.borrowed_props.borrow_mut();
props.drain(..).for_each(|comp| {
let comp = unsafe { &*comp };
if let Some(scope_id) = comp.scope.get() {
self.ensure_drop_safety(scope_id);
match comp.scope.get() {
Some(child) if child != scope_id => self.ensure_drop_safety(child),
_ => (),
}
if let Ok(mut props) = comp.props.try_borrow_mut() {
*props = None;
}
drop(comp.props.take());
});
// Now that all the references are gone, we can safely drop our own references in our listeners.
+2 -2
View File
@@ -380,8 +380,8 @@ impl<'b> VirtualDom {
use RenderReturn::*;
match return_nodes {
Sync(Ok(t)) => self.mount_component(scope, template, t, idx),
Sync(Err(_e)) => todo!("Propogate error upwards"),
Sync(Some(t)) => self.mount_component(scope, template, t, idx),
Sync(None) => todo!("Propogate error upwards"),
Async(_) => self.mount_component_placeholder(template, idx, scope),
}
}
+114 -66
View File
@@ -33,26 +33,26 @@ impl<'b> VirtualDom {
use RenderReturn::{Async, Sync};
match (old, new) {
(Sync(Ok(l)), Sync(Ok(r))) => self.diff_node(l, r),
(Sync(Some(l)), Sync(Some(r))) => self.diff_node(l, r),
// Err cases
(Sync(Ok(l)), Sync(Err(e))) => self.diff_ok_to_err(l, e),
(Sync(Err(e)), Sync(Ok(r))) => self.diff_err_to_ok(e, r),
(Sync(Err(_eo)), Sync(Err(_en))) => { /* nothing */ }
(Sync(Some(l)), Sync(None)) => self.diff_ok_to_err(l),
(Sync(None), Sync(Some(r))) => self.diff_err_to_ok(r),
(Sync(None), Sync(None)) => { /* nothing */ }
// Async
(Sync(Ok(_l)), Async(_)) => todo!(),
(Sync(Err(_e)), Async(_)) => todo!(),
(Async(_), Sync(Ok(_r))) => todo!(),
(Async(_), Sync(Err(_e))) => { /* nothing */ }
(Sync(Some(_l)), Async(_)) => todo!(),
(Sync(None), Async(_)) => todo!(),
(Async(_), Sync(Some(_r))) => todo!(),
(Async(_), Sync(None)) => { /* nothing */ }
(Async(_), Async(_)) => { /* nothing */ }
};
}
self.scope_stack.pop();
}
fn diff_ok_to_err(&mut self, _l: &'b VNode<'b>, _e: &anyhow::Error) {}
fn diff_err_to_ok(&mut self, _e: &anyhow::Error, _l: &'b VNode<'b>) {}
fn diff_ok_to_err(&mut self, _l: &'b VNode<'b>) {}
fn diff_err_to_ok(&mut self, _l: &'b VNode<'b>) {}
fn diff_node(&mut self, left_template: &'b VNode<'b>, right_template: &'b VNode<'b>) {
// If the templates are the same, we don't need to do anything, nor do we want to
@@ -149,22 +149,7 @@ impl<'b> VirtualDom {
// Replace components that have different render fns
if left.render_fn != right.render_fn {
let created = self.create_component_node(right_template, right, idx);
let head = unsafe {
self.scopes[left.scope.get().unwrap().0]
.root_node()
.extend_lifetime_ref()
};
let last = match head {
RenderReturn::Sync(Ok(node)) => self.find_last_element(node),
_ => todo!(),
};
self.mutations.push(Mutation::InsertAfter {
id: last,
m: created,
});
self.remove_component_node(left, true);
return;
return self.replace_vcomponent(right_template, right, idx, left);
}
// Make sure the new vcomponent has the right scopeid associated to it
@@ -197,6 +182,26 @@ impl<'b> VirtualDom {
});
}
fn replace_vcomponent(
&mut self,
right_template: &'b VNode<'b>,
right: &'b VComponent<'b>,
idx: usize,
left: &'b VComponent<'b>,
) {
let m = self.create_component_node(right_template, right, idx);
self.remove_component_node(left, true);
// We want to optimize the replace case to use one less mutation if possible
// Since mutations are done in reverse, the last node removed will be the first in the stack
// Instead of *just* removing it, we can use the replace mutation
match self.mutations.edits.pop().unwrap() {
Mutation::Remove { id } => self.mutations.push(Mutation::ReplaceWith { id, m }),
at => panic!("Expected remove mutation from remove_node {:#?}", at),
};
}
/// Lightly diff the two templates, checking only their roots.
///
/// The goal here is to preserve any existing component state that might exist. This is to preserve some React-like
@@ -668,7 +673,7 @@ impl<'b> VirtualDom {
Component(comp) => {
let scope = comp.scope.get().unwrap();
match unsafe { self.scopes[scope.0].root_node().extend_lifetime_ref() } {
RenderReturn::Sync(Ok(node)) => self.push_all_real_nodes(node),
RenderReturn::Sync(Some(node)) => self.push_all_real_nodes(node),
_ => todo!(),
}
}
@@ -706,11 +711,21 @@ impl<'b> VirtualDom {
fn replace(&mut self, left: &'b VNode<'b>, right: impl IntoIterator<Item = &'b VNode<'b>>) {
let m = self.create_children(right);
let id = self.find_last_element(left);
self.mutations.push(Mutation::InsertAfter { id, m });
let pre_edits = self.mutations.edits.len();
self.remove_node(left, true);
// We should always have a remove mutation
// Eventually we don't want to generate placeholders, so this might not be true. But it's true today
assert!(self.mutations.edits.len() > pre_edits);
// We want to optimize the replace case to use one less mutation if possible
// Since mutations are done in reverse, the last node removed will be the first in the stack
// Instead of *just* removing it, we can use the replace mutation
match self.mutations.edits.pop().unwrap() {
Mutation::Remove { id } => self.mutations.push(Mutation::ReplaceWith { id, m }),
_ => panic!("Expected remove mutation from remove_node"),
};
}
fn node_to_placeholder(&mut self, l: &'b [VNode<'b>], r: &'b VPlaceholder) {
@@ -719,24 +734,45 @@ impl<'b> VirtualDom {
r.id.set(Some(placeholder));
let id = self.find_last_element(&l[0]);
self.mutations
.push(Mutation::CreatePlaceholder { id: placeholder });
self.mutations.push(Mutation::InsertAfter { id, m: 1 });
self.remove_nodes(l);
// We want to optimize the replace case to use one less mutation if possible
// Since mutations are done in reverse, the last node removed will be the first in the stack
// Instead of *just* removing it, we can use the replace mutation
match self.mutations.edits.pop().unwrap() {
Mutation::Remove { id } => self.mutations.push(Mutation::ReplaceWith { id, m: 1 }),
_ => panic!("Expected remove mutation from remove_node"),
};
}
/// Remove these nodes from the dom
/// Wont generate mutations for the inner nodes
fn remove_nodes(&mut self, nodes: &'b [VNode<'b>]) {
nodes.iter().for_each(|node| self.remove_node(node, true));
nodes
.iter()
.rev()
.for_each(|node| self.remove_node(node, true));
}
fn remove_node(&mut self, node: &'b VNode<'b>, gen_muts: bool) {
// Clean up any attributes that have claimed a static node as dynamic for mount/unmounta
// Will not generate mutations!
self.reclaim_attributes(node);
// Remove the nested dynamic nodes
// We don't generate mutations for these, as they will be removed by the parent (in the next line)
// But we still need to make sure to reclaim them from the arena and drop their hooks, etc
self.remove_nested_dyn_nodes(node);
// Clean up the roots, assuming we need to generate mutations for these
// This is done last in order to preserve Node ID reclaim order (reclaim in reverse order of claim)
self.reclaim_roots(node, gen_muts);
}
fn reclaim_roots(&mut self, node: &VNode, gen_muts: bool) {
for (idx, _) in node.template.roots.iter().enumerate() {
if let Some(dy) = node.dynamic_root(idx) {
self.remove_dynamic_node(dy, gen_muts);
@@ -748,17 +784,9 @@ impl<'b> VirtualDom {
self.reclaim(id);
}
}
}
for (idx, dyn_node) in node.dynamic_nodes.iter().enumerate() {
// Roots are cleaned up automatically above
if node.template.node_paths[idx].len() == 1 {
continue;
}
self.remove_dynamic_node(dyn_node, false);
}
// we clean up nodes with dynamic attributes, provided the node is unique and not a root node
fn reclaim_attributes(&mut self, node: &VNode) {
let mut id = None;
for (idx, attr) in node.dynamic_attrs.iter().enumerate() {
// We'll clean up the root nodes either way, so don't worry
@@ -778,49 +806,69 @@ impl<'b> VirtualDom {
}
}
fn remove_nested_dyn_nodes(&mut self, node: &VNode) {
for (idx, dyn_node) in node.dynamic_nodes.iter().enumerate() {
// Roots are cleaned up automatically above
if node.template.node_paths[idx].len() == 1 {
continue;
}
self.remove_dynamic_node(dyn_node, false);
}
}
fn remove_dynamic_node(&mut self, node: &DynamicNode, gen_muts: bool) {
match node {
Component(comp) => self.remove_component_node(comp, gen_muts),
Text(t) => self.remove_text_node(t),
Placeholder(t) => self.remove_placeholder(t),
Text(t) => self.remove_text_node(t, gen_muts),
Placeholder(t) => self.remove_placeholder(t, gen_muts),
Fragment(nodes) => nodes
.iter()
.for_each(|node| self.remove_node(node, gen_muts)),
};
}
fn remove_placeholder(&mut self, t: &VPlaceholder) {
fn remove_placeholder(&mut self, t: &VPlaceholder, gen_muts: bool) {
if let Some(id) = t.id.take() {
if gen_muts {
self.mutations.push(Mutation::Remove { id });
}
self.reclaim(id)
}
}
fn remove_text_node(&mut self, t: &VText) {
fn remove_text_node(&mut self, t: &VText, gen_muts: bool) {
if let Some(id) = t.id.take() {
if gen_muts {
self.mutations.push(Mutation::Remove { id });
}
self.reclaim(id)
}
}
fn remove_component_node(&mut self, comp: &VComponent, gen_muts: bool) {
if let Some(scope) = comp.scope.take() {
match unsafe { self.scopes[scope.0].root_node().extend_lifetime_ref() } {
RenderReturn::Sync(Ok(t)) => self.remove_node(t, gen_muts),
_ => todo!("cannot handle nonstandard nodes"),
};
let scope = comp.scope.take().unwrap();
let props = self.scopes[scope.0].props.take();
match unsafe { self.scopes[scope.0].root_node().extend_lifetime_ref() } {
RenderReturn::Sync(Some(t)) => {
println!("Removing component node sync {:?}", gen_muts);
self.remove_node(t, gen_muts)
}
_ => todo!("cannot handle nonstandard nodes"),
};
self.dirty_scopes.remove(&DirtyScope {
height: self.scopes[scope.0].height,
id: scope,
});
let props = self.scopes[scope.0].props.take();
*comp.props.borrow_mut() = unsafe { std::mem::transmute(props) };
self.dirty_scopes.remove(&DirtyScope {
height: self.scopes[scope.0].height,
id: scope,
});
// make sure to wipe any of its props and listeners
self.ensure_drop_safety(scope);
self.scopes.remove(scope.0);
}
*comp.props.borrow_mut() = unsafe { std::mem::transmute(props) };
// make sure to wipe any of its props and listeners
self.ensure_drop_safety(scope);
self.scopes.remove(scope.0);
}
fn find_first_element(&self, node: &'b VNode<'b>) -> ElementId {
@@ -832,7 +880,7 @@ impl<'b> VirtualDom {
Some(Component(comp)) => {
let scope = comp.scope.get().unwrap();
match unsafe { self.scopes[scope.0].root_node().extend_lifetime_ref() } {
RenderReturn::Sync(Ok(t)) => self.find_first_element(t),
RenderReturn::Sync(Some(t)) => self.find_first_element(t),
_ => todo!("cannot handle nonstandard nodes"),
}
}
@@ -848,7 +896,7 @@ impl<'b> VirtualDom {
Some(Component(comp)) => {
let scope = comp.scope.get().unwrap();
match unsafe { self.scopes[scope.0].root_node().extend_lifetime_ref() } {
RenderReturn::Sync(Ok(t)) => self.find_last_element(t),
RenderReturn::Sync(Some(t)) => self.find_last_element(t),
_ => todo!("cannot handle nonstandard nodes"),
}
}
+1 -1
View File
@@ -5,7 +5,7 @@ use crate::ScopeId;
/// A boundary that will capture any errors from child components
#[allow(dead_code)]
pub struct ErrorBoundary {
error: RefCell<Option<(anyhow::Error, ScopeId)>>,
error: RefCell<Option<ScopeId>>,
id: ScopeId,
}
+3 -3
View File
@@ -27,8 +27,8 @@ use crate::innerlude::*;
/// You want to use this free-function when your fragment needs a key and simply returning multiple nodes from rsx! won't cut it.
#[allow(non_upper_case_globals, non_snake_case)]
pub fn Fragment<'a>(cx: Scope<'a, FragmentProps<'a>>) -> Element {
let children = cx.props.0.as_ref().map_err(|e| anyhow::anyhow!("{}", e))?;
Ok(VNode {
let children = cx.props.0.as_ref()?;
Some(VNode {
key: children.key,
parent: children.parent,
template: children.template,
@@ -95,7 +95,7 @@ impl<'a> Properties for FragmentProps<'a> {
type Builder = FragmentBuilder<'a, false>;
const IS_STATIC: bool = false;
fn builder() -> Self::Builder {
FragmentBuilder(VNode::empty())
FragmentBuilder(None)
}
unsafe fn memoize(&self, _other: &Self) -> bool {
false
+2 -2
View File
@@ -34,10 +34,10 @@ pub(crate) mod innerlude {
pub use crate::scopes::*;
pub use crate::virtual_dom::*;
/// An [`Element`] is a possibly-errored [`VNode`] created by calling `render` on [`Scope`] or [`ScopeState`].
/// An [`Element`] is a possibly-none [`VNode`] created by calling `render` on [`Scope`] or [`ScopeState`].
///
/// An Errored [`Element`] will propagate the error to the nearest error boundary.
pub type Element<'a> = Result<VNode<'a>, anyhow::Error>;
pub type Element<'a> = Option<VNode<'a>>;
/// A [`Component`] is a function that takes a [`Scope`] and returns an [`Element`].
///
+3 -13
View File
@@ -58,7 +58,7 @@ pub struct VNode<'a> {
impl<'a> VNode<'a> {
/// Create a template with no nodes that will be skipped over during diffing
pub fn empty() -> Element<'a> {
Ok(VNode {
Some(VNode {
key: None,
parent: None,
root_ids: &[],
@@ -602,16 +602,6 @@ impl<'a> IntoDynNode<'a> for DynamicNode<'a> {
}
}
// An element that's an error is currently lost into the ether
impl<'a> IntoDynNode<'a> for Element<'a> {
fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
match self {
Ok(val) => val.into_vnode(_cx),
_ => DynamicNode::default(),
}
}
}
impl<'a, T: IntoDynNode<'a>> IntoDynNode<'a> for Option<T> {
fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
match self {
@@ -624,7 +614,7 @@ impl<'a, T: IntoDynNode<'a>> IntoDynNode<'a> for Option<T> {
impl<'a> IntoDynNode<'a> for &Element<'a> {
fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
match self.as_ref() {
Ok(val) => val.clone().into_vnode(_cx),
Some(val) => val.clone().into_vnode(_cx),
_ => DynamicNode::default(),
}
}
@@ -678,7 +668,7 @@ impl<'a> IntoTemplate<'a> for VNode<'a> {
impl<'a> IntoTemplate<'a> for Element<'a> {
fn into_template(self, _cx: &'a ScopeState) -> VNode<'a> {
match self {
Ok(val) => val.into_template(_cx),
Some(val) => val.into_template(_cx),
_ => VNode::empty().unwrap(),
}
}
+1 -1
View File
@@ -78,7 +78,7 @@ impl VirtualDom {
fiber.waiting_on.borrow_mut().remove(&id);
if let RenderReturn::Sync(Ok(template)) = ret {
if let RenderReturn::Sync(Some(template)) = ret {
let mutations_ref = &mut fiber.mutations.borrow_mut();
let mutations = &mut **mutations_ref;
let template: &VNode = unsafe { std::mem::transmute(template) };
+1 -1
View File
@@ -390,7 +390,7 @@ impl<'src> ScopeState {
}
}
Ok(element)
Some(element)
}
/// Create a dynamic text node using [`Arguments`] and the [`ScopeState`]'s internal [`Bump`] allocator
+2 -2
View File
@@ -477,7 +477,7 @@ impl VirtualDom {
pub fn rebuild(&mut self) -> Mutations {
match unsafe { self.run_scope(ScopeId(0)).extend_lifetime_ref() } {
// Rebuilding implies we append the created elements to the root
RenderReturn::Sync(Ok(node)) => {
RenderReturn::Sync(Some(node)) => {
let m = self.create_scope(ScopeId(0), node);
self.mutations.edits.push(Mutation::AppendChildren {
id: ElementId(0),
@@ -485,7 +485,7 @@ impl VirtualDom {
});
}
// If an error occurs, we should try to render the default error component and context where the error occured
RenderReturn::Sync(Err(e)) => panic!("Cannot catch errors during rebuild {:?}", e),
RenderReturn::Sync(None) => panic!("Cannot catch errors during rebuild"),
RenderReturn::Async(_) => unreachable!("Root scope cannot be an async component"),
}