|
@@ -4,7 +4,7 @@
|
|
|
//! cheap and *very* fast to construct - building a full tree should be quick.
|
|
|
|
|
|
use crate::{
|
|
|
- innerlude::{empty_cell, Context, Element, ElementId, Properties, Scope, ScopeId, ScopeState},
|
|
|
+ innerlude::{empty_cell, Context, Element, ElementId, Properties, Scope, ScopeId},
|
|
|
lazynodes::LazyNodes,
|
|
|
};
|
|
|
use bumpalo::{boxed::Box as BumpBox, Bump};
|
|
@@ -20,7 +20,9 @@ use std::{
|
|
|
///
|
|
|
/// 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.
|
|
|
+#[derive(Clone, Debug)]
|
|
|
pub struct NodeLink {
|
|
|
+ pub(crate) link_idx: usize,
|
|
|
pub(crate) gen_id: u32,
|
|
|
pub(crate) scope_id: ScopeId,
|
|
|
}
|
|
@@ -198,6 +200,7 @@ impl<'src> VNode<'src> {
|
|
|
VNode::Linked(c) => VNode::Linked(NodeLink {
|
|
|
gen_id: c.gen_id,
|
|
|
scope_id: c.scope_id,
|
|
|
+ link_idx: c.link_idx,
|
|
|
}),
|
|
|
}
|
|
|
}
|
|
@@ -207,20 +210,22 @@ impl Debug for VNode<'_> {
|
|
|
fn fmt(&self, s: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
|
|
match &self {
|
|
|
VNode::Element(el) => s
|
|
|
- .debug_struct("VElement")
|
|
|
+ .debug_struct("VNode::VElement")
|
|
|
.field("name", &el.tag_name)
|
|
|
.field("key", &el.key)
|
|
|
.finish(),
|
|
|
|
|
|
- VNode::Text(t) => write!(s, "VText {{ text: {} }}", t.text),
|
|
|
- VNode::Anchor(_) => write!(s, "VAnchor"),
|
|
|
+ VNode::Text(t) => write!(s, "VNode::VText {{ text: {} }}", t.text),
|
|
|
+ VNode::Anchor(_) => write!(s, "VNode::VAnchor"),
|
|
|
|
|
|
- VNode::Fragment(frag) => write!(s, "VFragment {{ children: {:?} }}", frag.children),
|
|
|
- VNode::Suspended { .. } => write!(s, "VSuspended"),
|
|
|
- VNode::Component(comp) => write!(s, "VComponent {{ fc: {:?}}}", comp.user_fc),
|
|
|
+ VNode::Fragment(frag) => {
|
|
|
+ write!(s, "VNode::VFragment {{ children: {:?} }}", frag.children)
|
|
|
+ }
|
|
|
+ VNode::Suspended { .. } => write!(s, "VNode::VSuspended"),
|
|
|
+ VNode::Component(comp) => write!(s, "VNode::VComponent {{ fc: {:?}}}", comp.user_fc),
|
|
|
VNode::Linked(c) => write!(
|
|
|
s,
|
|
|
- "VCached {{ gen_id: {}, scope_id: {:?} }}",
|
|
|
+ "VNode::VCached {{ gen_id: {}, scope_id: {:?} }}",
|
|
|
c.gen_id, c.scope_id
|
|
|
),
|
|
|
}
|
|
@@ -344,31 +349,29 @@ pub struct Listener<'bump> {
|
|
|
pub(crate) callback: RefCell<Option<BumpBox<'bump, dyn FnMut(Box<dyn Any + Send>) + 'bump>>>,
|
|
|
}
|
|
|
|
|
|
+pub type VCompCaller<'src> = BumpBox<'src, dyn Fn(Context) -> Element + 'src>;
|
|
|
/// Virtual Components for custom user-defined components
|
|
|
/// Only supports the functional syntax
|
|
|
pub struct VComponent<'src> {
|
|
|
pub key: Option<&'src str>,
|
|
|
|
|
|
pub associated_scope: Cell<Option<ScopeId>>,
|
|
|
- // pub associated_scope: Cell<Option<*mut ScopeInner>>,
|
|
|
|
|
|
// Function pointer to the FC that was used to generate this component
|
|
|
pub user_fc: *const (),
|
|
|
+
|
|
|
pub(crate) can_memoize: bool,
|
|
|
|
|
|
+ pub(crate) hard_allocation: Cell<Option<*const ()>>,
|
|
|
+
|
|
|
// Raw pointer into the bump arena for the props of the component
|
|
|
- pub(crate) raw_props: *const (),
|
|
|
+ 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) comparator: Option<BumpBox<'src, dyn Fn(&VComponent) -> bool + 'src>>,
|
|
|
}
|
|
|
|
|
|
-pub enum VCompCaller<'src> {
|
|
|
- Borrowed(BumpBox<'src, dyn for<'b> Fn(&'b ScopeState) -> Element + 'src>),
|
|
|
- Owned(Box<dyn for<'b> Fn(&'b ScopeState) -> Element>),
|
|
|
-}
|
|
|
-
|
|
|
pub struct VSuspended<'a> {
|
|
|
pub task_id: u64,
|
|
|
pub dom_id: Cell<Option<ElementId>>,
|
|
@@ -511,7 +514,7 @@ impl<'a> NodeFactory<'a> {
|
|
|
|
|
|
pub fn component<P>(
|
|
|
&self,
|
|
|
- component: fn(Scope<'a, P>) -> Element,
|
|
|
+ component: fn(Context<'a>, &'a P) -> Element,
|
|
|
props: P,
|
|
|
key: Option<Arguments>,
|
|
|
) -> VNode<'a>
|
|
@@ -528,99 +531,84 @@ impl<'a> NodeFactory<'a> {
|
|
|
- if the props aren't static, then we convert them into a box which we pass off between renders
|
|
|
*/
|
|
|
|
|
|
- let bump = self.bump();
|
|
|
-
|
|
|
- // let p = BumpBox::new_in(x, a)
|
|
|
-
|
|
|
- // the best place to allocate the props are the other component's arena
|
|
|
- // the second best place is the global allocator
|
|
|
-
|
|
|
- // // if the props are static
|
|
|
- // let boxed = if P::IS_STATIC {
|
|
|
- // todo!()
|
|
|
- // } else {
|
|
|
- // todo!()
|
|
|
- // }
|
|
|
-
|
|
|
- // let caller = Box::new(|f: &ScopeInner| -> Element {
|
|
|
- // //
|
|
|
- // component((f, &props))
|
|
|
- // });
|
|
|
+ // let bump = self.bump();
|
|
|
|
|
|
+ // // later, we'll do a hard allocation
|
|
|
+ // let raw_ptr = bump.alloc(props);
|
|
|
+ let bump = self.bump();
|
|
|
+ let props = bump.alloc(props);
|
|
|
+ let bump_props = props as *mut P as *mut ();
|
|
|
let user_fc = component as *const ();
|
|
|
|
|
|
- // let comparator: &mut dyn Fn(&VComponent) -> bool = bump.alloc_with(|| {
|
|
|
- // move |other: &VComponent| {
|
|
|
- // if user_fc == other.user_fc {
|
|
|
- // // Safety
|
|
|
- // // - We guarantee that FC<P> is the same by function pointer
|
|
|
- // // - Because FC<P> is the same, then P must be the same (even with generics)
|
|
|
- // // - Non-static P are autoderived to memoize as false
|
|
|
- // // - This comparator is only called on a corresponding set of bumpframes
|
|
|
- // let props_memoized = unsafe {
|
|
|
- // let real_other: &P = &*(other.raw_props as *const _ as *const P);
|
|
|
- // props.memoize(real_other)
|
|
|
- // };
|
|
|
-
|
|
|
- // // It's only okay to memoize if there are no children and the props can be memoized
|
|
|
- // // Implementing memoize is unsafe and done automatically with the props trait
|
|
|
- // props_memoized
|
|
|
- // } else {
|
|
|
- // false
|
|
|
- // }
|
|
|
- // }
|
|
|
- // });
|
|
|
- // let comparator = Some(unsafe { BumpBox::from_raw(comparator) });
|
|
|
-
|
|
|
- let key = key.map(|f| self.raw_text(f).0);
|
|
|
-
|
|
|
- let caller = match P::IS_STATIC {
|
|
|
- true => {
|
|
|
- // it just makes sense to box the props
|
|
|
- let boxed_props: Box<P> = Box::new(props);
|
|
|
- let props_we_know_are_static = todo!();
|
|
|
- VCompCaller::Owned(Box::new(|f| {
|
|
|
- //
|
|
|
-
|
|
|
- let p = todo!();
|
|
|
-
|
|
|
- todo!()
|
|
|
- }))
|
|
|
+ let comparator: &mut dyn Fn(&VComponent) -> bool = bump.alloc_with(|| {
|
|
|
+ move |other: &VComponent| {
|
|
|
+ if user_fc == other.user_fc {
|
|
|
+ // Safety
|
|
|
+ // - We guarantee that FC<P> is the same by function pointer
|
|
|
+ // - Because FC<P> is the same, then P must be the same (even with generics)
|
|
|
+ // - Non-static P are autoderived to memoize as false
|
|
|
+ // - This comparator is only called on a corresponding set of bumpframes
|
|
|
+ let props_memoized = unsafe {
|
|
|
+ let real_other: &P = &*(other.bump_props as *const _ as *const P);
|
|
|
+ props.memoize(real_other)
|
|
|
+ };
|
|
|
+
|
|
|
+ // It's only okay to memoize if there are no children and the props can be memoized
|
|
|
+ // Implementing memoize is unsafe and done automatically with the props trait
|
|
|
+ props_memoized
|
|
|
+ } else {
|
|
|
+ false
|
|
|
+ }
|
|
|
}
|
|
|
- false => VCompCaller::Borrowed({
|
|
|
- //
|
|
|
-
|
|
|
- todo!()
|
|
|
- // let caller = bump.alloc()
|
|
|
- }),
|
|
|
+ });
|
|
|
+
|
|
|
+ let drop_props = {
|
|
|
+ // create a closure to drop the props
|
|
|
+ let mut has_dropped = false;
|
|
|
+
|
|
|
+ let drop_props: &mut dyn FnMut() = bump.alloc_with(|| {
|
|
|
+ move || unsafe {
|
|
|
+ log::debug!("dropping props!");
|
|
|
+ if !has_dropped {
|
|
|
+ let real_other = bump_props as *mut _ as *mut P;
|
|
|
+ let b = BumpBox::from_raw(real_other);
|
|
|
+ std::mem::drop(b);
|
|
|
+
|
|
|
+ has_dropped = true;
|
|
|
+ } else {
|
|
|
+ panic!("Drop props called twice - this is an internal failure of Dioxus");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ let drop_props = unsafe { BumpBox::from_raw(drop_props) };
|
|
|
+
|
|
|
+ RefCell::new(Some(drop_props))
|
|
|
};
|
|
|
|
|
|
- todo!()
|
|
|
- // let caller: &'a mut dyn for<'b> Fn(&'b ScopeInner) -> Element<'b> =
|
|
|
- // bump.alloc(move |scope: &ScopeInner| -> Element {
|
|
|
- // log::debug!("calling component renderr {:?}", scope.our_arena_idx);
|
|
|
- // let props: &'_ P = unsafe { &*(raw_props as *const P) };
|
|
|
-
|
|
|
- // let scp: &'a ScopeInner = unsafe { std::mem::transmute(scope) };
|
|
|
- // let s: Scope<'a, P> = (scp, props);
|
|
|
-
|
|
|
- // let res: Element = component(s);
|
|
|
- // unsafe { std::mem::transmute(res) }
|
|
|
- // });
|
|
|
-
|
|
|
- // let caller = unsafe { BumpBox::from_raw(caller) };
|
|
|
-
|
|
|
- // VNode::Component(bump.alloc(VComponent {
|
|
|
- // user_fc,
|
|
|
- // comparator,
|
|
|
- // raw_props,
|
|
|
- // caller,
|
|
|
- // is_static: P::IS_STATIC,
|
|
|
- // key,
|
|
|
- // can_memoize: P::IS_STATIC,
|
|
|
- // drop_props,
|
|
|
- // associated_scope: Cell::new(None),
|
|
|
- // }))
|
|
|
+ let key = key.map(|f| self.raw_text(f).0);
|
|
|
+
|
|
|
+ let caller: &'a mut dyn Fn(&Scope) -> Element =
|
|
|
+ bump.alloc(move |scope: &Scope| -> Element {
|
|
|
+ log::debug!("calling component renderr {:?}", scope.our_arena_idx);
|
|
|
+ let props: &'_ P = unsafe { &*(bump_props as *const P) };
|
|
|
+ let res = component(scope, props);
|
|
|
+ // let res = component((Context { scope }, props));
|
|
|
+ unsafe { std::mem::transmute(res) }
|
|
|
+ });
|
|
|
+
|
|
|
+ let can_memoize = P::IS_STATIC;
|
|
|
+
|
|
|
+ VNode::Component(bump.alloc(VComponent {
|
|
|
+ user_fc,
|
|
|
+ comparator,
|
|
|
+ bump_props,
|
|
|
+ caller,
|
|
|
+ key,
|
|
|
+ can_memoize,
|
|
|
+ drop_props,
|
|
|
+ associated_scope: Cell::new(None),
|
|
|
+ }))
|
|
|
}
|
|
|
|
|
|
pub fn listener(
|