Ver Fonte

feat: somewhat working with rc and weak

Jonathan Kelley há 4 anos atrás
pai
commit
d4f1cea

+ 0 - 5
packages/core/examples/borrowed.rs

@@ -71,14 +71,9 @@ impl PartialEq for ChildProps<'_> {
 
 impl<'a> Properties for ChildProps<'a> {
     type Builder = ChildPropsBuilder<'a, ((), ())>;
-    type StaticOutput = ChildProps<'static>;
     fn builder() -> <Self as Properties>::Builder {
         ChildProps::builder()
     }
-
-    unsafe fn into_static(self) -> Self::StaticOutput {
-        unsafe { std::mem::transmute(self) }
-    }
 }
 
 fn ChildItem(_ctx: Context, _props: &ChildProps) -> DomTree {

+ 0 - 5
packages/core/examples/fc.rs

@@ -27,12 +27,7 @@ fn main() {}
 
 impl Properties for ExampleProps {
     type Builder = ExamplePropsBuilder<((),)>;
-    type StaticOutput = ExampleProps;
     fn builder() -> Self::Builder {
         ExampleProps::builder()
     }
-
-    unsafe fn into_static(self) -> Self {
-        self
-    }
 }

+ 3 - 3
packages/core/examples/nested.rs

@@ -12,12 +12,12 @@ static Header: FC<()> = |ctx, props| {
 
     let handler1 = move || println!("Value is {}", inner.current());
 
-    ctx.render(|bump| {
-        builder::ElementBuilder::new(bump, "div")
+    ctx.render(|c| {
+        builder::ElementBuilder::new(c, "div")
             .child(VNode::Component(VComponent::new(
                 Bottom,
                 //
-                &mut (),
+                c.bump.alloc(()),
             )))
             .finish()
     })

+ 0 - 5
packages/core/examples/step.rs

@@ -27,12 +27,7 @@ static Example: FC<SomeProps> = |ctx, _props| {
 // toodo: derive this
 impl Properties for SomeProps {
     type Builder = SomePropsBuilder<((),)>;
-    type StaticOutput = SomeProps;
     fn builder() -> Self::Builder {
         SomeProps::builder()
     }
-
-    unsafe fn into_static(self) -> Self {
-        self
-    }
 }

+ 9 - 5
packages/core/src/diff.rs

@@ -32,7 +32,7 @@
 //!
 //! More info on how to improve this diffing algorithm:
 //!  - 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 fxhash::{FxHashMap, FxHashSet};
 use generational_arena::Arena;
@@ -60,12 +60,15 @@ use std::{
 pub struct DiffMachine<'a> {
     pub change_list: EditMachine<'a>,
     pub diffed: FxHashSet<ScopeIdx>,
-    pub lifecycle_events: VecDeque<LifeCycleEvent>,
+    pub lifecycle_events: VecDeque<LifeCycleEvent<'a>>,
 }
 
-#[derive(Debug)]
-pub enum LifeCycleEvent {
-    Mount { caller: Caller, id: Uuid },
+// #[derive(Debug)]
+pub enum LifeCycleEvent<'a> {
+    Mount {
+        caller: Rc<dyn for<'r> Fn(Context<'r>) -> DomTree + 'a>,
+        id: Uuid,
+    },
     PropsChanged,
     SameProps,
     Remove,
@@ -260,6 +263,7 @@ impl<'a> DiffMachine<'a> {
                 self.change_list
                     .create_text_node("placeholder for vcomponent");
                 let id = uuid::Uuid::new_v4();
+                *component.stable_addr.as_ref().borrow_mut() = Some(id);
                 self.change_list.save_known_root(id);
                 self.lifecycle_events.push_back(LifeCycleEvent::Mount {
                     caller: component.caller.clone(),

+ 3 - 2
packages/core/src/nodebuilder.rs

@@ -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> {
-    VNode::Component(crate::nodes::VComponent::new(f, ctx.bump.alloc(p)))
+pub fn virtual_child<'a, T: Properties + 'a>(ctx: &NodeCtx<'a>, f: FC<T>, p: T) -> VNode<'a> {
+    let propsd: &'a mut _ = ctx.bump.alloc(p);
+    VNode::Component(crate::nodes::VComponent::new(f, propsd))
 }
 
 trait Bany {

+ 56 - 47
packages/core/src/nodes.rs

@@ -264,35 +264,40 @@ impl<'a> VText<'a> {
 
 /// Virtual Components for custom user-defined components
 /// 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 stable_addr: StableScopeAddres,
-    pub raw_props: Rc<*mut dyn Any>,
-    // pub comparator: Comparator,
-    pub caller: Caller,
-    pub caller_ref: *const (),
+
+    pub comparator: Rc<dyn Fn(&VComponent) -> bool + 'src>,
+    pub caller: Rc<dyn for<'r> Fn(Context<'r>) -> DomTree + 'src>,
+
+    // 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 ()>,
 }
-pub trait PropsComparator {}
-
-#[derive(Clone)]
-// pub struct Comparator(pub Rc<dyn Fn(&dyn PropsComparator) -> bool>);
-pub struct Comparator(pub Rc<dyn Fn(&dyn Any) -> 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>);
-impl std::fmt::Debug for Caller {
-    fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        Ok(())
+impl std::fmt::Debug for VComponent<'_> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        todo!()
     }
 }
+// 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(())
+//     }
+// }
+
+// pub struct Caller(pub Rc<dyn Fn(Context) -> DomTree>);
+// impl std::fmt::Debug for Caller {
+//     fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+//         Ok(())
+//     }
+// }
 
 impl<'a> VComponent<'a> {
     // 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)
     // TODO: lift the requirement that props need to be static
     // 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 {
-        let caller_ref = caller as *const ();
-
-        let props = Rc::new(props);
-
-        // used for memoization
-        let p1 = props.clone();
-
-        // let props_comparator = move |new_props| -> bool {
-        //     false
-        // let p = new_props.downcast_ref::<P::StaticOutput>().expect("");
-        // let r = unsafe { std::mem::transmute::<_, &P>(p) };
-        // &r == p1.as_ref()
-        // };
+    pub fn new<P: Properties + 'a>(component: FC<P>, props: &'a P) -> Self {
+        let caller_ref = component as *const ();
+
+        let raw_props = props as *const P as *const ();
+
+        let props_comparator = move |other: &VComponent| {
+            // Safety:
+            // We are guaranteed that the props will be of the same type because
+            // there is no way to create a VComponent other than this `new` method.
+            //
+            // Therefore, if the render functions are identical (by address), then so will be
+            // props type paramter (because it is the same render function). Therefore, we can be
+            // sure
+            if caller_ref == other.caller_ref {
+                let real_other = unsafe { &*(other.raw_props as *const _ as *const P) };
+                real_other == props
+            } else {
+                false
+            }
+        };
 
-        // used for actually rendering the custom component
-        let p2 = props.clone();
         let caller = move |ctx: Context| -> DomTree {
             // cast back into the right lifetime
-            caller(ctx, p2.as_ref())
+            let safe_props: &P = unsafe { &*(raw_props as *const P) };
+            component(ctx, props)
         };
 
-        todo!()
-        // Self {
-        //     caller_ref,
-        //     raw_props: props,
-        //     _p: PhantomData,
-        //     comparator: Comparator(Rc::new(props_comparator)),
-        //     caller: Caller(Rc::new(caller)),
-        //     stable_addr: Rc::new(RefCell::new(None)),
-        // }
+        Self {
+            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)),
+        }
     }
 }

+ 85 - 46
packages/core/src/scope.rs

@@ -1,15 +1,22 @@
 use crate::innerlude::*;
 use bumpalo::Bump;
 
-use std::{any::Any, cell::RefCell, collections::HashSet, marker::PhantomData, ops::Deref};
-
-pub trait Scoped {
-    fn run(&mut self);
-    // fn compare_props(&self, new: &dyn std::any::Any) -> bool;
-    fn call_listener(&mut self, trigger: EventTrigger);
-    fn new_frame<'bump>(&'bump self) -> &'bump VNode<'bump>;
-    fn old_frame<'bump>(&'bump self) -> &'bump VNode<'bump>;
-}
+use std::{
+    any::Any,
+    cell::RefCell,
+    collections::HashSet,
+    marker::PhantomData,
+    ops::Deref,
+    rc::{Rc, Weak},
+};
+
+// pub trait Scoped {
+//     fn run(&mut self);
+//     // fn compare_props(&self, new: &dyn std::any::Any) -> bool;
+//     fn call_listener(&mut self, trigger: EventTrigger);
+//     fn new_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`.
 ///
@@ -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.
 /// 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
     pub parent: Option<ScopeIdx>,
 
@@ -27,12 +35,15 @@ pub struct Scope<P: Properties> {
     //
     pub children: HashSet<ScopeIdx>,
 
+    pub caller: Weak<dyn Fn(Context) -> DomTree + 'static>,
+
     // // the props
     // pub props: P,
 
     // and the actual render function
-    pub caller: Caller,
-    _p: std::marker::PhantomData<P>,
+    // pub caller: *const dyn Fn(Context) -> DomTree,
+    // _p: std::marker::PhantomData<P>,
+    // _p: std::marker::PhantomData<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
 // todo: do the unsafety magic stuff to erase the type of p
-pub fn create_scoped(
-    // raw_f: FC<P>,
-    caller: Caller,
-
-    // props: P,
-    myidx: ScopeIdx,
-    parent: Option<ScopeIdx>,
-) -> Box<dyn Scoped> {
-    Box::new(Scope::<()> {
-        // raw_caller: raw_f,
-        _p: Default::default(),
-        caller,
-        myidx,
-        hook_arena: typed_arena::Arena::new(),
-        hooks: RefCell::new(Vec::new()),
-        frames: ActiveFrame::new(),
-        children: HashSet::new(),
-        listeners: Default::default(),
-        parent,
-        // props,
-    })
-}
+// pub fn create_scoped(
+//     // raw_f: FC<P>,
+//     // caller: Caller,
+
+//     // props: P,
+//     myidx: ScopeIdx,
+//     parent: Option<ScopeIdx>,
+// ) -> Box<dyn Scoped> {
+//     Box::new(Scope::<()> {
+//         // raw_caller: raw_f,
+//         _p: Default::default(),
+//         // caller,
+//         myidx,
+//         hook_arena: typed_arena::Arena::new(),
+//         hooks: RefCell::new(Vec::new()),
+//         frames: ActiveFrame::new(),
+//         children: HashSet::new(),
+//         listeners: Default::default(),
+//         parent,
+//         // props,
+//     })
+// }
+
+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> {
+    // impl<P: Properties + 'static> Scoped for Scope<P> {
     /// 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
     ///
     /// 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 = self.frames.next();
             frame.bump.reset();
@@ -105,8 +143,9 @@ impl<P: Properties + 'static> Scoped for Scope<P> {
             listeners: &self.listeners,
         };
 
+        todo!()
         // 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);
 
         /*
@@ -121,14 +160,14 @@ impl<P: Properties + 'static> Scoped for Scope<P> {
         - Public API cannot drop or destructure VNode
         */
 
-        frame.head_node = node_slot
-            .deref()
-            .borrow_mut()
-            .take()
-            .expect("Viewing did not happen");
+        // frame.head_node = node_slot
+        //     .deref()
+        //     .borrow_mut()
+        //     .take()
+        //     .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>()
     //         .map(|f| &self.props == f)
     //         .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
     // calling listeners will invalidate the list of 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 {
             listener_id,
             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()
     }
 
-    fn old_frame<'bump>(&'bump self) -> &'bump VNode<'bump> {
+    pub fn old_frame<'bump>(&'bump self) -> &'bump VNode<'bump> {
         self.frames.prev_head_node()
     }
 }

+ 16 - 15
packages/core/src/virtual_dom.rs

@@ -1,13 +1,14 @@
 // use crate::{changelist::EditList, nodes::VNode};
 
 use crate::innerlude::*;
-use crate::{
-    patch::Edit,
-    scope::{create_scoped, Scoped},
-};
+use crate::{patch::Edit, scope::Scope};
 use bumpalo::Bump;
 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.
 /// 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
     /// like a generational typemap bump arena
     /// -> 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: Rc<RefCell<Arena<Box<dyn Scoped>>>>,
     /// The index of the root component.
     /// Will not be ready if the dom is fresh
     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.
     #[doc(hidden)]
     _root_prop_type: std::any::TypeId,
@@ -55,6 +58,7 @@ impl VirtualDom {
     pub fn new(root: FC<()>) -> Self {
         Self::new_with_props(root, ())
     }
+
     /// 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.
     ///
@@ -63,18 +67,15 @@ impl VirtualDom {
     pub fn new_with_props<P: Properties + 'static>(root: FC<P>, root_props: P) -> Self {
         let mut components = Arena::new();
 
-        // Create a reference to the component in the arena
-        // Note: we are essentially running the "Mount" lifecycle event manually while the vdom doesnt yet exist
-        let caller = Caller(Rc::new(move |ctx| {
-            //
-            root(ctx, &root_props)
-            // 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));
+        // the root is kept around
+        let root_caller: Rc<dyn Fn(Context) -> DomTree + 'static> =
+            Rc::new(move |ctx| root(ctx, &root_props));
+        let weak_caller: Weak<dyn Fn(Context) -> DomTree + 'static> = Rc::downgrade(&root_caller);
+        let base_scope = components.insert_with(move |id| Scope::new(weak_caller, id, None));
 
         Self {
             components,
+            root_caller,
             base_scope,
             diff_bump: Bump::new(),
             _root_prop_type: TypeId::of::<P>(),

+ 0 - 6
packages/web/examples/infer.rs

@@ -60,13 +60,7 @@ static Example2: FC<ExampleProps> = |ctx, props| {
 
 impl Properties for ExampleProps {
     type Builder = ExamplePropsBuilder<((),)>;
-    type StaticOutput = ExampleProps;
     fn builder() -> Self::Builder {
         ExampleProps::builder()
     }
-
-
-    unsafe fn into_static(self) -> Self::StaticOutput {
-        todo!()
-    }
 }