remove a lot of unsafe

This commit is contained in:
Evan Almloff committed 2024-01-05 10:57:05 -06:00
1 parent c70e2bfcb6
commit 0c76770da0
12 files changed
+201 -363

No files matched your search

+23 -11
View File
@@ -1,5 +1,5 @@
use crate::{nodes::RenderReturn, Element}; use crate::{nodes::RenderReturn, Element};
use std::{ops::Deref, panic::AssertUnwindSafe}; use std::{any::Any, ops::Deref, panic::AssertUnwindSafe};
/// A boxed version of AnyProps that can be cloned /// A boxed version of AnyProps that can be cloned
pub(crate) struct BoxedAnyProps { pub(crate) struct BoxedAnyProps {
@@ -7,7 +7,7 @@ pub(crate) struct BoxedAnyProps {
} }
impl BoxedAnyProps { impl BoxedAnyProps {
fn new(inner: impl AnyProps + 'static) -> Self { pub fn new(inner: impl AnyProps + 'static) -> Self {
Self { Self {
inner: Box::new(inner), inner: Box::new(inner),
} }
@@ -33,7 +33,8 @@ impl Clone for BoxedAnyProps {
/// A trait that essentially allows VComponentProps to be used generically /// A trait that essentially allows VComponentProps to be used generically
pub(crate) trait AnyProps { pub(crate) trait AnyProps {
fn render<'a>(&'a self) -> RenderReturn; fn render<'a>(&'a self) -> RenderReturn;
fn memoize(&self, other: &dyn AnyProps) -> bool; fn memoize(&self, other: &dyn Any) -> bool;
fn props(&self) -> &dyn Any;
fn duplicate(&self) -> Box<dyn AnyProps>; fn duplicate(&self) -> Box<dyn AnyProps>;
} }
@@ -41,25 +42,35 @@ pub(crate) struct VProps<P> {
pub render_fn: fn(P) -> Element, pub render_fn: fn(P) -> Element,
pub memo: fn(&P, &P) -> bool, pub memo: fn(&P, &P) -> bool,
pub props: P, pub props: P,
pub name: &'static str,
} }
impl<P> VProps<P> { impl<P> VProps<P> {
pub(crate) fn new(render_fn: fn(P) -> Element, memo: fn(&P, &P) -> bool, props: P) -> Self { pub(crate) fn new(
render_fn: fn(P) -> Element,
memo: fn(&P, &P) -> bool,
props: P,
name: &'static str,
) -> Self {
Self { Self {
render_fn, render_fn,
memo, memo,
props, props,
name,
} }
} }
} }
impl<P: Clone> AnyProps for VProps<P> { impl<P: Clone> AnyProps for VProps<P> {
// Safety: fn memoize(&self, other: &dyn Any) -> bool {
// this will downcast the other ptr as our swallowed type! match other.downcast_ref::<Self>() {
// you *must* make this check *before* calling this method Some(other) => (self.memo)(&self.props, &other.props),
// if your functions are not the same, then you will downcast a pointer into a different type (UB) None => false,
fn memoize(&self, other: &dyn AnyProps) -> bool { }
(self.memo)(self, other) }
fn props(&self) -> &dyn Any {
&self.props
} }
fn render(&self) -> RenderReturn { fn render(&self) -> RenderReturn {
@@ -72,7 +83,7 @@ impl<P: Clone> AnyProps for VProps<P> {
Ok(Some(e)) => RenderReturn::Ready(e), Ok(Some(e)) => RenderReturn::Ready(e),
Ok(None) => RenderReturn::default(), Ok(None) => RenderReturn::default(),
Err(err) => { Err(err) => {
let component_name = cx.name(); let component_name = self.name;
tracing::error!("Error while rendering component `{component_name}`: {err:?}"); tracing::error!("Error while rendering component `{component_name}`: {err:?}");
RenderReturn::default() RenderReturn::default()
} }
@@ -84,6 +95,7 @@ impl<P: Clone> AnyProps for VProps<P> {
render_fn: self.render_fn, render_fn: self.render_fn,
memo: self.memo, memo: self.memo,
props: self.props.clone(), props: self.props.clone(),
name: self.name,
}) })
} }
} }
+5 -20
View File
@@ -1,5 +1,3 @@
use std::ptr::NonNull;
use crate::{ use crate::{
innerlude::DirtyScope, nodes::RenderReturn, nodes::VNode, virtual_dom::VirtualDom, DynamicNode, innerlude::DirtyScope, nodes::RenderReturn, nodes::VNode, virtual_dom::VirtualDom, DynamicNode,
ScopeId, ScopeId,
@@ -21,16 +19,16 @@ pub struct ElementId(pub usize);
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct VNodeId(pub usize); pub struct VNodeId(pub usize);
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone)]
pub struct ElementRef { pub struct ElementRef {
// the pathway of the real element inside the template // the pathway of the real element inside the template
pub(crate) path: ElementPath, pub(crate) path: ElementPath,
// The actual template // the scope that this element belongs to
pub(crate) template: VNodeId,
// The scope the element belongs to
pub(crate) scope: ScopeId, pub(crate) scope: ScopeId,
// The actual element
pub(crate) element: VNode,
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
@@ -43,14 +41,6 @@ impl VirtualDom {
ElementId(self.elements.insert(None)) ElementId(self.elements.insert(None))
} }
pub(crate) fn next_vnode_ref(&mut self, vnode: &VNode) -> VNodeId {
let new_id = VNodeId(self.element_refs.insert(Some(unsafe {
std::mem::transmute::<NonNull<VNode>, _>(vnode.into())
})));
new_id
}
pub(crate) fn reclaim(&mut self, el: ElementId) { pub(crate) fn reclaim(&mut self, el: ElementId) {
self.try_reclaim(el) self.try_reclaim(el)
.unwrap_or_else(|| panic!("cannot reclaim {:?}", el)); .unwrap_or_else(|| panic!("cannot reclaim {:?}", el));
@@ -67,11 +57,6 @@ impl VirtualDom {
self.elements.try_remove(el.0).map(|_| ()) self.elements.try_remove(el.0).map(|_| ())
} }
pub(crate) fn set_template(&mut self, id: VNodeId, vnode: &VNode) {
self.element_refs[id.0] =
Some(unsafe { std::mem::transmute::<NonNull<VNode>, _>(vnode.into()) });
}
// Drop a scope and all its children // Drop a scope and all its children
// //
// Note: This will not remove any ids from the arena // Note: This will not remove any ids from the arena
+22 -33
View File
@@ -1,7 +1,4 @@
use crate::any_props::AnyProps; use crate::innerlude::{ElementPath, ElementRef, VComponent, VPlaceholder, VText};
use crate::innerlude::{
BorrowedAttributeValue, ElementPath, ElementRef, VComponent, VPlaceholder, VText,
};
use crate::mutations::Mutation; use crate::mutations::Mutation;
use crate::mutations::Mutation::*; use crate::mutations::Mutation::*;
use crate::nodes::VNode; use crate::nodes::VNode;
@@ -96,9 +93,6 @@ impl VirtualDom {
nodes_mut.resize(len, ElementId::default()); nodes_mut.resize(len, ElementId::default());
}; };
// Set this node id
node.stable_id.set(Some(self.next_vnode_ref(node)));
// The best renderers will have templates prehydrated and registered // The best renderers will have templates prehydrated and registered
// Just in case, let's create the template using instructions anyways // Just in case, let's create the template using instructions anyways
self.register_template(node.template.get()); self.register_template(node.template.get());
@@ -190,7 +184,7 @@ impl VirtualDom {
path: ElementPath { path: ElementPath {
path: template.template.get().node_paths[idx], path: template.template.get().node_paths[idx],
}, },
template: template.stable_id().unwrap(), element: template.clone(),
scope: self.runtime.current_scope_id().unwrap_or(ScopeId(0)), scope: self.runtime.current_scope_id().unwrap_or(ScopeId(0)),
}; };
self.create_dynamic_node(template_ref, node) self.create_dynamic_node(template_ref, node)
@@ -200,10 +194,10 @@ impl VirtualDom {
path: ElementPath { path: ElementPath {
path: template.template.get().node_paths[idx], path: template.template.get().node_paths[idx],
}, },
template: template.stable_id().unwrap(), element: template.clone(),
scope: self.runtime.current_scope_id().unwrap_or(ScopeId(0)), scope: self.runtime.current_scope_id().unwrap_or(ScopeId(0)),
}; };
parent.set(Some(template_ref)); *parent.borrow_mut() = Some(template_ref);
let id = self.set_slot(id); let id = self.set_slot(id);
self.mutations.push(CreatePlaceholder { id }); self.mutations.push(CreatePlaceholder { id });
1 1
@@ -217,12 +211,7 @@ impl VirtualDom {
} }
fn create_static_text(&mut self, value: &str, id: ElementId) { fn create_static_text(&mut self, value: &str, id: ElementId) {
// Safety: we promise not to re-alias this text later on after committing it to the mutation self.mutations.push(CreateTextNode { value, id });
let unbounded_text: &str = unsafe { std::mem::transmute(value) };
self.mutations.push(CreateTextNode {
value: unbounded_text,
id,
});
} }
/// We write all the descendent data for this element /// We write all the descendent data for this element
@@ -289,7 +278,7 @@ impl VirtualDom {
path: ElementPath { path: ElementPath {
path: template.template.get().node_paths[idx], path: template.template.get().node_paths[idx],
}, },
template: template.stable_id().unwrap(), element: template.clone(),
scope: self.runtime.current_scope_id().unwrap_or(ScopeId(0)), scope: self.runtime.current_scope_id().unwrap_or(ScopeId(0)),
}; };
let m = self.create_dynamic_node(boundary_ref, &template.dynamic_nodes[idx]); let m = self.create_dynamic_node(boundary_ref, &template.dynamic_nodes[idx]);
@@ -336,14 +325,14 @@ impl VirtualDom {
attribute.mounted_element.set(id); attribute.mounted_element.set(id);
// Safety: we promise not to re-alias this text later on after committing it to the mutation // Safety: we promise not to re-alias this text later on after committing it to the mutation
let unbounded_name: &str = unsafe { std::mem::transmute(attribute.name) }; let unbounded_name: &str = &attribute.name;
match &attribute.value { match &attribute.value {
AttributeValue::Listener(_) => { AttributeValue::Listener(_) => {
let path = &template.template.get().attr_paths[idx]; let path = &template.template.get().attr_paths[idx];
let element_ref = ElementRef { let element_ref = ElementRef {
path: ElementPath { path }, path: ElementPath { path },
template: template.stable_id().unwrap(), element: template.clone(),
scope: self.runtime.current_scope_id().unwrap_or(ScopeId(0)), scope: self.runtime.current_scope_id().unwrap_or(ScopeId(0)),
}; };
self.elements[id.0] = Some(element_ref); self.elements[id.0] = Some(element_ref);
@@ -354,9 +343,7 @@ impl VirtualDom {
}) })
} }
_ => { _ => {
// Safety: we promise not to re-alias this text later on after committing it to the mutation let unbounded_value = &attribute.value;
let value: BorrowedAttributeValue = (&attribute.value).into();
let unbounded_value = unsafe { std::mem::transmute(value) };
self.mutations.push(SetAttribute { self.mutations.push(SetAttribute {
name: unbounded_name, name: unbounded_name,
@@ -516,7 +503,7 @@ impl VirtualDom {
placeholder.id.set(Some(id)); placeholder.id.set(Some(id));
// Assign the placeholder's parent // Assign the placeholder's parent
placeholder.parent.set(Some(parent)); *placeholder.parent.borrow_mut() = Some(parent);
// Assign the ID to the existing node in the template // Assign the ID to the existing node in the template
self.mutations.push(AssignId { self.mutations.push(AssignId {
@@ -540,7 +527,11 @@ impl VirtualDom {
component.scope.set(Some(scope)); component.scope.set(Some(scope));
match self.run_scope(scope) { let new = self.run_scope(scope);
self.scopes[scope.0].last_rendered_node = Some(new.clone());
match &new {
// Create the component's root element // Create the component's root element
Ready(t) => { Ready(t) => {
self.assign_boundary_ref(parent, t); self.assign_boundary_ref(parent, t);
@@ -550,22 +541,20 @@ impl VirtualDom {
} }
} }
/// Load a scope from a vcomponent. If the props don't exist, that means the component is currently "live" /// Load a scope from a vcomponent. If the scope id doesn't exist, that means the component is currently "live"
fn load_scope_from_vcomponent(&mut self, component: &VComponent) -> ScopeId { fn load_scope_from_vcomponent(&mut self, component: &VComponent) -> ScopeId {
component component.scope.get().unwrap_or_else(|| {
.props self.new_scope(component.props.clone(), component.name)
.map(|props| { .context()
let unbounded_props: Box<dyn AnyProps> = unsafe { std::mem::transmute(props) }; .id
self.new_scope(unbounded_props, component.name).context().id })
})
.unwrap_or_else(|| component.scope.get().unwrap())
} }
fn mount_aborted(&mut self, placeholder: &VPlaceholder, parent: Option<ElementRef>) -> usize { fn mount_aborted(&mut self, placeholder: &VPlaceholder, parent: Option<ElementRef>) -> usize {
let id = self.next_element(); let id = self.next_element();
self.mutations.push(Mutation::CreatePlaceholder { id }); self.mutations.push(Mutation::CreatePlaceholder { id });
placeholder.id.set(Some(id)); placeholder.id.set(Some(id));
placeholder.parent.set(parent); *placeholder.parent.borrow_mut() = parent;
1 1
} }
+46 -75
View File
@@ -1,10 +1,9 @@
use std::ops::Deref;
use crate::{ use crate::{
any_props::AnyProps, any_props::AnyProps,
arena::ElementId, arena::ElementId,
innerlude::{ innerlude::{DirtyScope, ElementPath, ElementRef, VComponent, VPlaceholder, VText},
BorrowedAttributeValue, DirtyScope, ElementPath, ElementRef, VComponent, VPlaceholder,
VText,
},
mutations::Mutation, mutations::Mutation,
nodes::RenderReturn, nodes::RenderReturn,
nodes::{DynamicNode, VNode}, nodes::{DynamicNode, VNode},
@@ -17,52 +16,43 @@ use rustc_hash::{FxHashMap, FxHashSet};
use DynamicNode::*; use DynamicNode::*;
impl VirtualDom { impl VirtualDom {
pub(super) fn diff_scope(&mut self, scope: ScopeId) { pub(super) fn diff_scope(&mut self, scope: ScopeId, new_nodes: RenderReturn) {
self.runtime.scope_stack.borrow_mut().push(scope); self.runtime.scope_stack.borrow_mut().push(scope);
let scope_state = &mut self.get_scope(scope).unwrap(); let scope_state = &mut self.scopes[scope.0];
unsafe { // Load the old and new bump arenas
// Load the old and new bump arenas let old = std::mem::replace(&mut scope_state.last_rendered_node, Some(new_nodes)).unwrap();
let old = scope_state let new = scope_state.last_rendered_node.as_ref().unwrap();
.previous_frame()
.try_load_node()
.expect("Call rebuild before diffing");
let new = scope_state use RenderReturn::{Aborted, Ready};
.current_frame()
.try_load_node()
.expect("Call rebuild before diffing");
use RenderReturn::{Aborted, Ready}; match (&old, new) {
// Normal pathway
(Ready(l), Ready(r)) => self.diff_node(l, r),
match (old, new) { // Unwind the mutations if need be
// Normal pathway (Ready(l), Aborted(p)) => self.diff_ok_to_err(l, p),
(Ready(l), Ready(r)) => self.diff_node(l, r),
// Unwind the mutations if need be // Just move over the placeholder
(Ready(l), Aborted(p)) => self.diff_ok_to_err(l, p), (Aborted(l), Aborted(r)) => {
r.id.set(l.id.get());
*r.parent.borrow_mut() = l.parent.borrow().clone();
}
// Just move over the placeholder // Placeholder becomes something
(Aborted(l), Aborted(r)) => { // We should also clear the error now
r.id.set(l.id.get()); (Aborted(l), Ready(r)) => self.replace_placeholder(
r.parent.set(l.parent.get()) l,
} [r],
l.parent.borrow().expect("root node should not be none"),
// Placeholder becomes something ),
// We should also clear the error now };
(Aborted(l), Ready(r)) => self.replace_placeholder(
l,
[r],
l.parent.get().expect("root node should not be none"),
),
};
}
self.runtime.scope_stack.borrow_mut().pop(); self.runtime.scope_stack.borrow_mut().pop();
} }
fn diff_ok_to_err(&mut self, l: &VNode, p: &VPlaceholder) { fn diff_ok_to_err(&mut self, l: &VNode, p: &VPlaceholder) {
let id = self.next_element(); let id = self.next_element();
p.id.set(Some(id)); p.id.set(Some(id));
p.parent.set(l.parent.get()); *p.parent.borrow_mut() = l.parent.borrow().clone();
self.mutations.push(Mutation::CreatePlaceholder { id }); self.mutations.push(Mutation::CreatePlaceholder { id });
let pre_edits = self.mutations.edits.len(); let pre_edits = self.mutations.edits.len();
@@ -101,13 +91,7 @@ impl VirtualDom {
// Copy over the parent // Copy over the parent
{ {
right_template.parent.set(left_template.parent.get()); *right_template.parent.borrow_mut() = left_template.parent.borrow().clone();
}
// Update the bubble id pointer
right_template.stable_id.set(left_template.stable_id.get());
if let Some(bubble_id) = right_template.stable_id.get() {
self.set_template(bubble_id, right_template);
} }
// If the templates are the same, we don't need to do anything, nor do we want to // If the templates are the same, we don't need to do anything, nor do we want to
@@ -145,7 +129,7 @@ impl VirtualDom {
.enumerate() .enumerate()
.for_each(|(dyn_node_idx, (left_node, right_node))| { .for_each(|(dyn_node_idx, (left_node, right_node))| {
let current_ref = ElementRef { let current_ref = ElementRef {
template: right_template.stable_id().unwrap(), element: right_template.clone(),
path: ElementPath { path: ElementPath {
path: left_template.template.get().node_paths[dyn_node_idx], path: left_template.template.get().node_paths[dyn_node_idx],
}, },
@@ -175,19 +159,18 @@ impl VirtualDom {
(Fragment(left), Fragment(right)) => self.diff_non_empty_fragment(left, right, parent), (Fragment(left), Fragment(right)) => self.diff_non_empty_fragment(left, right, parent),
(Placeholder(left), Placeholder(right)) => { (Placeholder(left), Placeholder(right)) => {
right.id.set(left.id.get()); right.id.set(left.id.get());
right.parent.set(left.parent.get()); *right.parent.borrow_mut() = left.parent.borrow().clone();
}, },
(Component(left), Component(right)) => self.diff_vcomponent(left, right, Some(parent)), (Component(left), Component(right)) => self.diff_vcomponent(left, right, Some(parent)),
(Placeholder(left), Fragment(right)) => self.replace_placeholder(left, *right, parent), (Placeholder(left), Fragment(right)) => self.replace_placeholder(left, right, parent),
(Fragment(left), Placeholder(right)) => self.node_to_placeholder(left, right, parent), (Fragment(left), Placeholder(right)) => self.node_to_placeholder(left, right, parent),
_ => todo!("This is an usual custom case for dynamic nodes. We don't know how to handle it yet."), _ => todo!("This is an usual custom case for dynamic nodes. We don't know how to handle it yet."),
}; };
} }
fn update_attribute(&mut self, right_attr: &Attribute, left_attr: &Attribute) { fn update_attribute(&mut self, right_attr: &Attribute, left_attr: &Attribute) {
let name = unsafe { std::mem::transmute(left_attr.name) }; let name = &left_attr.name;
let value: BorrowedAttributeValue = (&right_attr.value).into(); let value = &right_attr.value;
let value = unsafe { std::mem::transmute(value) };
self.mutations.push(Mutation::SetAttribute { self.mutations.push(Mutation::SetAttribute {
id: left_attr.mounted_element.get(), id: left_attr.mounted_element.get(),
ns: right_attr.namespace, ns: right_attr.namespace,
@@ -218,13 +201,13 @@ impl VirtualDom {
// copy out the box for both // copy out the box for both
let old_scope = &self.scopes[scope_id.0]; let old_scope = &self.scopes[scope_id.0];
let old = old_scope.props.as_ref(); let old = old_scope.props.deref();
let new: &dyn AnyProps = right.props.as_ref(); let new: &dyn AnyProps = right.props.deref();
// If the props are static, then we try to memoize by setting the new with the old // If the props are static, then we try to memoize by setting the new with the old
// The target scopestate still has the reference to the old props, so there's no need to update anything // The target scopestate still has the reference to the old props, so there's no need to update anything
// This also implicitly drops the new props since they're not used // This also implicitly drops the new props since they're not used
if old.memoize(new) { if old.memoize(new.props()) {
tracing::trace!( tracing::trace!(
"Memoized props for component {:#?} ({})", "Memoized props for component {:#?} ({})",
scope_id, scope_id,
@@ -234,11 +217,11 @@ impl VirtualDom {
} }
// First, move over the props from the old to the new, dropping old props in the process // First, move over the props from the old to the new, dropping old props in the process
self.scopes[scope_id.0].props = new; self.scopes[scope_id.0].props = right.props.clone();
// Now run the component and diff it // Now run the component and diff it
self.run_scope(scope_id); let new = self.run_scope(scope_id);
self.diff_scope(scope_id); self.diff_scope(scope_id, new);
self.dirty_scopes.remove(&DirtyScope { self.dirty_scopes.remove(&DirtyScope {
height: self.runtime.get_context(scope_id).unwrap().height, height: self.runtime.get_context(scope_id).unwrap().height,
@@ -325,8 +308,10 @@ impl VirtualDom {
right.id.set(Some(id)); right.id.set(Some(id));
if left.value != right.value { if left.value != right.value {
let value = unsafe { std::mem::transmute(right.value) }; self.mutations.push(Mutation::SetText {
self.mutations.push(Mutation::SetText { id, value }); id,
value: &right.value,
});
} }
} }
@@ -824,7 +809,7 @@ impl VirtualDom {
let placeholder = self.next_element(); let placeholder = self.next_element();
r.id.set(Some(placeholder)); r.id.set(Some(placeholder));
r.parent.set(Some(parent)); r.parent.borrow_mut().replace(parent);
self.mutations self.mutations
.push(Mutation::CreatePlaceholder { id: placeholder }); .push(Mutation::CreatePlaceholder { id: placeholder });
@@ -862,16 +847,6 @@ impl VirtualDom {
// Clean up the roots, assuming we need to generate mutations for these // 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) // This is done last in order to preserve Node ID reclaim order (reclaim in reverse order of claim)
self.reclaim_roots(node, gen_muts); self.reclaim_roots(node, gen_muts);
// Clean up the vnode id
self.reclaim_vnode_id(node);
}
fn reclaim_vnode_id(&mut self, node: &VNode) {
// Clean up the vnode id
if let Some(id) = node.stable_id() {
self.element_refs.remove(id.0);
}
} }
fn reclaim_roots(&mut self, node: &VNode, gen_muts: bool) { fn reclaim_roots(&mut self, node: &VNode, gen_muts: bool) {
@@ -976,10 +951,6 @@ impl VirtualDom {
RenderReturn::Aborted(placeholder) => self.remove_placeholder(placeholder, gen_muts), RenderReturn::Aborted(placeholder) => self.remove_placeholder(placeholder, gen_muts),
}; };
// Restore the props back to the vcomponent in case it gets rendered again
let props = self.scopes[scope.0].props.take();
*comp.props.borrow_mut() = unsafe { std::mem::transmute(props) };
// Now drop all the resouces // Now drop all the resouces
self.drop_scope(scope, false); self.drop_scope(scope, false);
} }
@@ -1019,7 +990,7 @@ impl VirtualDom {
pub(crate) fn assign_boundary_ref(&mut self, parent: Option<ElementRef>, child: &VNode) { pub(crate) fn assign_boundary_ref(&mut self, parent: Option<ElementRef>, child: &VNode) {
if let Some(parent) = parent { if let Some(parent) = parent {
// assign the parent of the child // assign the parent of the child
child.parent.set(Some(parent)); child.parent.borrow_mut().replace(parent);
} }
} }
} }
+25 -29
View File
@@ -1,7 +1,6 @@
use crate::{ use crate::{
scope_context::{consume_context, current_scope_id, schedule_update_any}, scope_context::{consume_context, current_scope_id, schedule_update_any},
Element, IntoDynNode, Properties, ScopeId, ScopeState, Template, TemplateAttribute, Element, IntoDynNode, Properties, ScopeId, Template, TemplateAttribute, TemplateNode, VNode,
TemplateNode, VNode,
}; };
use std::{ use std::{
any::{Any, TypeId}, any::{Any, TypeId},
@@ -14,8 +13,9 @@ use std::{
}; };
/// Provide an error boundary to catch errors from child components /// Provide an error boundary to catch errors from child components
pub fn use_error_boundary(cx: &ScopeState) -> &ErrorBoundary { pub fn use_error_boundary() -> ErrorBoundary {
cx.use_hook(|| cx.provide_context(ErrorBoundary::new())) // use_hook(|| cx.provide_context(ErrorBoundary::new()))
todo!()
} }
/// A boundary that will capture any errors from child components /// A boundary that will capture any errors from child components
@@ -262,10 +262,11 @@ impl<T> Throw for Option<T> {
} }
} }
pub struct ErrorHandler(Box<dyn Fn(CapturedError) -> Element>); #[derive(Clone)]
pub struct ErrorHandler(Rc<dyn Fn(CapturedError) -> Element>);
impl<F: Fn(CapturedError) -> Element> From<F> for ErrorHandler { impl<F: Fn(CapturedError) -> Element> From<F> for ErrorHandler {
fn from(value: F) -> Self { fn from(value: F) -> Self {
Self(Box::new(value)) Self(Rc::new(value))
} }
} }
fn default_handler(error: CapturedError) -> Element { fn default_handler(error: CapturedError) -> Element {
@@ -284,15 +285,13 @@ fn default_handler(error: CapturedError) -> Element {
node_paths: &[&[0u8, 0u8]], node_paths: &[&[0u8, 0u8]],
attr_paths: &[], attr_paths: &[],
}; };
Some(VNode { Some(VNode::new(
parent: Default::default(), None,
stable_id: Default::default(), TEMPLATE,
key: None, Vec::with_capacity(1usize),
template: std::cell::Cell::new(TEMPLATE), vec![error.to_string().into_dyn_node()],
root_ids: Vec::with_capacity(1usize).into(), Default::default(),
dynamic_nodes: vec![error.to_string().into_dyn_node()], ))
dynamic_attrs: Default::default(),
})
} }
#[derive(Clone)] #[derive(Clone)]
@@ -417,7 +416,7 @@ impl<
::core::default::Default::default() ::core::default::Default::default()
}); });
let handle_error = ErrorBoundaryPropsBuilder_Optional::into_value(handle_error, || { let handle_error = ErrorBoundaryPropsBuilder_Optional::into_value(handle_error, || {
ErrorHandler(Box::new(default_handler)) ErrorHandler(Rc::new(default_handler))
}); });
ErrorBoundaryProps { ErrorBoundaryProps {
children, children,
@@ -448,27 +447,24 @@ impl<
/// They are similar to `try/catch` in JavaScript, but they only catch errors in the tree below them. /// They are similar to `try/catch` in JavaScript, but they only catch errors in the tree below them.
/// Error boundaries are quick to implement, but it can be useful to individually handle errors in your components to provide a better user experience when you know that an error is likely to occur. /// Error boundaries are quick to implement, but it can be useful to individually handle errors in your components to provide a better user experience when you know that an error is likely to occur.
#[allow(non_upper_case_globals, non_snake_case)] #[allow(non_upper_case_globals, non_snake_case)]
pub fn ErrorBoundary(cx: ErrorBoundaryProps) -> Element { pub fn ErrorBoundary(props: ErrorBoundaryProps) -> Element {
let error_boundary = use_error_boundary(cx); let error_boundary = use_error_boundary();
match error_boundary.take_error() { match error_boundary.take_error() {
Some(error) => cx.render((cx.props.handle_error.0)(error)), Some(error) => (props.handle_error.0)(error),
None => Some({ None => Some({
let __cx = cx;
static TEMPLATE: Template = Template { static TEMPLATE: Template = Template {
name: "examples/error_handle.rs:81:17:2342", name: "examples/error_handle.rs:81:17:2342",
roots: &[TemplateNode::Dynamic { id: 0usize }], roots: &[TemplateNode::Dynamic { id: 0usize }],
node_paths: &[&[0u8]], node_paths: &[&[0u8]],
attr_paths: &[], attr_paths: &[],
}; };
VNode { VNode::new(
parent: Default::default(), None,
stable_id: Default::default(), TEMPLATE,
key: None, Vec::with_capacity(1usize),
template: std::cell::Cell::new(TEMPLATE), vec![(props.children).into_dyn_node()],
root_ids: Vec::with_capacity(1usize).into(), Default::default(),
dynamic_nodes: vec![(&cx.props.children).into_dyn_node()], )
dynamic_attrs: __cx.bump().alloc([]),
}
}), }),
} }
} }
+1 -10
View File
@@ -27,16 +27,7 @@ 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. /// 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)] #[allow(non_upper_case_globals, non_snake_case)]
pub fn Fragment(cx: FragmentProps) -> Element { pub fn Fragment(cx: FragmentProps) -> Element {
let children = cx.0.as_ref()?; cx.0.clone()
Some(VNode {
key: children.key,
parent: children.parent.clone(),
stable_id: children.stable_id.clone(),
template: children.template.clone(),
root_ids: children.root_ids.clone(),
dynamic_nodes: children.dynamic_nodes,
dynamic_attrs: children.dynamic_attrs,
})
} }
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
+4 -4
View File
@@ -73,10 +73,10 @@ pub(crate) mod innerlude {
} }
pub use crate::innerlude::{ pub use crate::innerlude::{
fc_to_builder, vdom_is_rendering, AnyValue, Attribute, AttributeValue, BorrowedAttributeValue, fc_to_builder, vdom_is_rendering, AnyValue, Attribute, AttributeValue, CapturedError,
CapturedError, Component, DynamicNode, Element, ElementId, Event, Fragment, IntoDynNode, Component, DynamicNode, Element, ElementId, Event, Fragment, IntoDynNode, Mutation, Mutations,
Mutation, Mutations, Properties, RenderReturn, ScopeId, ScopeState, TaskId, Template, Properties, RenderReturn, ScopeId, ScopeState, TaskId, Template, TemplateAttribute,
TemplateAttribute, TemplateNode, VComponent, VNode, VPlaceholder, VText, VirtualDom, TemplateNode, VComponent, VNode, VPlaceholder, VText, VirtualDom,
}; };
/// The purpose of this module is to alleviate imports of many common types /// The purpose of this module is to alleviate imports of many common types
+2 -8
View File
@@ -1,6 +1,6 @@
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use crate::{arena::ElementId, innerlude::BorrowedAttributeValue, ScopeId, Template}; use crate::{arena::ElementId, AttributeValue, ScopeId, Template};
/// A container for all the relevant steps to modify the Real DOM /// A container for all the relevant steps to modify the Real DOM
/// ///
@@ -13,7 +13,6 @@ use crate::{arena::ElementId, innerlude::BorrowedAttributeValue, ScopeId, Templa
/// Templates, however, apply to all subtrees, not just target subtree. /// Templates, however, apply to all subtrees, not just target subtree.
/// ///
/// Mutations are the only link between the RealDOM and the VirtualDOM. /// Mutations are the only link between the RealDOM and the VirtualDOM.
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
#[derive(Debug, Default)] #[derive(Debug, Default)]
#[must_use = "not handling edits can lead to visual inconsistencies in UI"] #[must_use = "not handling edits can lead to visual inconsistencies in UI"]
pub struct Mutations<'a> { pub struct Mutations<'a> {
@@ -56,11 +55,6 @@ impl<'a> Mutations<'a> {
/// of the Dioxus VirtualDom. /// of the Dioxus VirtualDom.
/// ///
/// These edits can be serialized and sent over the network or through any interface /// These edits can be serialized and sent over the network or through any interface
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
serde(tag = "type")
)]
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum Mutation<'a> { pub enum Mutation<'a> {
/// Add these m children to the target element /// Add these m children to the target element
@@ -195,7 +189,7 @@ pub enum Mutation<'a> {
name: &'a str, name: &'a str,
/// The value of the attribute. /// The value of the attribute.
value: BorrowedAttributeValue<'a>, value: &'a AttributeValue,
/// The ID of the node to set the attribute of. /// The ID of the node to set the attribute of.
id: ElementId, id: ElementId,
+45 -115
View File
@@ -1,5 +1,8 @@
use crate::innerlude::{ElementRef, VNodeId}; use crate::any_props::BoxedAnyProps;
use crate::{any_props::AnyProps, arena::ElementId, Element, Event, ScopeId, ScopeState}; use crate::innerlude::ElementRef;
use crate::{arena::ElementId, Element, Event, ScopeId};
use std::ops::Deref;
use std::rc::Rc;
use std::{ use std::{
any::{Any, TypeId}, any::{Any, TypeId},
cell::{Cell, RefCell}, cell::{Cell, RefCell},
@@ -15,6 +18,7 @@ pub type TemplateId = &'static str;
/// ///
/// Dioxus will do its best to immediately resolve any async components into a regular Element, but as an implementor /// Dioxus will do its best to immediately resolve any async components into a regular Element, but as an implementor
/// you might need to handle the case where there's no node immediately ready. /// you might need to handle the case where there's no node immediately ready.
#[derive(Clone)]
pub enum RenderReturn { pub enum RenderReturn {
/// A currently-available element /// A currently-available element
Ready(VNode), Ready(VNode),
@@ -36,26 +40,23 @@ impl Default for RenderReturn {
/// ///
/// The dynamic parts of the template are stored separately from the static parts. This allows faster diffing by skipping /// The dynamic parts of the template are stored separately from the static parts. This allows faster diffing by skipping
/// static parts of the template. /// static parts of the template.
#[derive(Debug, Clone)] #[derive(Debug)]
pub struct VNode { pub struct VNodeInner {
/// The key given to the root of this template. /// The key given to the root of this template.
/// ///
/// In fragments, this is the key of the first child. In other cases, it is the key of the root. /// In fragments, this is the key of the first child. In other cases, it is the key of the root.
pub key: Option<String>, pub key: Option<String>,
/// When rendered, this template will be linked to its parent manually /// When rendered, this template will be linked to its parent manually
pub(crate) parent: Cell<Option<ElementRef>>, pub(crate) parent: RefCell<Option<ElementRef>>,
/// The bubble id assigned to the child that we need to update and drop when diffing happens
pub(crate) stable_id: Cell<Option<VNodeId>>,
/// The static nodes and static descriptor of the template
pub template: Cell<Template<'static>>,
/// The IDs for the roots of this template - to be used when moving the template around and removing it from /// The IDs for the roots of this template - to be used when moving the template around and removing it from
/// the actual Dom /// the actual Dom
pub root_ids: RefCell<Vec<ElementId>>, pub root_ids: RefCell<Vec<ElementId>>,
/// The static nodes and static descriptor of the template
pub template: Cell<Template<'static>>,
/// The dynamic parts of the template /// The dynamic parts of the template
pub dynamic_nodes: Vec<DynamicNode>, pub dynamic_nodes: Vec<DynamicNode>,
@@ -63,13 +64,33 @@ pub struct VNode {
pub dynamic_attrs: Vec<Attribute>, pub dynamic_attrs: Vec<Attribute>,
} }
/// A reference to a template along with any context needed to hydrate it
///
/// The dynamic parts of the template are stored separately from the static parts. This allows faster diffing by skipping
/// static parts of the template.
#[derive(Clone, Debug)]
pub struct VNode(Rc<VNodeInner>);
impl PartialEq for VNode {
fn eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.0, &other.0)
}
}
impl Deref for VNode {
type Target = VNodeInner;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl VNode { impl VNode {
/// Create a template with no nodes that will be skipped over during diffing /// Create a template with no nodes that will be skipped over during diffing
pub fn empty() -> Element { pub fn empty() -> Element {
Some(VNode { Some(Self(Rc::new(VNodeInner {
key: None, key: None,
parent: Default::default(), parent: Default::default(),
stable_id: Default::default(),
root_ids: Default::default(), root_ids: Default::default(),
dynamic_nodes: Vec::new(), dynamic_nodes: Vec::new(),
dynamic_attrs: Vec::new(), dynamic_attrs: Vec::new(),
@@ -79,7 +100,7 @@ impl VNode {
node_paths: &[], node_paths: &[],
attr_paths: &[], attr_paths: &[],
}), }),
}) })))
} }
/// Create a new VNode /// Create a new VNode
@@ -90,20 +111,14 @@ impl VNode {
dynamic_nodes: Vec<DynamicNode>, dynamic_nodes: Vec<DynamicNode>,
dynamic_attrs: Vec<Attribute>, dynamic_attrs: Vec<Attribute>,
) -> Self { ) -> Self {
Self { Self(Rc::new(VNodeInner {
key, key,
parent: Cell::new(None), parent: Default::default(),
stable_id: Cell::new(None),
template: Cell::new(template), template: Cell::new(template),
root_ids: RefCell::new(root_ids), root_ids: RefCell::new(root_ids),
dynamic_nodes, dynamic_nodes,
dynamic_attrs, dynamic_attrs,
} }))
}
/// Get the stable id of this node used for bubbling events
pub(crate) fn stable_id(&self) -> Option<VNodeId> {
self.stable_id.get()
} }
/// Load a dynamic root at the given index /// Load a dynamic root at the given index
@@ -293,7 +308,7 @@ pub enum TemplateNode {
/// A node created at runtime /// A node created at runtime
/// ///
/// This node's index in the DynamicNode list on VNode should match its repsective `Dynamic` index /// This node's index in the DynamicNode list on VNode should match its repsective `Dynamic` index
#[derive(Debug, Clone)] #[derive(Debug)]
pub enum DynamicNode { pub enum DynamicNode {
/// A component node /// A component node
/// ///
@@ -355,7 +370,6 @@ impl<'a> std::fmt::Debug for VComponent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("VComponent") f.debug_struct("VComponent")
.field("name", &self.name) .field("name", &self.name)
.field("static_props", &self.static_props)
.field("scope", &self.scope) .field("scope", &self.scope)
.finish() .finish()
} }
@@ -387,12 +401,12 @@ impl<'a> VText {
} }
/// A placeholder node, used by suspense and fragments /// A placeholder node, used by suspense and fragments
#[derive(Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct VPlaceholder { pub struct VPlaceholder {
/// The ID of this node in the real DOM /// The ID of this node in the real DOM
pub(crate) id: Cell<Option<ElementId>>, pub(crate) id: Cell<Option<ElementId>>,
/// The parent of this node /// The parent of this node
pub(crate) parent: Cell<Option<ElementRef>>, pub(crate) parent: RefCell<Option<ElementRef>>,
} }
impl VPlaceholder { impl VPlaceholder {
@@ -436,7 +450,7 @@ pub enum TemplateAttribute {
} }
/// An attribute on a DOM node, such as `id="my-thing"` or `href="https://example.com"` /// An attribute on a DOM node, such as `id="my-thing"` or `href="https://example.com"`
#[derive(Clone, Debug)] #[derive(Debug)]
pub struct Attribute { pub struct Attribute {
/// The name of the attribute. /// The name of the attribute.
pub name: &'static str, pub name: &'static str,
@@ -483,7 +497,6 @@ impl Attribute {
/// ///
/// These are built-in to be faster during the diffing process. To use a custom value, use the [`AttributeValue::Any`] /// These are built-in to be faster during the diffing process. To use a custom value, use the [`AttributeValue::Any`]
/// variant. /// variant.
#[derive(Clone)]
pub enum AttributeValue { pub enum AttributeValue {
/// Text attribute /// Text attribute
Text(String), Text(String),
@@ -498,7 +511,7 @@ pub enum AttributeValue {
Bool(bool), Bool(bool),
/// A listener, like "onclick" /// A listener, like "onclick"
Listener(ListenerCb), Listener(RefCell<ListenerCb>),
/// An arbitrary value that implements PartialEq and is static /// An arbitrary value that implements PartialEq and is static
Any(Box<dyn AnyValue>), Any(Box<dyn AnyValue>),
@@ -509,85 +522,6 @@ pub enum AttributeValue {
pub type ListenerCb = Box<dyn FnMut(Event<dyn Any>)>; pub type ListenerCb = Box<dyn FnMut(Event<dyn Any>)>;
/// Any of the built-in values that the Dioxus VirtualDom supports as dynamic attributes on elements that are borrowed
///
/// These varients are used to communicate what the value of an attribute is that needs to be updated
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serialize", serde(untagged))]
pub enum BorrowedAttributeValue<'a> {
/// Text attribute
Text(&'a str),
/// A float
Float(f64),
/// Signed integer
Int(i64),
/// Boolean
Bool(bool),
/// An arbitrary value that implements PartialEq and is static
#[cfg_attr(
feature = "serialize",
serde(
deserialize_with = "deserialize_any_value",
serialize_with = "serialize_any_value"
)
)]
Any(std::cell::Ref<'a, dyn AnyValue>),
/// A "none" value, resulting in the removal of an attribute from the dom
None,
}
impl<'a> From<&'a AttributeValue> for BorrowedAttributeValue<'a> {
fn from(value: &'a AttributeValue) -> Self {
match value {
AttributeValue::Text(value) => BorrowedAttributeValue::Text(value),
AttributeValue::Float(value) => BorrowedAttributeValue::Float(*value),
AttributeValue::Int(value) => BorrowedAttributeValue::Int(*value),
AttributeValue::Bool(value) => BorrowedAttributeValue::Bool(*value),
AttributeValue::Listener(_) => {
panic!("A listener cannot be turned into a borrowed value")
}
AttributeValue::Any(value) => {
let value = value.borrow();
BorrowedAttributeValue::Any(std::cell::Ref::map(value, |value| {
&**value.as_ref().unwrap()
}))
}
AttributeValue::None => BorrowedAttributeValue::None,
}
}
}
impl Debug for BorrowedAttributeValue<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Text(arg0) => f.debug_tuple("Text").field(arg0).finish(),
Self::Float(arg0) => f.debug_tuple("Float").field(arg0).finish(),
Self::Int(arg0) => f.debug_tuple("Int").field(arg0).finish(),
Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(),
Self::Any(_) => f.debug_tuple("Any").field(&"...").finish(),
Self::None => write!(f, "None"),
}
}
}
impl PartialEq for BorrowedAttributeValue<'_> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Text(l0), Self::Text(r0)) => l0 == r0,
(Self::Float(l0), Self::Float(r0)) => l0 == r0,
(Self::Int(l0), Self::Int(r0)) => l0 == r0,
(Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
(Self::Any(l0), Self::Any(r0)) => l0.any_cmp(&**r0),
_ => core::mem::discriminant(self) == core::mem::discriminant(other),
}
}
}
#[cfg(feature = "serialize")] #[cfg(feature = "serialize")]
fn serialize_any_value<S>(_: &std::cell::Ref<'_, dyn AnyValue>, _: S) -> Result<S::Ok, S::Error> fn serialize_any_value<S>(_: &std::cell::Ref<'_, dyn AnyValue>, _: S) -> Result<S::Ok, S::Error>
where where
@@ -626,11 +560,7 @@ impl PartialEq for AttributeValue {
(Self::Int(l0), Self::Int(r0)) => l0 == r0, (Self::Int(l0), Self::Int(r0)) => l0 == r0,
(Self::Bool(l0), Self::Bool(r0)) => l0 == r0, (Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
(Self::Listener(_), Self::Listener(_)) => true, (Self::Listener(_), Self::Listener(_)) => true,
(Self::Any(l0), Self::Any(r0)) => { (Self::Any(l0), Self::Any(r0)) => l0.as_ref().any_cmp(r0.as_ref()),
let l0 = l0.borrow();
let r0 = r0.borrow();
l0.as_ref().unwrap().any_cmp(&**r0.as_ref().unwrap())
}
_ => false, _ => false,
} }
} }
@@ -820,7 +750,7 @@ impl<'a> IntoAttributeValue for Arguments<'_> {
impl IntoAttributeValue for Box<dyn AnyValue> { impl IntoAttributeValue for Box<dyn AnyValue> {
fn into_value(self) -> AttributeValue { fn into_value(self) -> AttributeValue {
AttributeValue::Any(RefCell::new(Some(self))) AttributeValue::Any(self)
} }
} }
+2 -6
View File
@@ -1,5 +1,5 @@
use crate::{ use crate::{
any_props::AnyProps, any_props::{AnyProps, BoxedAnyProps},
innerlude::DirtyScope, innerlude::DirtyScope,
nodes::RenderReturn, nodes::RenderReturn,
scope_context::ScopeContext, scope_context::ScopeContext,
@@ -8,11 +8,7 @@ use crate::{
}; };
impl VirtualDom { impl VirtualDom {
pub(super) fn new_scope( pub(super) fn new_scope(&mut self, props: BoxedAnyProps, name: &'static str) -> &ScopeState {
&mut self,
props: Box<dyn AnyProps>,
name: &'static str,
) -> &ScopeState {
let parent_id = self.runtime.current_scope_id(); let parent_id = self.runtime.current_scope_id();
let height = parent_id let height = parent_id
.and_then(|parent_id| self.get_scope(parent_id).map(|f| f.context().height + 1)) .and_then(|parent_id| self.get_scope(parent_id).map(|f| f.context().height + 1))
+6 -11
View File
@@ -1,6 +1,5 @@
use crate::{ use crate::{
any_props::AnyProps, any_props::{BoxedAnyProps, VProps},
any_props::VProps,
innerlude::{DynamicNode, EventHandler, VComponent, VText}, innerlude::{DynamicNode, EventHandler, VComponent, VText},
nodes::{IntoAttributeValue, IntoDynNode, RenderReturn}, nodes::{IntoAttributeValue, IntoDynNode, RenderReturn},
runtime::Runtime, runtime::Runtime,
@@ -49,7 +48,7 @@ pub struct ScopeState {
pub(crate) last_rendered_node: Option<RenderReturn>, pub(crate) last_rendered_node: Option<RenderReturn>,
pub(crate) props: Box<dyn AnyProps>, pub(crate) props: BoxedAnyProps,
} }
impl Drop for ScopeState { impl Drop for ScopeState {
@@ -300,16 +299,12 @@ impl<'src> ScopeState {
// The properties must be valid until the next bump frame // The properties must be valid until the next bump frame
P: Properties, P: Properties,
{ {
let vcomp = VProps::new(component, P::memoize, props); let vcomp = VProps::new(component, P::memoize, props, fn_name);
// cast off the lifetime of the render return
let as_dyn: Box<dyn AnyProps> = Box::new(vcomp);
let extended: Box<dyn AnyProps> = unsafe { std::mem::transmute(as_dyn) };
DynamicNode::Component(VComponent { DynamicNode::Component(VComponent {
name: fn_name, name: fn_name,
render_fn: component as *const (), render_fn: component as *const (),
props: extended, props: BoxedAnyProps::new(vcomp),
scope: Default::default(), scope: Default::default(),
}) })
} }
@@ -332,14 +327,14 @@ impl<'src> ScopeState {
&'src self, &'src self,
mut callback: impl FnMut(Event<T>) + 'src, mut callback: impl FnMut(Event<T>) + 'src,
) -> AttributeValue { ) -> AttributeValue {
AttributeValue::Listener(Box::new(move |event: Event<dyn Any>| { AttributeValue::Listener(RefCell::new(Box::new(move |event: Event<dyn Any>| {
if let Ok(data) = event.data.downcast::<T>() { if let Ok(data) = event.data.downcast::<T>() {
callback(Event { callback(Event {
propagates: event.propagates, propagates: event.propagates,
data, data,
}); });
} }
})) })))
} }
/// Create a new [`AttributeValue`] with a value that implements [`AnyValue`] /// Create a new [`AttributeValue`] with a value that implements [`AnyValue`]
+20 -41
View File
@@ -3,7 +3,7 @@
//! This module provides the primary mechanics to create a hook-based, concurrent VDOM for Rust. //! This module provides the primary mechanics to create a hook-based, concurrent VDOM for Rust.
use crate::{ use crate::{
any_props::VProps, any_props::{BoxedAnyProps, VProps},
arena::ElementId, arena::ElementId,
innerlude::{DirtyScope, ElementRef, ErrorBoundary, Mutations, Scheduler, SchedulerMsg}, innerlude::{DirtyScope, ElementRef, ErrorBoundary, Mutations, Scheduler, SchedulerMsg},
mutations::Mutation, mutations::Mutation,
@@ -11,14 +11,12 @@ use crate::{
nodes::{Template, TemplateId}, nodes::{Template, TemplateId},
runtime::{Runtime, RuntimeGuard}, runtime::{Runtime, RuntimeGuard},
scopes::{ScopeId, ScopeState}, scopes::{ScopeId, ScopeState},
AttributeValue, Element, Event, VNode, AttributeValue, Element, Event,
}; };
use futures_util::{pin_mut, StreamExt}; use futures_util::{pin_mut, StreamExt};
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use slab::Slab; use slab::Slab;
use std::{ use std::{any::Any, cell::Cell, collections::BTreeSet, future::Future, rc::Rc, sync::Arc};
any::Any, cell::Cell, collections::BTreeSet, future::Future, ptr::NonNull, rc::Rc, sync::Arc,
};
/// A virtual node system that progresses user events and diffs UI trees. /// A virtual node system that progresses user events and diffs UI trees.
/// ///
@@ -187,9 +185,6 @@ pub struct VirtualDom {
// Maps a template path to a map of byteindexes to templates // Maps a template path to a map of byteindexes to templates
pub(crate) templates: FxHashMap<TemplateId, FxHashMap<usize, Template<'static>>>, pub(crate) templates: FxHashMap<TemplateId, FxHashMap<usize, Template<'static>>>,
// Every element is actually a dual reference - one to the template and the other to the dynamic node in that template
pub(crate) element_refs: Slab<Option<NonNull<VNode>>>,
// The element ids that are used in the renderer // The element ids that are used in the renderer
pub(crate) elements: Slab<Option<ElementRef>>, pub(crate) elements: Slab<Option<ElementRef>>,
@@ -268,13 +263,12 @@ impl VirtualDom {
dirty_scopes: Default::default(), dirty_scopes: Default::default(),
templates: Default::default(), templates: Default::default(),
elements: Default::default(), elements: Default::default(),
element_refs: Default::default(),
mutations: Mutations::default(), mutations: Mutations::default(),
suspended_scopes: Default::default(), suspended_scopes: Default::default(),
}; };
let root = dom.new_scope( let root = dom.new_scope(
Box::new(VProps::new(root, |_, _| unreachable!(), root_props)), BoxedAnyProps::new(VProps::new(root, |_, _| unreachable!(), root_props, "root")),
"app", "app",
); );
@@ -363,14 +357,10 @@ impl VirtualDom {
| <-- no, broke early | <-- no, broke early
*/ */
let parent_path = match self.elements.get(element.0) { let parent_path = match self.elements.get(element.0) {
Some(Some(el)) => el, Some(Some(el)) => el.clone(),
_ => return, _ => return,
}; };
let mut parent_node = self let mut parent_node = Some(parent_path);
.element_refs
.get(parent_path.template.0)
.cloned()
.map(|el| (*parent_path, el));
let mut listeners = vec![]; let mut listeners = vec![];
// We will clone this later. The data itself is wrapped in RC to be used in callbacks if required // We will clone this later. The data itself is wrapped in RC to be used in callbacks if required
@@ -382,13 +372,12 @@ impl VirtualDom {
// If the event bubbles, we traverse through the tree until we find the target element. // If the event bubbles, we traverse through the tree until we find the target element.
if bubbles { if bubbles {
// Loop through each dynamic attribute (in a depth first order) in this template before moving up to the template's parent. // Loop through each dynamic attribute (in a depth first order) in this template before moving up to the template's parent.
while let Some((path, el_ref)) = parent_node { while let Some(path) = parent_node {
// safety: we maintain references of all vnodes in the element slab let el_ref = &path.element;
let template = unsafe { el_ref.unwrap().as_ref() }; let node_template = el_ref.template.get();
let node_template = template.template.get();
let target_path = path.path; let target_path = path.path;
for (idx, attr) in template.dynamic_attrs.iter().enumerate() { for (idx, attr) in el_ref.dynamic_attrs.iter().enumerate() {
let this_path = node_template.attr_paths[idx]; let this_path = node_template.attr_paths[idx];
// Remove the "on" prefix if it exists, TODO, we should remove this and settle on one // Remove the "on" prefix if it exists, TODO, we should remove this and settle on one
@@ -413,9 +402,7 @@ impl VirtualDom {
let origin = path.scope; let origin = path.scope;
self.runtime.scope_stack.borrow_mut().push(origin); self.runtime.scope_stack.borrow_mut().push(origin);
self.runtime.rendering.set(false); self.runtime.rendering.set(false);
if let Some(cb) = listener.borrow_mut().as_deref_mut() { (listener.borrow_mut())(uievent.clone());
cb(uievent.clone());
}
self.runtime.scope_stack.borrow_mut().pop(); self.runtime.scope_stack.borrow_mut().pop();
self.runtime.rendering.set(true); self.runtime.rendering.set(true);
@@ -425,22 +412,16 @@ impl VirtualDom {
} }
} }
parent_node = template.parent.get().and_then(|element_ref| { parent_node = el_ref.parent.borrow().clone();
self.element_refs
.get(element_ref.template.0)
.cloned()
.map(|el| (element_ref, el))
});
} }
} else { } else {
// Otherwise, we just call the listener on the target element // Otherwise, we just call the listener on the target element
if let Some((path, el_ref)) = parent_node { if let Some(path) = parent_node {
// safety: we maintain references of all vnodes in the element slab let el_ref = &path.element;
let template = unsafe { el_ref.unwrap().as_ref() }; let node_template = el_ref.template.get();
let node_template = template.template.get();
let target_path = path.path; let target_path = path.path;
for (idx, attr) in template.dynamic_attrs.iter().enumerate() { for (idx, attr) in el_ref.dynamic_attrs.iter().enumerate() {
let this_path = node_template.attr_paths[idx]; let this_path = node_template.attr_paths[idx];
// Remove the "on" prefix if it exists, TODO, we should remove this and settle on one // Remove the "on" prefix if it exists, TODO, we should remove this and settle on one
@@ -450,9 +431,7 @@ impl VirtualDom {
let origin = path.scope; let origin = path.scope;
self.runtime.scope_stack.borrow_mut().push(origin); self.runtime.scope_stack.borrow_mut().push(origin);
self.runtime.rendering.set(false); self.runtime.rendering.set(false);
if let Some(cb) = listener.as_deref_mut() { (listener.borrow_mut())(uievent.clone());
cb(uievent.clone());
}
self.runtime.scope_stack.borrow_mut().pop(); self.runtime.scope_stack.borrow_mut().pop();
self.runtime.rendering.set(true); self.runtime.rendering.set(true);
@@ -570,7 +549,7 @@ impl VirtualDom {
match unsafe { self.run_scope(ScopeId::ROOT) } { match unsafe { self.run_scope(ScopeId::ROOT) } {
// Rebuilding implies we append the created elements to the root // Rebuilding implies we append the created elements to the root
RenderReturn::Ready(node) => { RenderReturn::Ready(node) => {
let m = self.create_scope(ScopeId::ROOT, node); let m = self.create_scope(ScopeId::ROOT, &node);
self.mutations.edits.push(Mutation::AppendChildren { self.mutations.edits.push(Mutation::AppendChildren {
id: ElementId(0), id: ElementId(0),
m, m,
@@ -645,8 +624,8 @@ impl VirtualDom {
{ {
let _runtime = RuntimeGuard::new(self.runtime.clone()); let _runtime = RuntimeGuard::new(self.runtime.clone());
// Run the scope and get the mutations // Run the scope and get the mutations
self.run_scope(dirty.id); let new_nodes = self.run_scope(dirty.id);
self.diff_scope(dirty.id); self.diff_scope(dirty.id, new_nodes);
} }
} }