wip: rename

This commit is contained in:
Jonathan Kelley committed 2021-11-28 16:25:42 -05:00
1 parent 93d4b8ca7c
commit 36d89beb34
20 files changed
+243 -7549

No files matched your search

+2 -2
View File
@@ -5,12 +5,12 @@
//! if the type supports PartialEq. The Properties trait is used by the rsx! and html! macros to generate the type-safe builder
//! that ensures compile-time required and optional fields on cx.
use crate::innerlude::{Context, Element, LazyNodes, NodeLink};
use crate::innerlude::{Context, Element, LazyNodes, VPortal};
pub struct FragmentProps(Element);
pub struct FragmentBuilder<const BUILT: bool>(Element);
impl FragmentBuilder<false> {
pub fn children(self, children: Option<NodeLink>) -> FragmentBuilder<true> {
pub fn children(self, children: Option<VPortal>) -> FragmentBuilder<true> {
FragmentBuilder(children)
}
}
+109 -94
View File
@@ -198,12 +198,13 @@ impl<'bump> DiffStack<'bump> {
}
}
fn push_subtree(&mut self) {
self.nodes_created_stack.push(0);
self.instructions.push(DiffInstruction::Mount {
and: MountType::Append,
});
}
// todo: subtrees
// fn push_subtree(&mut self) {
// self.nodes_created_stack.push(0);
// self.instructions.push(DiffInstruction::Mount {
// and: MountType::Append,
// });
// }
fn push_nodes_created(&mut self, count: usize) {
self.nodes_created_stack.push(count);
@@ -289,7 +290,7 @@ impl<'bump> DiffState<'bump> {
1
}
VNode::Linked(linked) => {
VNode::Portal(linked) => {
let node = unsafe { &*linked.node };
let node: &VNode = unsafe { std::mem::transmute(node) };
self.push_all_nodes(node)
@@ -357,8 +358,8 @@ impl<'bump> DiffState<'bump> {
VNode::Placeholder(anchor) => self.create_anchor_node(anchor, node),
VNode::Element(element) => self.create_element_node(element, node),
VNode::Fragment(frag) => self.create_fragment_node(frag),
VNode::Component(component) => self.create_component_node(component),
VNode::Linked(linked) => self.create_linked_node(linked),
VNode::Component(component) => self.create_component_node(*component),
VNode::Portal(linked) => self.create_linked_node(linked),
}
}
@@ -422,14 +423,18 @@ impl<'bump> DiffState<'bump> {
self.mutations.set_attribute(attr, real_id.as_u64());
}
if !children.is_empty() && children.len() == 1 {
if let VNode::Text(vtext) = children[0] {
self.mutations.set_text(vtext.text, real_id.as_u64());
return;
}
}
// todo: the settext optimization
self.stack.create_children(children, MountType::Append);
// if children.len() == 1 {
// if let VNode::Text(vtext) = children[0] {
// self.mutations.set_text(vtext.text, real_id.as_u64());
// return;
// }
// }
if !children.is_empty() {
self.stack.create_children(children, MountType::Append);
}
}
fn create_fragment_node(&mut self, frag: &'bump VFragment<'bump>) {
@@ -454,8 +459,6 @@ impl<'bump> DiffState<'bump> {
self.scopes
.new_with_key(fc_ptr, caller, parent_scope, container, height, subtree);
log::debug!("container for scope {:?} is {:?}", new_idx, container);
// Actually initialize the caller's slot with the right address
vcomponent.associated_scope.set(Some(new_idx));
@@ -463,18 +466,12 @@ impl<'bump> DiffState<'bump> {
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
}
// TODO:
// add noderefs to current noderef list Noderefs
// add effects to current effect list Effects
let new_component = self.scopes.get_scope(&new_idx).unwrap();
log::debug!(
"initializing component {:?} with height {:?}",
new_idx,
height + 1
);
// TODO: add noderefs to current noderef list Noderefs
let _new_component = self.scopes.get_scope(&new_idx).unwrap();
// Run the scope for one iteration to initialize it
if self.scopes.run_scope(&new_idx) {
@@ -482,16 +479,17 @@ impl<'bump> DiffState<'bump> {
let nextnode = self.scopes.fin_head(&new_idx);
self.stack.create_component(new_idx, nextnode);
if new_component.is_subtree_root.get() {
self.stack.push_subtree();
}
// todo: subtrees
// if new_component.is_subtree_root.get() {
// self.stack.push_subtree();
// }
}
// Finally, insert this scope as a seen node.
self.seen_scopes.insert(new_idx);
}
fn create_linked_node(&mut self, link: &'bump NodeLink) {
fn create_linked_node(&mut self, link: &'bump VPortal) {
if link.scope_id.get().is_none() {
if let Some(cur_scope) = self.stack.current_scope() {
link.scope_id.set(Some(cur_scope));
@@ -513,17 +511,17 @@ impl<'bump> DiffState<'bump> {
self.diff_text_nodes(old, new, old_node, new_node);
}
(Component(old), Component(new)) => {
self.diff_component_nodes(old_node, new_node, old, 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)) => new.dom_id.set(old.dom_id.get()),
(Element(old), Element(new)) => self.diff_element_nodes(old, new, old_node, new_node),
(Linked(old), Linked(new)) => self.diff_linked_nodes(old, new),
(Portal(old), Portal(new)) => self.diff_linked_nodes(old, new),
// Anything else is just a basic replace and create
(
Linked(_) | Component(_) | Fragment(_) | Text(_) | Element(_) | Placeholder(_),
Linked(_) | Component(_) | Fragment(_) | Text(_) | Element(_) | Placeholder(_),
Portal(_) | Component(_) | Fragment(_) | Text(_) | Element(_) | Placeholder(_),
Portal(_) | Component(_) | Fragment(_) | Text(_) | Element(_) | Placeholder(_),
) => self
.stack
.create_node(new_node, MountType::Replace { old: old_node }),
@@ -636,60 +634,76 @@ impl<'bump> DiffState<'bump> {
}
}
match (old.children.len(), new.children.len()) {
(0, 0) => {}
(1, 1) => {
let old1 = &old.children[0];
let new1 = &new.children[0];
match (old1, new1) {
(VNode::Text(old_text), VNode::Text(new_text)) => {
if old_text.text != new_text.text {
self.mutations.set_text(new_text.text, root.as_u64());
}
}
(VNode::Text(_old_text), _) => {
self.stack.element_stack.push(root);
self.stack.instructions.push(DiffInstruction::PopElement);
self.stack.create_node(new1, MountType::Append);
}
(_, VNode::Text(new_text)) => {
self.remove_nodes([old1], false);
self.mutations.set_text(new_text.text, root.as_u64());
}
_ => {
self.stack.element_stack.push(root);
self.stack.instructions.push(DiffInstruction::PopElement);
self.diff_children(old.children, new.children);
}
}
}
(0, 1) => {
if let VNode::Text(text) = &new.children[0] {
self.mutations.set_text(text.text, root.as_u64());
} else {
self.stack.element_stack.push(root);
self.stack.instructions.push(DiffInstruction::PopElement);
}
}
(0, _) => {
self.mutations.edits.push(PushRoot {
root: root.as_u64(),
});
self.stack.element_stack.push(root);
self.stack.instructions.push(DiffInstruction::PopElement);
self.stack.create_children(new.children, MountType::Append);
}
(_, 0) => {
self.remove_nodes(old.children, false);
self.mutations.set_text("", root.as_u64());
}
(_, _) => {
self.stack.element_stack.push(root);
self.stack.instructions.push(DiffInstruction::PopElement);
self.diff_children(old.children, new.children);
}
if old.children.is_empty() && !new.children.is_empty() {
self.mutations.edits.push(PushRoot {
root: root.as_u64(),
});
self.stack.element_stack.push(root);
self.stack.instructions.push(DiffInstruction::PopElement);
self.stack.create_children(new.children, MountType::Append);
} else {
self.stack.element_stack.push(root);
self.stack.instructions.push(DiffInstruction::PopElement);
self.diff_children(old.children, new.children);
}
// todo: this is for the "settext" optimization
// it works, but i'm not sure if it's the direction we want to take
// match (old.children.len(), new.children.len()) {
// (0, 0) => {}
// (1, 1) => {
// let old1 = &old.children[0];
// let new1 = &new.children[0];
// match (old1, new1) {
// (VNode::Text(old_text), VNode::Text(new_text)) => {
// if old_text.text != new_text.text {
// self.mutations.set_text(new_text.text, root.as_u64());
// }
// }
// (VNode::Text(_old_text), _) => {
// self.stack.element_stack.push(root);
// self.stack.instructions.push(DiffInstruction::PopElement);
// self.stack.create_node(new1, MountType::Append);
// }
// (_, VNode::Text(new_text)) => {
// self.remove_nodes([old1], false);
// self.mutations.set_text(new_text.text, root.as_u64());
// }
// _ => {
// self.stack.element_stack.push(root);
// self.stack.instructions.push(DiffInstruction::PopElement);
// self.diff_children(old.children, new.children);
// }
// }
// }
// (0, 1) => {
// if let VNode::Text(text) = &new.children[0] {
// self.mutations.set_text(text.text, root.as_u64());
// } else {
// self.stack.element_stack.push(root);
// self.stack.instructions.push(DiffInstruction::PopElement);
// }
// }
// (0, _) => {
// self.mutations.edits.push(PushRoot {
// root: root.as_u64(),
// });
// self.stack.element_stack.push(root);
// self.stack.instructions.push(DiffInstruction::PopElement);
// self.stack.create_children(new.children, MountType::Append);
// }
// (_, 0) => {
// self.remove_nodes(old.children, false);
// self.mutations.set_text("", root.as_u64());
// }
// (_, _) => {
// self.stack.element_stack.push(root);
// self.stack.instructions.push(DiffInstruction::PopElement);
// self.diff_children(old.children, new.children);
// }
// }
}
fn diff_component_nodes(
@@ -721,6 +735,7 @@ impl<'bump> DiffState<'bump> {
.get_scope_mut(&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.
@@ -754,7 +769,7 @@ impl<'bump> DiffState<'bump> {
self.diff_children(old.children, new.children);
}
fn diff_linked_nodes(&mut self, old: &'bump NodeLink, new: &'bump NodeLink) {
fn diff_linked_nodes(&mut self, old: &'bump VPortal, new: &'bump VPortal) {
if !std::ptr::eq(old.node, new.node) {
// if the ptrs are the same then theyr're the same
let old: &VNode = unsafe { std::mem::transmute(&*old.node) };
@@ -1207,7 +1222,7 @@ impl<'bump> DiffState<'bump> {
VNode::Text(t) => break t.dom_id.get(),
VNode::Element(t) => break t.dom_id.get(),
VNode::Placeholder(t) => break t.dom_id.get(),
VNode::Linked(l) => {
VNode::Portal(l) => {
let node: &VNode = unsafe { std::mem::transmute(&*l.node) };
self.find_last_element(node);
}
@@ -1235,7 +1250,7 @@ impl<'bump> DiffState<'bump> {
let scope_id = el.associated_scope.get().unwrap();
search_node = Some(self.scopes.root_node(&scope_id));
}
VNode::Linked(link) => {
VNode::Portal(link) => {
let node = unsafe { std::mem::transmute(&*link.node) };
search_node = Some(node);
}
@@ -1281,7 +1296,7 @@ impl<'bump> DiffState<'bump> {
self.scopes.try_remove(&scope_id).unwrap();
}
VNode::Linked(l) => {
VNode::Portal(l) => {
let node: &'bump VNode<'bump> = unsafe { std::mem::transmute(&*l.node) };
self.replace_node(node, nodes_created);
}
@@ -1330,7 +1345,7 @@ impl<'bump> DiffState<'bump> {
self.remove_nodes(f.children, gen_muts);
}
VNode::Linked(l) => {
VNode::Portal(l) => {
let node = unsafe { std::mem::transmute(&*l.node) };
self.remove_nodes(Some(node), gen_muts);
}
+2 -2
View File
@@ -31,14 +31,14 @@ pub(crate) mod innerlude {
pub use crate::scopearena::*;
pub use crate::virtual_dom::*;
pub type Element = Option<NodeLink>;
pub type Element = Option<VPortal>;
pub type FC<P> = for<'a> fn(Context<'a>, &'a P) -> Element;
}
pub use crate::innerlude::{
Attribute, Context, DioxusElement, DomEdit, Element, ElementId, EventPriority, IntoVNode,
LazyNodes, Listener, MountType, Mutations, NodeFactory, Properties, SchedulerMsg, ScopeId,
UserEvent, VElement, VFragment, VNode, VPlaceholder, VSuspended, VirtualDom, FC,
UserEvent, VElement, VFragment, VNode, VirtualDom, FC,
};
pub mod prelude {
+48 -62
View File
@@ -129,7 +129,7 @@ pub enum VNode<'src> {
///
/// let node: NodeLink = vdom.render_vnode(rsx!( "hello" ));
/// ```
Linked(NodeLink),
Portal(VPortal),
}
impl<'src> VNode<'src> {
@@ -139,10 +139,9 @@ impl<'src> VNode<'src> {
VNode::Element(el) => el.key,
VNode::Component(c) => c.key,
VNode::Fragment(f) => f.key,
VNode::Text(_t) => None,
VNode::Placeholder(_f) => None,
VNode::Linked(_c) => None,
VNode::Portal(_c) => None,
}
}
@@ -161,8 +160,7 @@ impl<'src> VNode<'src> {
VNode::Text(el) => el.dom_id.get(),
VNode::Element(el) => el.dom_id.get(),
VNode::Placeholder(el) => el.dom_id.get(),
VNode::Linked(_) => None,
VNode::Portal(_) => None,
VNode::Fragment(_) => None,
VNode::Component(_) => None,
}
@@ -171,7 +169,6 @@ impl<'src> VNode<'src> {
pub(crate) fn children(&self) -> &[VNode<'src>] {
match &self {
VNode::Fragment(f) => f.children,
VNode::Component(_c) => todo!("children are not accessible through this"),
_ => &[],
}
}
@@ -187,7 +184,7 @@ impl<'src> VNode<'src> {
children: f.children,
key: f.key,
}),
VNode::Linked(c) => VNode::Linked(NodeLink {
VNode::Portal(c) => VNode::Portal(VPortal {
scope_id: c.scope_id.clone(),
link_idx: c.link_idx.clone(),
node: c.node,
@@ -204,15 +201,13 @@ impl Debug for VNode<'_> {
.field("name", &el.tag_name)
.field("key", &el.key)
.finish(),
VNode::Text(t) => write!(s, "VNode::VText {{ text: {} }}", t.text),
VNode::Placeholder(_) => write!(s, "VNode::VAnchor"),
VNode::Fragment(frag) => {
write!(s, "VNode::VFragment {{ children: {:?} }}", frag.children)
}
VNode::Component(comp) => write!(s, "VNode::VComponent {{ fc: {:?}}}", comp.user_fc),
VNode::Linked(c) => write!(s, "VNode::VCached {{ scope_id: {:?} }}", c.scope_id.get()),
VNode::Portal(c) => write!(s, "VNode::VCached {{ scope_id: {:?} }}", c.scope_id.get()),
}
}
}
@@ -348,6 +343,32 @@ pub struct Listener<'bump> {
RefCell<Option<BumpBox<'bump, dyn FnMut(std::sync::Arc<dyn Any + Send + Sync>) + 'bump>>>,
}
/// A cached node is a "pointer" to a "rendered" node in a particular scope
///
/// It does not provide direct access to the node, so it doesn't carry any lifetime information with it
///
/// It is used during the diffing/rendering process as a runtime key into an existing set of nodes. The "render" key
/// is essentially a unique key to guarantee safe usage of the Node.
///
/// Linked VNodes can only be made through the [`Context::render`] method
///
/// Typically, NodeLinks are found *not* in a VNode. When NodeLinks are in a VNode, the NodeLink was passed into
/// an `rsx!` call.
///
/// todo: remove the raw pointer and use runtime checks instead
#[derive(Debug)]
pub struct VPortal {
pub(crate) link_idx: Cell<usize>,
pub(crate) scope_id: Cell<Option<ScopeId>>,
pub(crate) node: *const VNode<'static>,
}
impl PartialEq for VPortal {
fn eq(&self, other: &Self) -> bool {
self.node == other.node
}
}
/// Virtual Components for custom user-defined components
/// Only supports the functional syntax
pub struct VComponent<'src> {
@@ -366,45 +387,14 @@ pub struct VComponent<'src> {
pub(crate) bump_props: *const (),
// during the "teardown" process we'll take the caller out so it can be dropped properly
// pub(crate) caller: Option<VCompCaller<'src>>,
pub(crate) caller: &'src dyn Fn(&'src Scope) -> Element,
pub(crate) comparator: Option<&'src dyn Fn(&VComponent) -> bool>,
// todo: re-add this
pub(crate) drop_props: RefCell<Option<BumpBox<'src, dyn FnMut()>>>,
}
pub struct VSuspended {
pub task_id: usize,
pub scope: Cell<Option<ScopeId>>,
pub dom_id: Cell<Option<ElementId>>,
pub parent: Cell<Option<ElementId>>,
}
/// A cached node is a "pointer" to a "rendered" node in a particular scope
///
/// It does not provide direct access to the node, so it doesn't carry any lifetime information with it
///
/// It is used during the diffing/rendering process as a runtime key into an existing set of nodes. The "render" key
/// is essentially a unique key to guarantee safe usage of the Node.
///
/// Linked VNodes can only be made through the [`Context::render`] method
///
/// Typically, NodeLinks are found *not* in a VNode. When NodeLinks are in a VNode, the NodeLink was passed into
/// an `rsx!` call.
#[derive(Debug)]
pub struct NodeLink {
pub(crate) link_idx: Cell<usize>,
pub(crate) scope_id: Cell<Option<ScopeId>>,
pub(crate) node: *const VNode<'static>,
}
impl PartialEq for NodeLink {
fn eq(&self, other: &Self) -> bool {
self.node == other.node
}
}
/// This struct provides an ergonomic API to quickly build VNodes.
///
/// NodeFactory is used to build VNodes in the component's memory space.
@@ -624,8 +614,8 @@ impl<'a> NodeFactory<'a> {
callback: BumpBox<'a, dyn FnMut(Arc<dyn Any + Send + Sync>) + 'a>,
) -> Listener<'a> {
Listener {
mounted_node: Cell::new(None),
event,
mounted_node: Cell::new(None),
callback: RefCell::new(Some(callback)),
}
}
@@ -634,15 +624,14 @@ impl<'a> NodeFactory<'a> {
self,
node_iter: impl IntoIterator<Item = impl IntoVNode<'a> + 'c> + 'b,
) -> VNode<'a> {
let bump = self.bump;
let mut nodes = bumpalo::collections::Vec::new_in(bump);
let mut nodes = bumpalo::collections::Vec::new_in(self.bump);
for node in node_iter {
nodes.push(node.into_vnode(self));
}
if nodes.is_empty() {
nodes.push(VNode::Placeholder(bump.alloc(VPlaceholder {
nodes.push(VNode::Placeholder(self.bump.alloc(VPlaceholder {
dom_id: empty_cell(),
})));
}
@@ -657,26 +646,23 @@ impl<'a> NodeFactory<'a> {
self,
node_iter: impl IntoIterator<Item = impl IntoVNode<'a> + 'c> + 'b,
) -> VNode<'a> {
let bump = self.bump;
let mut nodes = bumpalo::collections::Vec::new_in(bump);
let mut nodes = bumpalo::collections::Vec::new_in(self.bump);
for node in node_iter {
nodes.push(node.into_vnode(self));
}
if nodes.is_empty() {
nodes.push(VNode::Placeholder(bump.alloc(VPlaceholder {
nodes.push(VNode::Placeholder(self.bump.alloc(VPlaceholder {
dom_id: empty_cell(),
})));
}
let children = nodes.into_bump_slice();
// todo: add a backtrace
if cfg!(debug_assertions) && children.len() > 1 && children.last().unwrap().key().is_none()
{
use backtrace::Backtrace;
let bt = Backtrace::new();
// todo: make the backtrace prettier or remove it altogether
log::error!(
r#"
Warning: Each child in an array or iterator should have a unique "key" prop.
@@ -685,7 +671,7 @@ impl<'a> NodeFactory<'a> {
-------------
{:?}
"#,
bt
backtrace::Backtrace::new()
);
}
@@ -740,7 +726,7 @@ impl<'a> NodeFactory<'a> {
key: None,
});
let ptr = self.bump.alloc(frag) as *const _;
Some(NodeLink {
Some(VPortal {
link_idx: Default::default(),
scope_id: Default::default(),
node: unsafe { std::mem::transmute(ptr) },
@@ -847,10 +833,10 @@ impl IntoVNode<'_> for Arguments<'_> {
}
// called cx.render from a helper function
impl IntoVNode<'_> for Option<NodeLink> {
impl IntoVNode<'_> for Option<VPortal> {
fn into_vnode(self, _cx: NodeFactory) -> VNode {
match self {
Some(node) => VNode::Linked(node),
Some(node) => VNode::Portal(node),
None => {
todo!()
}
@@ -860,10 +846,10 @@ impl IntoVNode<'_> for Option<NodeLink> {
// essentially passing elements through props
// just build a new element in place
impl IntoVNode<'_> for &Option<NodeLink> {
impl IntoVNode<'_> for &Option<VPortal> {
fn into_vnode(self, _cx: NodeFactory) -> VNode {
match self {
Some(node) => VNode::Linked(NodeLink {
Some(node) => VNode::Portal(VPortal {
link_idx: node.link_idx.clone(),
scope_id: node.scope_id.clone(),
node: node.node,
@@ -876,15 +862,15 @@ impl IntoVNode<'_> for &Option<NodeLink> {
}
}
impl IntoVNode<'_> for NodeLink {
impl IntoVNode<'_> for VPortal {
fn into_vnode(self, _cx: NodeFactory) -> VNode {
VNode::Linked(self)
VNode::Portal(self)
}
}
impl IntoVNode<'_> for &NodeLink {
impl IntoVNode<'_> for &VPortal {
fn into_vnode(self, _cx: NodeFactory) -> VNode {
VNode::Linked(NodeLink {
VNode::Portal(VPortal {
link_idx: self.link_idx.clone(),
scope_id: self.scope_id.clone(),
node: self.node,
+44 -103
View File
@@ -43,6 +43,7 @@ pub type Context<'a> = &'a Scope;
/// use case they might have.
pub struct Scope {
pub(crate) parent_scope: Option<*mut Scope>,
pub(crate) container: ElementId,
pub(crate) our_arena_idx: ScopeId,
@@ -61,7 +62,9 @@ pub struct Scope {
pub(crate) items: RefCell<SelfReferentialItems<'static>>,
pub(crate) hooks: HookList,
pub(crate) hook_arena: Bump,
pub(crate) hook_vals: RefCell<SmallVec<[*mut dyn Any; 5]>>,
pub(crate) hook_idx: Cell<usize>,
pub(crate) shared_contexts: RefCell<HashMap<TypeId, Rc<dyn Any>>>,
@@ -100,7 +103,9 @@ impl Scope {
///
/// assert_eq!(base.subtree(), 0);
/// ```
pub fn subtree(&self) -> u32 {
///
/// todo: enable
pub(crate) fn _subtree(&self) -> u32 {
self.subtree.get()
}
@@ -119,14 +124,13 @@ impl Scope {
/// rsx!(cx, div { "Subtree {id}"})
/// };
/// ```
pub fn create_subtree(&self) -> Option<u32> {
///
/// todo: enable subtree
pub(crate) fn _create_subtree(&self) -> Option<u32> {
if self.is_subtree_root.get() {
None
} else {
todo!()
// let cur = self.subtree().get();
// self.shared.cur_subtree.set(cur + 1);
// Some(cur)
}
}
@@ -165,6 +169,7 @@ impl Scope {
/// assert_eq!(base.parent(), None);
/// ```
pub fn parent(&self) -> Option<ScopeId> {
// safety: the pointer to our parent is *always* valid thanks to the bump arena
self.parent_scope.map(|p| unsafe { &*p }.our_arena_idx)
}
@@ -189,11 +194,8 @@ impl Scope {
///
/// ## Notice: you should prefer using prepare_update and get_scope_id
pub fn schedule_update(&self) -> Rc<dyn Fn() + 'static> {
// pub fn schedule_update(&self) -> Rc<dyn Fn() + 'static> {
let chan = self.sender.clone();
let id = self.scope_id();
let (chan, id) = (self.sender.clone(), self.scope_id());
Rc::new(move || {
// log::debug!("set on channel an update for scope {:?}", id);
let _ = chan.unbounded_send(SchedulerMsg::Immediate(id));
})
}
@@ -281,7 +283,11 @@ impl Scope {
}
}
/// Pushes the future onto the poll queue to be polled
/// Pushes the future onto the poll queue to be polled after the component renders.
///
///
///
///
/// The future is forcibly dropped if the component is not ready by the next render
pub fn push_task<'src, F: Future<Output = ()>>(
&'src self,
@@ -292,7 +298,7 @@ impl Scope {
F: 'src,
{
self.sender
.unbounded_send(SchedulerMsg::TaskPushed(self.our_arena_idx))
.unbounded_send(SchedulerMsg::NewTask(self.our_arena_idx))
.unwrap();
// allocate the future
@@ -326,7 +332,7 @@ impl Scope {
/// cx.render(lazy_tree)
/// }
///```
pub fn render<'src>(&'src self, rsx: Option<LazyNodes<'src, '_>>) -> Option<NodeLink> {
pub fn render<'src>(&'src self, rsx: Option<LazyNodes<'src, '_>>) -> Option<VPortal> {
let frame = self.wip_frame();
let bump = &frame.bump;
let factory = NodeFactory { bump };
@@ -336,7 +342,7 @@ impl Scope {
let node_ptr = node as *mut _;
let node_ptr = unsafe { std::mem::transmute(node_ptr) };
let link = NodeLink {
let link = VPortal {
scope_id: Cell::new(Some(self.our_arena_idx)),
link_idx: Cell::new(0),
node: node_ptr,
@@ -371,24 +377,35 @@ impl Scope {
initializer: impl FnOnce(usize) -> State,
runner: impl FnOnce(&'src mut State) -> Output,
) -> Output {
if self.hooks.at_end() {
self.hooks.push_hook(initializer(self.hooks.len()));
let mut vals = self.hook_vals.borrow_mut();
let hook_len = vals.len();
let cur_idx = self.hook_idx.get();
if cur_idx >= hook_len {
let val = self.hook_arena.alloc(initializer(hook_len));
vals.push(val);
}
const HOOK_ERR_MSG: &str = r###"
Unable to retrieve the hook that was initialized at this index.
Consult the `rules of hooks` to understand how to use hooks properly.
let state = vals
.get(cur_idx)
.and_then(|inn| {
self.hook_idx.set(cur_idx + 1);
let raw_box = unsafe { &mut **inn };
raw_box.downcast_mut::<State>()
})
.expect(
r###"
Unable to retrieve the hook that was initialized at this index.
Consult the `rules of hooks` to understand how to use hooks properly.
You likely used the hook in a conditional. Hooks rely on consistent ordering between renders.
Functions prefixed with "use" should never be called conditionally.
"###;
You likely used the hook in a conditional. Hooks rely on consistent ordering between renders.
Functions prefixed with "use" should never be called conditionally.
"###,
);
runner(self.hooks.next::<State>().expect(HOOK_ERR_MSG))
runner(state)
}
}
// Important internal methods
impl Scope {
/// The "work in progress frame" represents the frame that is currently being worked on.
pub(crate) fn wip_frame(&self) -> &BumpFrame {
match self.generation.get() & 1 == 0 {
@@ -449,11 +466,7 @@ impl BumpFrame {
Self { bump, nodes }
}
pub fn _allocated_bytes(&self) -> usize {
self.bump.allocated_bytes()
}
pub fn assign_nodelink(&self, node: &NodeLink) {
pub fn assign_nodelink(&self, node: &VPortal) {
let mut nodes = self.nodes.borrow_mut();
let len = nodes.len();
@@ -463,78 +476,6 @@ impl BumpFrame {
}
}
/// An abstraction over internally stored data using a hook-based memory layout.
///
/// Hooks are allocated using Boxes and then our stored references are given out.
///
/// It's unsafe to "reset" the hooklist, but it is safe to add hooks into it.
///
/// Todo: this could use its very own bump arena, but that might be a tad overkill
#[derive(Default)]
pub(crate) struct HookList {
arena: Bump,
vals: RefCell<SmallVec<[*mut dyn Any; 5]>>,
idx: Cell<usize>,
}
impl HookList {
pub fn new(capacity: usize) -> Self {
Self {
arena: Bump::with_capacity(capacity),
..Default::default()
}
}
pub(crate) fn next<T: 'static>(&self) -> Option<&mut T> {
self.vals.borrow().get(self.idx.get()).and_then(|inn| {
self.idx.set(self.idx.get() + 1);
let raw_box = unsafe { &mut **inn };
raw_box.downcast_mut::<T>()
})
}
/// This resets the internal iterator count
/// It's okay that we've given out each hook, but now we have the opportunity to give it out again
/// Therefore, resetting is considered unsafe
///
/// This should only be ran by Dioxus itself before "running scope".
/// Dioxus knows how to descend through the tree to prevent mutable aliasing.
pub(crate) unsafe fn reset(&self) {
self.idx.set(0);
}
pub(crate) fn push_hook<T: 'static>(&self, new: T) {
let val = self.arena.alloc(new);
self.vals.borrow_mut().push(val)
}
pub(crate) fn len(&self) -> usize {
self.vals.borrow().len()
}
pub(crate) fn cur_idx(&self) -> usize {
self.idx.get()
}
pub(crate) fn at_end(&self) -> bool {
self.cur_idx() >= self.len()
}
pub fn clear(&mut self) {
self.vals.borrow_mut().drain(..).for_each(|state| {
let as_mut = unsafe { &mut *state };
let boxed = unsafe { bumpalo::boxed::Box::from_raw(as_mut) };
drop(boxed);
});
}
/// Get the ammount of memory a hooklist uses
/// Used in heuristics
pub fn _get_hook_arena_size(&self) -> usize {
self.arena.allocated_bytes()
}
}
#[test]
fn sizeof() {
dbg!(std::mem::size_of::<Scope>());
+19 -3
View File
@@ -180,7 +180,6 @@ impl ScopeArena {
caller,
generation: 0.into(),
hooks: HookList::new(hook_capacity),
shared_contexts: Default::default(),
items: RefCell::new(SelfReferentialItems {
@@ -188,6 +187,10 @@ impl ScopeArena {
borrowed_props: Default::default(),
tasks: Default::default(),
}),
hook_arena: Bump::new(),
hook_vals: RefCell::new(smallvec::SmallVec::with_capacity(hook_capacity)),
hook_idx: Default::default(),
});
let any_item = self.scopes.borrow_mut().insert(new_scope_id, scope);
@@ -208,7 +211,14 @@ impl ScopeArena {
let scope = unsafe { &mut *self.scopes.borrow_mut().remove(id).unwrap() };
// we're just reusing scopes so we need to clear it out
scope.hooks.clear();
scope.hook_vals.get_mut().drain(..).for_each(|state| {
let as_mut = unsafe { &mut *state };
let boxed = unsafe { bumpalo::boxed::Box::from_raw(as_mut) };
drop(boxed);
});
scope.hook_idx.set(0);
scope.hook_arena.reset();
scope.shared_contexts.get_mut().clear();
scope.parent_scope = None;
scope.generation.set(0);
@@ -310,7 +320,13 @@ impl ScopeArena {
// Safety:
// - We dropped the listeners, so no more &mut T can be used while these are held
// - All children nodes that rely on &mut T are replaced with a new reference
unsafe { scope.hooks.reset() };
scope.hook_vals.get_mut().drain(..).for_each(|state| {
let as_mut = unsafe { &mut *state };
let boxed = unsafe { bumpalo::boxed::Box::from_raw(as_mut) };
drop(boxed);
});
scope.hook_idx.set(0);
scope.hook_arena.reset();
// Safety:
// - We've dropped all references to the wip bump frame with "ensure_drop_safety"
+5 -8
View File
@@ -8,10 +8,7 @@ use futures_util::{Future, StreamExt};
use fxhash::FxHashSet;
use indexmap::IndexSet;
use smallvec::SmallVec;
use std::pin::Pin;
use std::sync::Arc;
use std::task::Poll;
use std::{any::Any, collections::VecDeque};
use std::{any::Any, collections::VecDeque, pin::Pin, sync::Arc, task::Poll};
/// A virtual node system that progresses user events and diffs UI trees.
///
@@ -286,7 +283,7 @@ impl VirtualDom {
if let Some(msg) = self.pending_messages.pop_back() {
match msg {
// just keep looping, the task is now saved but we should actually poll it
SchedulerMsg::TaskPushed(id) => {
SchedulerMsg::NewTask(id) => {
self.scopes.pending_futures.borrow_mut().insert(id);
}
SchedulerMsg::UiEvent(event) => {
@@ -546,7 +543,7 @@ pub enum SchedulerMsg {
Immediate(ScopeId),
// an async task pushed from an event handler (or just spawned)
TaskPushed(ScopeId),
NewTask(ScopeId),
}
/// User Events are events that are shuttled from the renderer into the VirtualDom trhough the scheduler channel.
@@ -675,9 +672,9 @@ impl<'a> Future for PollTasks<'a> {
// I think the futures neeed to be pinned using bumpbox or something
// right now, they're bump allocated so this shouldn't matter anyway - they're not going to move
let task_mut = task.as_mut();
let unpinned = unsafe { Pin::new_unchecked(task_mut) };
let pinned = unsafe { Pin::new_unchecked(task_mut) };
if unpinned.poll(cx).is_ready() {
if pinned.poll(cx).is_ready() {
all_pending = false
} else {
unfinished_tasks.push(task);