feat: somewhat working with rc and weak

This commit is contained in:
Jonathan Kelley committed 2021-03-12 14:27:32 -05:00
1 parent ec801eab16
commit d4f1ceaffb
10 files changed
+170 -137

No files matched your search

-5
View File
@@ -71,14 +71,9 @@ impl PartialEq for ChildProps<'_> {
impl<'a> Properties for ChildProps<'a> { impl<'a> Properties for ChildProps<'a> {
type Builder = ChildPropsBuilder<'a, ((), ())>; type Builder = ChildPropsBuilder<'a, ((), ())>;
type StaticOutput = ChildProps<'static>;
fn builder() -> <Self as Properties>::Builder { fn builder() -> <Self as Properties>::Builder {
ChildProps::builder() ChildProps::builder()
} }
unsafe fn into_static(self) -> Self::StaticOutput {
unsafe { std::mem::transmute(self) }
}
} }
fn ChildItem(_ctx: Context, _props: &ChildProps) -> DomTree { fn ChildItem(_ctx: Context, _props: &ChildProps) -> DomTree {
-5
View File
@@ -27,12 +27,7 @@ fn main() {}
impl Properties for ExampleProps { impl Properties for ExampleProps {
type Builder = ExamplePropsBuilder<((),)>; type Builder = ExamplePropsBuilder<((),)>;
type StaticOutput = ExampleProps;
fn builder() -> Self::Builder { fn builder() -> Self::Builder {
ExampleProps::builder() ExampleProps::builder()
} }
unsafe fn into_static(self) -> Self {
self
}
} }
+3 -3
View File
@@ -12,12 +12,12 @@ static Header: FC<()> = |ctx, props| {
let handler1 = move || println!("Value is {}", inner.current()); let handler1 = move || println!("Value is {}", inner.current());
ctx.render(|bump| { ctx.render(|c| {
builder::ElementBuilder::new(bump, "div") builder::ElementBuilder::new(c, "div")
.child(VNode::Component(VComponent::new( .child(VNode::Component(VComponent::new(
Bottom, Bottom,
// //
&mut (), c.bump.alloc(()),
))) )))
.finish() .finish()
}) })
-5
View File
@@ -27,12 +27,7 @@ static Example: FC<SomeProps> = |ctx, _props| {
// toodo: derive this // toodo: derive this
impl Properties for SomeProps { impl Properties for SomeProps {
type Builder = SomePropsBuilder<((),)>; type Builder = SomePropsBuilder<((),)>;
type StaticOutput = SomeProps;
fn builder() -> Self::Builder { fn builder() -> Self::Builder {
SomeProps::builder() SomeProps::builder()
} }
unsafe fn into_static(self) -> Self {
self
}
} }
+9 -5
View File
@@ -32,7 +32,7 @@
//! //!
//! More info on how to improve this diffing algorithm: //! More info on how to improve this diffing algorithm:
//! - https://hacks.mozilla.org/2019/03/fast-bump-allocated-virtual-doms-with-rust-and-wasm/ //! - https://hacks.mozilla.org/2019/03/fast-bump-allocated-virtual-doms-with-rust-and-wasm/
use crate::{innerlude::*, scope::Scoped}; use crate::{innerlude::*, scope::Scope};
use bumpalo::Bump; use bumpalo::Bump;
use fxhash::{FxHashMap, FxHashSet}; use fxhash::{FxHashMap, FxHashSet};
use generational_arena::Arena; use generational_arena::Arena;
@@ -60,12 +60,15 @@ use std::{
pub struct DiffMachine<'a> { pub struct DiffMachine<'a> {
pub change_list: EditMachine<'a>, pub change_list: EditMachine<'a>,
pub diffed: FxHashSet<ScopeIdx>, pub diffed: FxHashSet<ScopeIdx>,
pub lifecycle_events: VecDeque<LifeCycleEvent>, pub lifecycle_events: VecDeque<LifeCycleEvent<'a>>,
} }
#[derive(Debug)] // #[derive(Debug)]
pub enum LifeCycleEvent { pub enum LifeCycleEvent<'a> {
Mount { caller: Caller, id: Uuid }, Mount {
caller: Rc<dyn for<'r> Fn(Context<'r>) -> DomTree + 'a>,
id: Uuid,
},
PropsChanged, PropsChanged,
SameProps, SameProps,
Remove, Remove,
@@ -260,6 +263,7 @@ impl<'a> DiffMachine<'a> {
self.change_list self.change_list
.create_text_node("placeholder for vcomponent"); .create_text_node("placeholder for vcomponent");
let id = uuid::Uuid::new_v4(); let id = uuid::Uuid::new_v4();
*component.stable_addr.as_ref().borrow_mut() = Some(id);
self.change_list.save_known_root(id); self.change_list.save_known_root(id);
self.lifecycle_events.push_back(LifeCycleEvent::Mount { self.lifecycle_events.push_back(LifeCycleEvent::Mount {
caller: component.caller.clone(), caller: component.caller.clone(),
+3 -2
View File
@@ -521,8 +521,9 @@ pub fn attr<'a>(name: &'static str, value: &'a str) -> Attribute<'a> {
// } // }
// } // }
pub fn virtual_child<'a, T: Properties>(ctx: &NodeCtx<'a>, f: FC<T>, p: T) -> VNode<'a> { pub fn virtual_child<'a, T: Properties + 'a>(ctx: &NodeCtx<'a>, f: FC<T>, p: T) -> VNode<'a> {
VNode::Component(crate::nodes::VComponent::new(f, ctx.bump.alloc(p))) let propsd: &'a mut _ = ctx.bump.alloc(p);
VNode::Component(crate::nodes::VComponent::new(f, propsd))
} }
trait Bany { trait Bany {
+56 -47
View File
@@ -264,35 +264,40 @@ impl<'a> VText<'a> {
/// Virtual Components for custom user-defined components /// Virtual Components for custom user-defined components
/// Only supports the functional syntax /// Only supports the functional syntax
pub type StableScopeAddres = Rc<RefCell<Option<usize>>>; pub type StableScopeAddres = Rc<RefCell<Option<uuid::Uuid>>>;
#[derive(Debug, Clone)]
pub struct VComponent<'src> { pub struct VComponent<'src> {
pub stable_addr: StableScopeAddres, pub stable_addr: StableScopeAddres,
pub raw_props: Rc<*mut dyn Any>,
// pub comparator: Comparator, pub comparator: Rc<dyn Fn(&VComponent) -> bool + 'src>,
pub caller: Caller, pub caller: Rc<dyn for<'r> Fn(Context<'r>) -> DomTree + 'src>,
pub caller_ref: *const (),
// a pointer into the bump arena (given by the 'src lifetime)
raw_props: *const (),
// a pointer to the raw fn typ
caller_ref: *const (),
_p: PhantomData<&'src ()>, _p: PhantomData<&'src ()>,
} }
pub trait PropsComparator {}
#[derive(Clone)] impl std::fmt::Debug for VComponent<'_> {
// pub struct Comparator(pub Rc<dyn Fn(&dyn PropsComparator) -> bool>); fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
pub struct Comparator(pub Rc<dyn Fn(&dyn Any) -> bool>); todo!()
impl std::fmt::Debug for Comparator {
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Ok(())
} }
} }
// pub struct Comparator(pub Rc<dyn Fn(&VComponent) -> bool>);
// impl std::fmt::Debug for Comparator {
// fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// Ok(())
// }
// }
#[derive(Clone)] // pub struct Caller(pub Rc<dyn Fn(Context) -> DomTree>);
pub struct Caller(pub Rc<dyn Fn(Context) -> DomTree>); // impl std::fmt::Debug for Caller {
impl std::fmt::Debug for Caller { // fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { // Ok(())
Ok(()) // }
} // }
}
impl<'a> VComponent<'a> { impl<'a> VComponent<'a> {
// use the type parameter on props creation and move it into a portable context // use the type parameter on props creation and move it into a portable context
@@ -300,36 +305,40 @@ impl<'a> VComponent<'a> {
// - perform comparisons when diffing (memoization) // - perform comparisons when diffing (memoization)
// TODO: lift the requirement that props need to be static // TODO: lift the requirement that props need to be static
// we want them to borrow references... maybe force implementing a "to_static_unsafe" trait // we want them to borrow references... maybe force implementing a "to_static_unsafe" trait
pub fn new<P: Properties>(caller: FC<P>, props: &mut P) -> Self { pub fn new<P: Properties + 'a>(component: FC<P>, props: &'a P) -> Self {
let caller_ref = caller as *const (); let caller_ref = component as *const ();
let props = Rc::new(props); let raw_props = props as *const P as *const ();
// used for memoization let props_comparator = move |other: &VComponent| {
let p1 = props.clone(); // Safety:
// We are guaranteed that the props will be of the same type because
// let props_comparator = move |new_props| -> bool { // there is no way to create a VComponent other than this `new` method.
// false //
// let p = new_props.downcast_ref::<P::StaticOutput>().expect(""); // Therefore, if the render functions are identical (by address), then so will be
// let r = unsafe { std::mem::transmute::<_, &P>(p) }; // props type paramter (because it is the same render function). Therefore, we can be
// &r == p1.as_ref() // sure
// }; if caller_ref == other.caller_ref {
let real_other = unsafe { &*(other.raw_props as *const _ as *const P) };
// used for actually rendering the custom component real_other == props
let p2 = props.clone(); } else {
let caller = move |ctx: Context| -> DomTree { false
// cast back into the right lifetime }
caller(ctx, p2.as_ref())
}; };
todo!() let caller = move |ctx: Context| -> DomTree {
// Self { // cast back into the right lifetime
// caller_ref, let safe_props: &P = unsafe { &*(raw_props as *const P) };
// raw_props: props, component(ctx, props)
// _p: PhantomData, };
// comparator: Comparator(Rc::new(props_comparator)),
// caller: Caller(Rc::new(caller)), Self {
// stable_addr: Rc::new(RefCell::new(None)), caller_ref,
// } raw_props: props as *const P as *const _,
_p: PhantomData,
caller: Rc::new(caller),
comparator: Rc::new(props_comparator),
stable_addr: Rc::new(RefCell::new(None)),
}
} }
} }
+83 -44
View File
@@ -1,15 +1,22 @@
use crate::innerlude::*; use crate::innerlude::*;
use bumpalo::Bump; use bumpalo::Bump;
use std::{any::Any, cell::RefCell, collections::HashSet, marker::PhantomData, ops::Deref}; use std::{
any::Any,
cell::RefCell,
collections::HashSet,
marker::PhantomData,
ops::Deref,
rc::{Rc, Weak},
};
pub trait Scoped { // pub trait Scoped {
fn run(&mut self); // fn run(&mut self);
// fn compare_props(&self, new: &dyn std::any::Any) -> bool; // // fn compare_props(&self, new: &dyn std::any::Any) -> bool;
fn call_listener(&mut self, trigger: EventTrigger); // fn call_listener(&mut self, trigger: EventTrigger);
fn new_frame<'bump>(&'bump self) -> &'bump VNode<'bump>; // fn new_frame<'bump>(&'bump self) -> &'bump VNode<'bump>;
fn old_frame<'bump>(&'bump self) -> &'bump VNode<'bump>; // fn old_frame<'bump>(&'bump self) -> &'bump VNode<'bump>;
} // }
/// Every component in Dioxus is represented by a `Scope`. /// Every component in Dioxus is represented by a `Scope`.
/// ///
@@ -17,7 +24,8 @@ pub trait Scoped {
/// ///
/// Scopes are allocated in a generational arena. As components are mounted/unmounted, they will replace slots of dead components. /// Scopes are allocated in a generational arena. As components are mounted/unmounted, they will replace slots of dead components.
/// The actual contents of the hooks, though, will be allocated with the standard allocator. These should not allocate as frequently. /// The actual contents of the hooks, though, will be allocated with the standard allocator. These should not allocate as frequently.
pub struct Scope<P: Properties> { pub struct Scope {
// pub struct Scope<P: Properties> {
// Map to the parent // Map to the parent
pub parent: Option<ScopeIdx>, pub parent: Option<ScopeIdx>,
@@ -27,12 +35,15 @@ pub struct Scope<P: Properties> {
// //
pub children: HashSet<ScopeIdx>, pub children: HashSet<ScopeIdx>,
pub caller: Weak<dyn Fn(Context) -> DomTree + 'static>,
// // the props // // the props
// pub props: P, // pub props: P,
// and the actual render function // and the actual render function
pub caller: Caller, // pub caller: *const dyn Fn(Context) -> DomTree,
_p: std::marker::PhantomData<P>, // _p: std::marker::PhantomData<P>,
// _p: std::marker::PhantomData<P>,
// pub raw_caller: FC<P>, // pub raw_caller: FC<P>,
// ========================== // ==========================
@@ -57,35 +68,62 @@ pub struct Scope<P: Properties> {
// instead of having it as a trait method, we use a single function // instead of having it as a trait method, we use a single function
// todo: do the unsafety magic stuff to erase the type of p // todo: do the unsafety magic stuff to erase the type of p
pub fn create_scoped( // pub fn create_scoped(
// raw_f: FC<P>, // // raw_f: FC<P>,
caller: Caller, // // caller: Caller,
// props: P, // // props: P,
myidx: ScopeIdx, // myidx: ScopeIdx,
parent: Option<ScopeIdx>, // parent: Option<ScopeIdx>,
) -> Box<dyn Scoped> { // ) -> Box<dyn Scoped> {
Box::new(Scope::<()> { // Box::new(Scope::<()> {
// raw_caller: raw_f, // // raw_caller: raw_f,
_p: Default::default(), // _p: Default::default(),
caller, // // caller,
myidx, // myidx,
hook_arena: typed_arena::Arena::new(), // hook_arena: typed_arena::Arena::new(),
hooks: RefCell::new(Vec::new()), // hooks: RefCell::new(Vec::new()),
frames: ActiveFrame::new(), // frames: ActiveFrame::new(),
children: HashSet::new(), // children: HashSet::new(),
listeners: Default::default(), // listeners: Default::default(),
parent, // parent,
// props, // // props,
}) // })
} // }
impl<P: Properties + 'static> Scoped for Scope<P> { impl Scope {
// we are being created in the scope of an existing component (where the creator_node lifetime comes into play)
// we are going to break this lifetime by force in order to save it on ourselves.
// To make sure that the lifetime isn't truly broken, we receive a Weak RC so we can't keep it around after the parent dies.
// This should never happen, but is a good check to keep around
pub fn new<'creator_node>(
caller: Weak<dyn Fn(Context) -> DomTree + 'creator_node>,
myidx: ScopeIdx,
parent: Option<ScopeIdx>,
) -> Self {
// caller has been broken free
// however, it's still weak, so if the original Rc gets killed, we can't touch it
let broken_caller: Weak<dyn Fn(Context) -> DomTree + 'static> =
unsafe { std::mem::transmute(caller) };
Self {
caller: broken_caller,
hook_arena: typed_arena::Arena::new(),
hooks: RefCell::new(Vec::new()),
frames: ActiveFrame::new(),
children: HashSet::new(),
listeners: Default::default(),
parent,
myidx,
}
}
// impl<P: Properties + 'static> Scoped for Scope<P> {
/// Create a new context and run the component with references from the Virtual Dom /// Create a new context and run the component with references from the Virtual Dom
/// This function downcasts the function pointer based on the stored props_type /// This function downcasts the function pointer based on the stored props_type
/// ///
/// Props is ?Sized because we borrow the props and don't need to know the size. P (sized) is used as a marker (unsized) /// Props is ?Sized because we borrow the props and don't need to know the size. P (sized) is used as a marker (unsized)
fn run<'bump>(&'bump mut self) { pub fn run<'bump>(&'bump mut self) {
let frame = { let frame = {
let frame = self.frames.next(); let frame = self.frames.next();
frame.bump.reset(); frame.bump.reset();
@@ -105,8 +143,9 @@ impl<P: Properties + 'static> Scoped for Scope<P> {
listeners: &self.listeners, listeners: &self.listeners,
}; };
todo!()
// Note that the actual modification of the vnode head element occurs during this call // Note that the actual modification of the vnode head element occurs during this call
let _: DomTree = (self.caller.0.as_ref())(ctx); // let _: DomTree = (self.caller.0.as_ref())(ctx);
// let _: DomTree = (self.raw_caller)(ctx, &self.props); // let _: DomTree = (self.raw_caller)(ctx, &self.props);
/* /*
@@ -121,14 +160,14 @@ impl<P: Properties + 'static> Scoped for Scope<P> {
- Public API cannot drop or destructure VNode - Public API cannot drop or destructure VNode
*/ */
frame.head_node = node_slot // frame.head_node = node_slot
.deref() // .deref()
.borrow_mut() // .borrow_mut()
.take() // .take()
.expect("Viewing did not happen"); // .expect("Viewing did not happen");
} }
// fn compare_props(&self, new: &dyn Any) -> bool { // pub fn compare_props(&self, new: &dyn Any) -> bool {
// new.downcast_ref::<P>() // new.downcast_ref::<P>()
// .map(|f| &self.props == f) // .map(|f| &self.props == f)
// .expect("Props should not be of a different type") // .expect("Props should not be of a different type")
@@ -137,7 +176,7 @@ impl<P: Properties + 'static> Scoped for Scope<P> {
// A safe wrapper around calling listeners // A safe wrapper around calling listeners
// calling listeners will invalidate the list of listeners // calling listeners will invalidate the list of listeners
// The listener list will be completely drained because the next frame will write over previous listeners // The listener list will be completely drained because the next frame will write over previous listeners
fn call_listener(&mut self, trigger: EventTrigger) { pub fn call_listener(&mut self, trigger: EventTrigger) {
let EventTrigger { let EventTrigger {
listener_id, listener_id,
event: source, event: source,
@@ -165,11 +204,11 @@ impl<P: Properties + 'static> Scoped for Scope<P> {
} }
} }
fn new_frame<'bump>(&'bump self) -> &'bump VNode<'bump> { pub fn new_frame<'bump>(&'bump self) -> &'bump VNode<'bump> {
self.frames.current_head_node() self.frames.current_head_node()
} }
fn old_frame<'bump>(&'bump self) -> &'bump VNode<'bump> { pub fn old_frame<'bump>(&'bump self) -> &'bump VNode<'bump> {
self.frames.prev_head_node() self.frames.prev_head_node()
} }
} }
+16 -15
View File
@@ -1,13 +1,14 @@
// use crate::{changelist::EditList, nodes::VNode}; // use crate::{changelist::EditList, nodes::VNode};
use crate::innerlude::*; use crate::innerlude::*;
use crate::{ use crate::{patch::Edit, scope::Scope};
patch::Edit,
scope::{create_scoped, Scoped},
};
use bumpalo::Bump; use bumpalo::Bump;
use generational_arena::Arena; use generational_arena::Arena;
use std::{any::TypeId, cell::RefCell, rc::Rc}; use std::{
any::TypeId,
cell::RefCell,
rc::{Rc, Weak},
};
/// An integrated virtual node system that progresses events and diffs UI trees. /// An integrated virtual node system that progresses events and diffs UI trees.
/// Differences are converted into patches which a renderer can use to draw the UI. /// Differences are converted into patches which a renderer can use to draw the UI.
@@ -18,13 +19,15 @@ pub struct VirtualDom {
/// eventually, come up with a better datastructure that reuses boxes for known P types /// eventually, come up with a better datastructure that reuses boxes for known P types
/// like a generational typemap bump arena /// like a generational typemap bump arena
/// -> IE a cache line for each P type with some heuristics on optimizing layout /// -> IE a cache line for each P type with some heuristics on optimizing layout
pub(crate) components: Arena<Box<dyn Scoped>>, pub(crate) components: Arena<Scope>,
// pub(crate) components: RefCell<Arena<Box<dyn Scoped>>>, // pub(crate) components: RefCell<Arena<Box<dyn Scoped>>>,
// pub(crate) components: Rc<RefCell<Arena<Box<dyn Scoped>>>>, // pub(crate) components: Rc<RefCell<Arena<Box<dyn Scoped>>>>,
/// The index of the root component. /// The index of the root component.
/// Will not be ready if the dom is fresh /// Will not be ready if the dom is fresh
pub(crate) base_scope: ScopeIdx, pub(crate) base_scope: ScopeIdx,
pub(crate) root_caller: Rc<dyn Fn(Context) -> DomTree + 'static>,
// Type of the original props. This is done so VirtualDom does not need to be generic. // Type of the original props. This is done so VirtualDom does not need to be generic.
#[doc(hidden)] #[doc(hidden)]
_root_prop_type: std::any::TypeId, _root_prop_type: std::any::TypeId,
@@ -55,6 +58,7 @@ impl VirtualDom {
pub fn new(root: FC<()>) -> Self { pub fn new(root: FC<()>) -> Self {
Self::new_with_props(root, ()) Self::new_with_props(root, ())
} }
/// Start a new VirtualDom instance with a dependent props. /// Start a new VirtualDom instance with a dependent props.
/// Later, the props can be updated by calling "update" with a new set of props, causing a set of re-renders. /// Later, the props can be updated by calling "update" with a new set of props, causing a set of re-renders.
/// ///
@@ -63,18 +67,15 @@ impl VirtualDom {
pub fn new_with_props<P: Properties + 'static>(root: FC<P>, root_props: P) -> Self { pub fn new_with_props<P: Properties + 'static>(root: FC<P>, root_props: P) -> Self {
let mut components = Arena::new(); let mut components = Arena::new();
// Create a reference to the component in the arena // the root is kept around
// Note: we are essentially running the "Mount" lifecycle event manually while the vdom doesnt yet exist let root_caller: Rc<dyn Fn(Context) -> DomTree + 'static> =
let caller = Caller(Rc::new(move |ctx| { Rc::new(move |ctx| root(ctx, &root_props));
// let weak_caller: Weak<dyn Fn(Context) -> DomTree + 'static> = Rc::downgrade(&root_caller);
root(ctx, &root_props) let base_scope = components.insert_with(move |id| Scope::new(weak_caller, id, None));
// DomTree {}
}));
let base_scope = components.insert_with(|id| create_scoped(caller, id, None));
// let base_scope = components.insert_with(|id| create_scoped(root, root_props, id, None));
Self { Self {
components, components,
root_caller,
base_scope, base_scope,
diff_bump: Bump::new(), diff_bump: Bump::new(),
_root_prop_type: TypeId::of::<P>(), _root_prop_type: TypeId::of::<P>(),
-6
View File
@@ -60,13 +60,7 @@ static Example2: FC<ExampleProps> = |ctx, props| {
impl Properties for ExampleProps { impl Properties for ExampleProps {
type Builder = ExamplePropsBuilder<((),)>; type Builder = ExamplePropsBuilder<((),)>;
type StaticOutput = ExampleProps;
fn builder() -> Self::Builder { fn builder() -> Self::Builder {
ExampleProps::builder() ExampleProps::builder()
} }
unsafe fn into_static(self) -> Self::StaticOutput {
todo!()
}
} }