浏览代码

Feat: remove our use of ouroborous.

Instead, we're just going stick with unsafely transmuting the static
lifetime when accessing the active frame. This is a bit scarier, but
greatly simplifies containing the VNode.
Jonathan Kelley 4 年之前
父节点
当前提交
bcbb93b
共有 5 个文件被更改,包括 137 次插入161 次删除
  1. 1 1
      packages/core/examples/step.rs
  2. 2 8
      packages/core/src/context.rs
  3. 7 7
      packages/core/src/lib.rs
  4. 111 130
      packages/core/src/scope.rs
  5. 16 15
      packages/core/src/virtual_dom.rs

+ 1 - 1
packages/core/examples/step.rs

@@ -5,7 +5,7 @@
 //!     render it again
 //!     consume the diffs and write that to a renderer
 
-use dioxus_core::{prelude::*, scope::Scope};
+use dioxus_core::prelude::*;
 
 fn main() -> Result<(), ()> {
     let p1 = Props { name: "bob".into() };

+ 2 - 8
packages/core/src/context.rs

@@ -30,15 +30,10 @@ use std::{
 pub struct Context<'src> {
     pub idx: AtomicUsize,
 
-    pub(crate) scope: &'src Scope,
-    /// Direct access to the properties used to create this component.
-    // pub props: &'src PropType,
-
     // Borrowed from scope
     pub(crate) arena: &'src typed_arena::Arena<Hook>,
     pub(crate) hooks: &'src RefCell<Vec<*mut Hook>>,
     pub(crate) bump: &'src Bump,
-    // pub(crate) components: &'src generational_arena::Arena<Scope>,
 
     // holder for the src lifetime
     // todo @jon remove this
@@ -74,7 +69,7 @@ impl<'a> Context<'a> {
     /// }
     ///```
     pub fn view(self, lazy_nodes: impl FnOnce(&'a Bump) -> VNode<'a> + 'a) -> VNode<'a> {
-        lazy_nodes(self.bump.borrow())
+        lazy_nodes(self.bump)
     }
 
     pub fn callback(&self, f: impl Fn(()) + 'static) {}
@@ -105,8 +100,7 @@ pub mod hooks {
     }
 
     impl<'a> Context<'a> {
-        // impl<'a, P> Context<'a> {
-        // impl<'a, P> Context<'a, P> {
+        /// TODO: @jon, rework this so we dont have to use unsafe to make hooks and then return them
         /// use_hook provides a way to store data between renders for functional components.
         /// todo @jon: ensure the hook arena is stable with pin or is stable by default
         pub fn use_hook<'internal, 'scope, InternalHookState: 'static, Output: 'internal>(

+ 7 - 7
packages/core/src/lib.rs

@@ -87,19 +87,19 @@ pub mod builder {
 
 // types used internally that are important
 pub(crate) mod innerlude {
-    pub use crate::component::{Component, Properties};
+    pub(crate) use crate::component::{Component, Properties};
     use crate::context::hooks::Hook;
-    pub use crate::context::Context;
-    pub use crate::error::{Error, Result};
+    pub(crate) use crate::context::Context;
+    pub(crate) use crate::error::{Error, Result};
     use crate::nodes;
-    pub use crate::scope::Scope;
-    pub use crate::virtual_dom::VirtualDom;
-    pub use nodes::*;
+    pub(crate) use crate::scope::Scope;
+    pub(crate) use crate::virtual_dom::VirtualDom;
+    pub(crate) use nodes::*;
 
     // pub use nodes::iterables::IterableNodes;
     /// This type alias is an internal way of abstracting over the static functions that represent components.
 
-    pub type FC<P> = for<'a> fn(Context<'a>, &'a P) -> &'a VNode<'a>;
+    pub type FC<P> = for<'a> fn(Context<'a>, &'a P) -> VNode<'a>;
     // pub type FC<P> = for<'a> fn(Context<'a, P>) -> VNode<'a>;
 
     // TODO @Jon, fix this

+ 111 - 130
packages/core/src/scope.rs

@@ -15,77 +15,13 @@ use std::{
     todo,
 };
 
-pub struct BumpContainer(pub UnsafeCell<Bump>);
-impl BumpContainer {
-    fn new() -> Self {
-        Self(UnsafeCell::new(Bump::new()))
-    }
-}
-
-impl Deref for BumpContainer {
-    type Target = Bump;
-
-    fn deref(&self) -> &Self::Target {
-        todo!()
-        // self.0.borrow()
-    }
-}
-impl DerefMut for BumpContainer {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        todo!()
-        // self.0.borrow_mut()
-    }
-}
-unsafe impl StableAddress for BumpContainer {}
-
-#[ouroboros::self_referencing]
-pub struct BumpFrame {
-    pub bump: BumpContainer,
-
-    #[covariant]
-    #[borrows(bump)]
-    pub head_node: &'this VNode<'this>,
-}
-
-pub struct ActiveFrame {
-    pub idx: AtomicUsize,
-    pub frames: [BumpFrame; 2],
-}
-
-impl ActiveFrame {
-    fn from_frames(a: BumpFrame, b: BumpFrame) -> Self {
-        Self {
-            idx: 0.into(),
-            frames: [a, b],
-        }
-    }
-
-    fn next(&self) -> &BumpFrame {
-        self.idx.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
-        let cur = self.idx.borrow().load(std::sync::atomic::Ordering::Relaxed);
-        match cur % 1 {
-            1 => &self.frames[1],
-            0 => &self.frames[0],
-            _ => unreachable!("mod cannot by non-zero"),
-        }
-    }
-    // fn next(&self) -> &BumpFrame {
-    //     self.idx.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
-    //     let cur = self.idx.borrow().load(std::sync::atomic::Ordering::Relaxed);
-    //     match cur % 2_usize {
-    //         1 => &self.frames[1],
-    //         0 => &self.frames[0],
-    //     }
-    // }
-}
-
 /// Every component in Dioxus is represented by a `Scope`.
 ///
 /// Scopes contain the state for hooks, the component's props, and other lifecycle information.
 ///
 /// 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 {
+pub(crate) struct Scope {
     // TODO @Jon
     // These hooks are actually references into the hook arena
     // These two could be combined with "OwningRef" to remove unsafe usage
@@ -107,6 +43,8 @@ pub struct Scope {
     // IE Which listeners need to be woken up?
     pub listeners: Vec<Box<dyn Fn()>>,
 
+    pub props: Box<dyn std::any::Any>,
+
     //
     pub props_type: TypeId,
     pub caller: *const i32,
@@ -128,7 +66,11 @@ pub struct Scope {
 
 impl Scope {
     // create a new scope from a function
-    pub(crate) fn new<T: 'static>(f: FC<T>, parent: Option<Index>) -> Self {
+    pub(crate) fn new<T: 'static>(
+        f: FC<T>,
+        props: impl Properties + 'static,
+        parent: Option<Index>,
+    ) -> Self {
         // Capture the props type
         let props_type = TypeId::of::<T>();
         let hook_arena = typed_arena::Arena::new();
@@ -137,24 +79,20 @@ impl Scope {
         // Capture the caller
         let caller = f as *const i32;
 
-        // Create the two buffers the componetn will render into
-        // There will always be an "old" and "new"
-
         let listeners = Vec::new();
 
-        let new_frame = BumpFrameBuilder {
-            bump: BumpContainer::new(),
-            head_node_builder: |bump| bump.alloc(VNode::text("")),
-        }
-        .build();
+        let old_frame = BumpFrame {
+            bump: Bump::new(),
+            head_node: VNode::text(""),
+        };
 
-        let old_frame = BumpFrameBuilder {
-            bump: BumpContainer::new(),
-            head_node_builder: |bump| bump.alloc(VNode::text("")),
-        }
-        .build();
+        let new_frame = BumpFrame {
+            bump: Bump::new(),
+            head_node: VNode::text(""),
+        };
 
         let frames = ActiveFrame::from_frames(old_frame, new_frame);
+        let props = Box::new(props);
 
         Self {
             hook_arena,
@@ -164,64 +102,107 @@ impl Scope {
             frames,
             listeners,
             parent,
+            props,
         }
     }
 
+    /// Update this component's props with a new set of props
+    ///
+    ///
+    pub(crate) fn update_props<P: Properties + Sized + 'static>(
+        &mut self,
+        new_props: Box<P>,
+    ) -> crate::error::Result<()> {
+        Ok(())
+    }
+
     /// 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)
-    pub(crate) fn run<'a, 'bump, P: Properties + ?Sized>(&'bump mut self, props: &'a P) {
-        // I really wanted to do this safely, but I don't think we can.
-        // We want to reset the bump before writing into it. This requires &mut to the bump
-        // Ouroborous lets us borrow with self, but the heads (IE the source) cannot be changed while the ref is live
-
-        // n.b, there might be a better way of doing this active frame stuff - perhaps swapping
-        let frame = self.frames.next();
-
-        frame.with_bump(|bump_container| {
-            let bump: &mut Bump = unsafe { &mut *bump_container.0.get() };
-            bump.reset();
-
-            let bump = &*bump;
-
-            let ctx: Context<'bump> = Context {
-                scope: &*self,
-                _p: PhantomData {},
-                arena: &self.hook_arena,
-                hooks: &self.hooks,
-                idx: 0.into(),
-                bump,
-            };
-
-            /*
-            SAFETY ALERT
-
-            This particular usage of transmute is outlined in its docs https://doc.rust-lang.org/std/mem/fn.transmute.html
-            We hide the generic bound on the function item by casting it to raw pointer. When the function is actually called,
-            we transmute the function back using the props as reference.
-
-            we could do a better check to make sure that the TypeID is correct before casting
-            --
-            This is safe because we check that the generic type matches before casting.
-            */
-            let caller = unsafe { std::mem::transmute::<*const i32, FC<P>>(self.caller) };
-            let nodes: &'bump VNode  = caller(ctx, props);
-        });
-
-        // let new_nodes = caller(ctx, props);
-        // let r = new_nodes as *const _;
-        // self.old_root = self.new_root;
-        // self.new_root = new_nodes as *const _;
-
-        // let old_nodes: &mut VNode<'static> = unsafe { &mut *self.root_node };
-
-        // TODO: Iterate through the new nodes
-        // move any listeners into ourself
-
-        // perform the diff, dumping into the mutable change list
-        // this doesnt perform any "diff compression" where an event and a re-render
-        // crate::diff::diff(old_nodes, &new_nodes);
+    pub(crate) fn run<'bump, P: Properties + Sized + 'static>(&'bump mut self) {
+        let frame = {
+            let frame = self.frames.next();
+            frame.bump.reset();
+            frame
+        };
+
+        let ctx: Context<'bump> = Context {
+            arena: &self.hook_arena,
+            hooks: &self.hooks,
+            bump: &frame.bump,
+            idx: 0.into(),
+            _p: PhantomData {},
+        };
+
+        /*
+        SAFETY ALERT
+
+        This particular usage of transmute is outlined in its docs https://doc.rust-lang.org/std/mem/fn.transmute.html
+        We hide the generic bound on the function item by casting it to raw pointer. When the function is actually called,
+        we transmute the function back using the props as reference.
+
+        we could do a better check to make sure that the TypeID is correct before casting
+        --
+        This is safe because we check that the generic type matches before casting.
+        */
+
+        let caller = unsafe { std::mem::transmute::<*const i32, FC<P>>(self.caller) };
+        let nodes: VNode<'bump> = caller(ctx, self.props.downcast_ref::<P>().unwrap());
+
+        /*
+        SAFETY ALERT
+
+        DO NOT USE THIS VNODE WITHOUT THE APPOPRIATE ACCESSORS.
+        KEEPING THIS STATIC REFERENCE CAN LEAD TO UB.
+
+        Some things to note:
+        - The VNode itself is bound to the lifetime, but it itself is owned by scope.
+        - The VNode has a private API and can only be used from accessors.
+        - Public API cannot drop or destructure VNode
+        */
+
+        let unsafe_node = unsafe { std::mem::transmute::<VNode<'bump>, VNode<'static>>(nodes) };
+        frame.head_node = unsafe_node;
+
+        todo!()
+    }
+
+    /// Accessor to get the root node and its children (safely)\
+    /// Scope is self-referntial, so we are forced to use the 'static lifetime to cheat
+    pub fn current_root_node<'bump>(&'bump self) -> &'bump VNode<'bump> {
+        todo!()
+    }
+    pub fn prev_root_node<'bump>(&'bump self) -> &'bump VNode<'bump> {
         todo!()
     }
 }
+
+pub struct BumpFrame {
+    pub bump: Bump,
+    pub head_node: VNode<'static>,
+}
+
+pub struct ActiveFrame {
+    pub idx: AtomicUsize,
+    pub frames: [BumpFrame; 2],
+}
+
+impl ActiveFrame {
+    fn from_frames(a: BumpFrame, b: BumpFrame) -> Self {
+        Self {
+            idx: 0.into(),
+            frames: [a, b],
+        }
+    }
+
+    fn next(&mut self) -> &mut BumpFrame {
+        self.idx.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
+        let cur = self.idx.borrow().load(std::sync::atomic::Ordering::Relaxed);
+        match cur % 1 {
+            1 => &mut self.frames[1],
+            0 => &mut self.frames[0],
+            _ => unreachable!("mod cannot by non-zero"),
+        }
+    }
+}

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

@@ -56,27 +56,28 @@ impl VirtualDom {
         // 3. Create the lifecycle queue
         // 4. Create the event queue
 
+        todo!();
         // Arena allocate all the components
         // This should make it *really* easy to store references in events and such
-        let mut components = Arena::new();
+        // let mut components = Arena::new();
 
         // Create a reference to the component in the arena
-        let base_scope = components.insert(Scope::new(root, None));
+        // let base_scope = components.insert(Scope::new(root, None));
 
-        // Create a new mount event with no root container
-        let first_event = LifecycleEvent::mount(base_scope, None, 0, root_props);
+        // // Create a new mount event with no root container
+        // let first_event = LifecycleEvent::mount(base_scope, None, 0, root_props);
 
-        // Create an event queue with a mount for the base scope
-        let event_queue = Rc::new(RefCell::new(vec![first_event].into_iter().collect()));
+        // // Create an event queue with a mount for the base scope
+        // let event_queue = Rc::new(RefCell::new(vec![first_event].into_iter().collect()));
 
-        let _root_prop_type = TypeId::of::<P>();
+        // let _root_prop_type = TypeId::of::<P>();
 
-        Self {
-            components,
-            base_scope,
-            event_queue,
-            _root_prop_type,
-        }
+        // Self {
+        //     components,
+        //     base_scope,
+        //     event_queue,
+        //     _root_prop_type,
+        // }
     }
 
     /// With access to the virtual dom, schedule an update to the Root component's props
@@ -207,8 +208,8 @@ impl VirtualDom {
                     // mount to the root
                 }
 
-                let g = props.as_ref();
-                scope.run(g);
+                // let g = props.as_ref();
+                // scope.run(g);
                 // scope.run(runner, props, dom);
             }