浏览代码

wip: it compiles cleanly

Jonathan Kelley 3 年之前
父节点
当前提交
fd0c384dac
共有 3 个文件被更改,包括 159 次插入245 次删除
  1. 13 67
      packages/core/src/diff.rs
  2. 17 2
      packages/core/src/scopearena.rs
  3. 129 176
      packages/core/src/virtual_dom.rs

+ 13 - 67
packages/core/src/diff.rs

@@ -108,7 +108,7 @@ pub struct DiffState<'bump> {
     pub mutations: Mutations<'bump>,
     pub(crate) stack: DiffStack<'bump>,
     pub seen_scopes: FxHashSet<ScopeId>,
-    pub(crate) cfg: DiffCfg,
+    pub force_diff: bool,
 }
 
 impl<'bump> DiffState<'bump> {
@@ -117,68 +117,14 @@ impl<'bump> DiffState<'bump> {
             mutations,
             stack: DiffStack::new(),
             seen_scopes: Default::default(),
-            cfg: Default::default(),
-        }
-    }
-}
-
-pub(crate) struct DiffCfg {
-    pub force_diff: bool,
-}
-impl Default for DiffCfg {
-    fn default() -> Self {
-        Self {
-            force_diff: Default::default(),
+            force_diff: false,
         }
     }
 }
 
-/// a "saved" form of a diff machine
-/// in regular diff machine, the &'bump reference is a stack borrow, but the
-/// bump lifetimes are heap borrows.
-pub(crate) struct SavedDiffWork<'bump> {
-    pub mutations: Mutations<'bump>,
-    pub stack: DiffStack<'bump>,
-    pub seen_scopes: FxHashSet<ScopeId>,
-}
-
-impl<'a> SavedDiffWork<'a> {
-    pub unsafe fn extend(self: SavedDiffWork<'a>) -> SavedDiffWork<'static> {
-        std::mem::transmute(self)
-    }
-
-    pub unsafe fn promote<'b>(self) -> DiffState<'b> {
-        let extended: SavedDiffWork<'b> = std::mem::transmute(self);
-        DiffState {
-            cfg: DiffCfg::default(),
-            mutations: extended.mutations,
-            stack: extended.stack,
-            seen_scopes: extended.seen_scopes,
-        }
-    }
-}
-
-impl<'bump> VirtualDom {
-    // impl<'bump> DiffState<'bump> {
-    // pub(crate) fn new(mutations: Mutations<'bump>) -> Self {
-    //     Self {
-    //         mutations,
-    //         cfg: DiffCfg::default(),
-    //         stack: DiffStack::new(),
-    //         seen_scopes: FxHashSet::default(),
-    //     }
-    // }
-
-    // pub fn save(self) -> SavedDiffWork<'bump> {
-    //     SavedDiffWork {
-    //         mutations: state.mutations,
-    //         stack: state.stack,
-    //         seen_scopes: self.seen_scopes,
-    //     }
-    // }
-
-    pub fn diff_scope(&'bump self, state: &mut DiffState<'bump>, id: ScopeId) {
-        if let Some(component) = self.scopes.get(&id) {
+impl<'bump> ScopeArena {
+    pub fn diff_scope(&'bump self, state: &mut DiffState<'bump>, id: &ScopeId) {
+        if let Some(component) = self.get_scope(&id) {
             let (old, new) = (component.frames.wip_head(), component.frames.fin_head());
             state.stack.push(DiffInstruction::Diff { new, old });
             self.work(state, || false);
@@ -382,7 +328,7 @@ impl<'bump> VirtualDom {
         state.stack.add_child_count(1);
 
         if let Some(cur_scope_id) = state.stack.current_scope() {
-            let scope = self.scopes.get(&cur_scope_id).unwrap();
+            let scope = self.get_scope(&cur_scope_id).unwrap();
 
             for listener in *listeners {
                 self.attach_listener_to_scope(state, listener, scope);
@@ -427,7 +373,7 @@ impl<'bump> VirtualDom {
         let parent_scope = self.get_scope(&parent_idx).unwrap();
 
         let new_idx: ScopeId = todo!();
-        // self.scopes
+        // self
         //     .new_with_key(fc_ptr, vcomp, parent_scope, height, subtree, sender);
 
         // .(|new_idx| {
@@ -445,7 +391,7 @@ impl<'bump> VirtualDom {
         vcomponent.associated_scope.set(Some(new_idx));
 
         if !vcomponent.can_memoize {
-            let cur_scope = self.scopes.get(&parent_idx).unwrap();
+            let cur_scope = self.get_scope(&parent_idx).unwrap();
             let extended = vcomponent as *const VComponent;
             let extended: *const VComponent<'static> = unsafe { std::mem::transmute(extended) };
 
@@ -456,7 +402,7 @@ impl<'bump> VirtualDom {
         //  add noderefs to current noderef list Noderefs
         //  add effects to current effect list Effects
 
-        let new_component = self.scopes.get(&new_idx).unwrap();
+        let new_component = self.get_scope(&new_idx).unwrap();
 
         log::debug!(
             "initializing component {:?} with height {:?}",
@@ -606,7 +552,7 @@ impl<'bump> VirtualDom {
         //
         // TODO: take a more efficient path than this
         if let Some(cur_scope_id) = state.stack.current_scope() {
-            let scope = self.scopes.get(&cur_scope_id).unwrap();
+            let scope = self.get_scope(&cur_scope_id).unwrap();
 
             if old.listeners.len() == new.listeners.len() {
                 for (old_l, new_l) in old.listeners.iter().zip(new.listeners.iter()) {
@@ -664,7 +610,7 @@ impl<'bump> VirtualDom {
             new.associated_scope.set(Some(scope_addr));
 
             // make sure the component's caller function is up to date
-            let scope = self.scopes.get(&scope_addr).unwrap();
+            let scope = self.get_scope(&scope_addr).unwrap();
             scope.update_vcomp(new);
 
             // React doesn't automatically memoize, but we do.
@@ -1312,7 +1258,7 @@ impl<'bump> VirtualDom {
 
                 VNode::Component(c) => {
                     let scope_id = c.associated_scope.get().unwrap();
-                    let scope = self.scopes.get(&scope_id).unwrap();
+                    let scope = self.get_scope(&scope_id).unwrap();
                     let root = scope.root_node();
                     self.remove_nodes(state, Some(root), gen_muts);
 
@@ -1366,7 +1312,7 @@ impl<'bump> VirtualDom {
         if let Some(scope) = state
             .stack
             .current_scope()
-            .and_then(|id| self.scopes.get(&id))
+            .and_then(|id| self.get_scope(&id))
         {
             // safety: this lifetime is managed by the logic on scope
             let extended: &VSuspended<'static> = unsafe { std::mem::transmute(suspended) };

+ 17 - 2
packages/core/src/scopearena.rs

@@ -21,18 +21,20 @@ pub(crate) struct ScopeArena {
     bump: Bump,
     scopes: Vec<*mut ScopeState>,
     free_scopes: Vec<ScopeId>,
+    pub(crate) sender: UnboundedSender<SchedulerMsg>,
 }
 
 impl ScopeArena {
-    pub fn new() -> Self {
+    pub fn new(sender: UnboundedSender<SchedulerMsg>) -> Self {
         Self {
             bump: Bump::new(),
             scopes: Vec::new(),
             free_scopes: Vec::new(),
+            sender,
         }
     }
 
-    pub fn get(&self, id: &ScopeId) -> Option<&ScopeState> {
+    pub fn get_scope(&self, id: &ScopeId) -> Option<&ScopeState> {
         unsafe { Some(&*self.scopes[id.0]) }
     }
 
@@ -86,5 +88,18 @@ impl ScopeArena {
         }
     }
 
+    pub fn try_remove(&self, id: &ScopeId) -> Option<ScopeState> {
+        todo!()
+    }
+
+    pub fn reserve_node(&self, node: &VNode) -> ElementId {
+        todo!()
+        // self.node_reservations.insert(id);
+    }
+
+    pub fn collect_garbage(&self, id: ElementId) {
+        todo!()
+    }
+
     // scopes never get dropepd
 }

+ 129 - 176
packages/core/src/virtual_dom.rs

@@ -88,8 +88,6 @@ pub struct VirtualDom {
     pending_messages: VecDeque<SchedulerMsg>,
     dirty_scopes: IndexSet<ScopeId>,
 
-    saved_state: Option<SavedDiffWork<'static>>,
-
     in_progress: bool,
 }
 
@@ -164,7 +162,7 @@ impl VirtualDom {
         sender: UnboundedSender<SchedulerMsg>,
         receiver: UnboundedReceiver<SchedulerMsg>,
     ) -> Self {
-        let mut scopes = ScopeArena::new();
+        let mut scopes = ScopeArena::new(sender.clone());
 
         let base_scope = scopes.new_with_key(
             //
@@ -191,7 +189,6 @@ impl VirtualDom {
             pending_futures: Default::default(),
             dirty_scopes: Default::default(),
 
-            saved_state: None,
             in_progress: false,
         }
     }
@@ -216,7 +213,7 @@ impl VirtualDom {
     ///
     ///
     pub fn get_scope<'a>(&'a self, id: &ScopeId) -> Option<&'a ScopeState> {
-        self.scopes.get(&id)
+        self.scopes.get_scope(&id)
     }
 
     /// Get an [`UnboundedSender`] handle to the channel used by the scheduler.
@@ -246,62 +243,73 @@ impl VirtualDom {
     /// This lets us poll async tasks during idle periods without blocking the main thread.
     pub async fn wait_for_work(&mut self) {
         // todo: poll the events once even if there is work to do to prevent starvation
-        if self.has_any_work() {
-            return;
-        }
-
-        struct PollTasks<'a> {
-            pending_futures: &'a FxHashSet<ScopeId>,
-            scopes: &'a ScopeArena,
-        }
-
-        impl<'a> Future for PollTasks<'a> {
-            type Output = ();
-
-            fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
-                let mut all_pending = true;
 
-                // Poll every scope manually
-                for fut in self.pending_futures.iter() {
-                    let scope = self.scopes.get(fut).expect("Scope should never be moved");
-
-                    let mut items = scope.items.borrow_mut();
-                    for task in items.tasks.iter_mut() {
-                        let task = task.as_mut();
+        // if there's no futures in the virtualdom, just wait for a scheduler message and put it into the queue to be processed
+        if self.pending_futures.is_empty() {
+            self.pending_messages
+                .push_front(self.receiver.next().await.unwrap());
+        } else {
+            struct PollTasks<'a> {
+                pending_futures: &'a FxHashSet<ScopeId>,
+                scopes: &'a ScopeArena,
+            }
 
-                        // todo: does this make sense?
-                        // I don't usually write futures by hand
-                        let unpinned = unsafe { Pin::new_unchecked(task) };
-                        match unpinned.poll(cx) {
-                            Poll::Ready(_) => {
-                                all_pending = false;
+            impl<'a> Future for PollTasks<'a> {
+                type Output = ();
+
+                fn poll(
+                    self: Pin<&mut Self>,
+                    cx: &mut std::task::Context<'_>,
+                ) -> Poll<Self::Output> {
+                    let mut all_pending = true;
+
+                    // Poll every scope manually
+                    for fut in self.pending_futures.iter() {
+                        let scope = self
+                            .scopes
+                            .get_scope(fut)
+                            .expect("Scope should never be moved");
+
+                        let mut items = scope.items.borrow_mut();
+                        for task in items.tasks.iter_mut() {
+                            let task = task.as_mut();
+
+                            // todo: does this make sense?
+                            // I don't usually write futures by hand
+                            // 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 unpinned = unsafe { Pin::new_unchecked(task) };
+
+                            if let Poll::Ready(_) = unpinned.poll(cx) {
+                                all_pending = false
                             }
-                            Poll::Pending => {}
                         }
                     }
-                }
 
-                // Resolve the future if any task is ready
-                match all_pending {
-                    true => Poll::Pending,
-                    false => Poll::Ready(()),
+                    // Resolve the future if any singular task is ready
+                    match all_pending {
+                        true => Poll::Pending,
+                        false => Poll::Ready(()),
+                    }
                 }
             }
-        }
 
-        let scheduler_fut = self.receiver.next();
-        let tasks_fut = PollTasks {
-            pending_futures: &self.pending_futures,
-            scopes: &self.scopes,
-        };
+            // Poll both the futures and the scheduler message queue simulataneously
+            use futures_util::future::{select, Either};
 
-        use futures_util::future::{select, Either};
-        match select(tasks_fut, scheduler_fut).await {
-            // Tasks themselves don't generate work
-            Either::Left((_, _)) => {}
+            let scheduler_fut = self.receiver.next();
+            let tasks_fut = PollTasks {
+                pending_futures: &self.pending_futures,
+                scopes: &self.scopes,
+            };
+
+            match select(tasks_fut, scheduler_fut).await {
+                // Futures don't generate work
+                Either::Left((_, _)) => {}
 
-            // Save these messages in FIFO to be processed later
-            Either::Right((msg, _)) => self.pending_messages.push_front(msg.unwrap()),
+                // Save these messages in FIFO to be processed later
+                Either::Right((msg, _)) => self.pending_messages.push_front(msg.unwrap()),
+            }
         }
     }
 
@@ -333,10 +341,15 @@ impl VirtualDom {
     ///
     /// ```no_run
     /// static App: FC<()> = |(cx, props)|rsx!(cx, div {"hello"} );
+    ///
     /// let mut dom = VirtualDom::new(App);
+    ///
     /// loop {
-    ///     let deadline = TimeoutFuture::from_ms(16);
+    ///     let mut timeout = TimeoutFuture::from_ms(16);
+    ///     let deadline = move || timeout.now_or_never();
+    ///
     ///     let mutations = dom.run_with_deadline(deadline).await;
+    ///
     ///     apply_mutations(mutations);
     /// }
     /// ```
@@ -352,124 +365,91 @@ impl VirtualDom {
         &'a mut self,
         mut deadline: impl FnMut() -> bool,
     ) -> Vec<Mutations<'a>> {
-        let mut committed_mutations = Vec::<Mutations<'static>>::new();
+        let mut committed_mutations = Vec::<Mutations>::new();
 
         while self.has_any_work() {
             while let Ok(Some(msg)) = self.receiver.try_next() {
                 self.pending_messages.push_front(msg);
             }
 
-            for msg in self.pending_messages.drain(..) {
+            while let Some(msg) = self.pending_messages.pop_back() {
                 match msg {
                     SchedulerMsg::Immediate(id) => {
-                        // it's dirty
                         self.dirty_scopes.insert(id);
                     }
                     SchedulerMsg::UiEvent(event) => {
-                        // there's an event
-                        let scope = self.scopes.get(&event.scope_id).unwrap();
                         if let Some(element) = event.mounted_dom_id {
                             log::info!("Calling listener {:?}, {:?}", event.scope_id, element);
 
+                            let scope = self.scopes.get_scope(&event.scope_id).unwrap();
+
                             // TODO: bubble properly here
                             scope.call_listener(event, element);
 
                             while let Ok(Some(dirty_scope)) = self.receiver.try_next() {
-                                match dirty_scope {
-                                    SchedulerMsg::Immediate(im) => {
-                                        self.dirty_scopes.insert(im);
-                                    }
-                                    SchedulerMsg::UiEvent(e) => self.ui_events.push_back(e),
-                                }
+                                self.pending_messages.push_front(dirty_scope);
                             }
+                        } else {
+                            log::debug!("User event without a targetted ElementId. Unsure how to proceed. {:?}", event);
                         }
                     }
                 }
             }
 
-            let work_complete = {
-                // Work through the current subtree, and commit the results when it finishes
-                // When the deadline expires, give back the work
-                let saved_state = unsafe { self.load_work() };
-
-                // We have to split away some parts of ourself - current lane is borrowed mutably
-                let mut machine = unsafe { saved_state.promote() };
-
-                let mut ran_scopes = FxHashSet::default();
-
-                if machine.stack.is_empty() {
-                    todo!("order scopes");
-                    // self.dirty_scopes.retain(|id| self.get_scope(id).is_some());
-                    // self.dirty_scopes.sort_by(|a, b| {
-                    //     let h1 = self.get_scope(a).unwrap().height;
-                    //     let h2 = self.get_scope(b).unwrap().height;
-                    //     h1.cmp(&h2).reverse()
-                    // });
-
-                    if let Some(scopeid) = self.dirty_scopes.pop() {
-                        log::info!("handling dirty scope {:?}", scopeid);
-                        if !ran_scopes.contains(&scopeid) {
-                            ran_scopes.insert(scopeid);
-                            log::debug!("about to run scope {:?}", scopeid);
-
-                            // if let Some(component) = self.get_scope_mut(&scopeid) {
-                            if self.run_scope(&scopeid) {
-                                todo!("diff the scope")
-                                // let (old, new) = (component.frames.wip_head(), component.frames.fin_head());
-                                // // let (old, new) = (component.frames.wip_head(), component.frames.fin_head());
-                                // machine.stack.scope_stack.push(scopeid);
-                                // machine.stack.push(DiffInstruction::Diff { new, old });
-                            }
-                            // }
-                        }
-                    }
-                }
+            let mut diff_state: DiffState = DiffState::new(Mutations::new());
 
-                let work_completed: bool = todo!();
-                // let work_completed = machine.work(deadline_reached);
+            let mut ran_scopes = FxHashSet::default();
 
-                // log::debug!("raw edits {:?}", machine.mutations.edits);
+            // todo: the 2021 version of rust will let us not have to force the borrow
+            let scopes = &self.scopes;
 
-                let mut machine: DiffState<'static> = unsafe { std::mem::transmute(machine) };
-                // let mut saved = machine.save();
+            // Sort the scopes by height. Theoretically, we'll de-duplicate scopes by height
+            self.dirty_scopes
+                .retain(|id| scopes.get_scope(id).is_some());
 
-                if work_completed {
-                    for node in machine.seen_scopes.drain() {
-                        // self.dirty_scopes.clear();
-                        // self.ui_events.clear();
-                        self.dirty_scopes.remove(&node);
-                        // self.dirty_scopes.remove(&node);
-                    }
-
-                    let mut new_mutations = Mutations::new();
+            self.dirty_scopes.sort_by(|a, b| {
+                let h1 = scopes.get_scope(a).unwrap().height;
+                let h2 = scopes.get_scope(b).unwrap().height;
+                h1.cmp(&h2).reverse()
+            });
 
-                    for edit in machine.mutations.edits.drain(..) {
-                        new_mutations.edits.push(edit);
-                    }
+            if let Some(scopeid) = self.dirty_scopes.pop() {
+                log::info!("handling dirty scope {:?}", scopeid);
 
-                    // for edit in saved.edits.drain(..) {
-                    //     new_mutations.edits.push(edit);
-                    // }
+                if !ran_scopes.contains(&scopeid) {
+                    ran_scopes.insert(scopeid);
 
-                    // std::mem::swap(&mut new_mutations, &mut saved.mutations);
+                    log::debug!("about to run scope {:?}", scopeid);
 
-                    mutations.push(new_mutations);
+                    if self.run_scope(&scopeid) {
+                        let scope = self.scopes.get_scope(&scopeid).unwrap();
+                        let (old, new) = (scope.frames.wip_head(), scope.frames.fin_head());
+                        diff_state.stack.scope_stack.push(scopeid);
+                        diff_state.stack.push(DiffInstruction::Diff { new, old });
+                    }
+                }
+            }
 
-                    // log::debug!("saved edits {:?}", mutations);
+            let work_completed = self.scopes.work(&mut diff_state, &mut deadline);
 
-                    todo!();
-                    // let mut saved = machine.save();
-                    // self.save_work(saved);
-                    true
+            if work_completed {
+                let DiffState {
+                    mutations,
+                    seen_scopes,
+                    stack,
+                    ..
+                } = diff_state;
 
-                    // self.save_work(saved);
-                    // false
-                } else {
-                    false
+                for scope in seen_scopes {
+                    self.dirty_scopes.remove(&scope);
                 }
-            };
 
-            if !work_complete {
+                // I think the stack should be empty at the end of diffing?
+                debug_assert_eq!(stack.scope_stack.len(), 0);
+
+                committed_mutations.push(mutations);
+            } else {
+                todo!("don't have a mechanism to pause work (yet)");
                 return committed_mutations;
             }
         }
@@ -495,7 +475,13 @@ impl VirtualDom {
     /// apply_edits(edits);
     /// ```
     pub fn rebuild(&mut self) -> Mutations {
-        self.hard_diff(&self.base_scope)
+        // todo: I think we need to append a node or something
+        //     diff_machine
+        //         .stack
+        //         .create_node(cur_component.frames.fin_head(), MountType::Append);
+
+        let scope = self.base_scope;
+        self.hard_diff(&scope).unwrap()
     }
 
     /// Compute a manual diff of the VirtualDOM between states.
@@ -532,59 +518,26 @@ impl VirtualDom {
     ///
     /// let edits = dom.diff();
     /// ```
-    pub fn hard_diff<'a>(&'a mut self, base_scope: &ScopeId) -> Mutations<'a> {
-        // // TODO: drain any in-flight work
-        // // We run the component. If it succeeds, then we can diff it and add the changes to the dom.
-        // if self.run_scope(&base_scope) {
-        //     let cur_component = self
-        //         .get_scope_mut(&base_scope)
-        //         .expect("The base scope should never be moved");
+    pub fn hard_diff<'a>(&'a mut self, scope_id: &ScopeId) -> Option<Mutations<'a>> {
+        log::debug!("hard diff {:?}", scope_id);
 
-        //     log::debug!("rebuild {:?}", base_scope);
-
-        //     let mut diff_machine = DiffState::new(Mutations::new());
-        //     diff_machine
-        //         .stack
-        //         .create_node(cur_component.frames.fin_head(), MountType::Append);
-
-        //     diff_machine.stack.scope_stack.push(base_scope);
-
-        //     todo!()
-        //     // self.work(&mut diff_machine, || false);
-        //     // diff_machine.work(|| false);
-        // } else {
-        //     // todo: should this be a hard error?
-        //     log::warn!(
-        //         "Component failed to run successfully during rebuild.
-        //         This does not result in a failed rebuild, but indicates a logic failure within your app."
-        //     );
-        // }
+        if self.run_scope(&scope_id) {
+            let mut diff_machine = DiffState::new(Mutations::new());
 
-        // todo!()
-        // // unsafe { std::mem::transmute(diff_machine.mutations) }
+            diff_machine.force_diff = true;
 
-        let cur_component = self
-            .scopes
-            .get(&base_scope)
-            .expect("The base scope should never be moved");
+            self.scopes.diff_scope(&mut diff_machine, scope_id);
 
-        log::debug!("hard diff {:?}", base_scope);
-
-        if self.run_scope(&base_scope) {
-            let mut diff_machine = DiffState::new(Mutations::new());
-            diff_machine.cfg.force_diff = true;
-            self.diff_scope(&mut diff_machine, base_scope);
-            // diff_machine.diff_scope(base_scope);
-            diff_machine.mutations
+            Some(diff_machine.mutations)
         } else {
-            Mutations::new()
+            None
         }
     }
 
-    pub fn run_scope(&mut self, id: &ScopeId) -> bool {
+    pub fn run_scope(&self, id: &ScopeId) -> bool {
         let scope = self
             .scopes
-            .get(id)
+            .get_scope(id)
             .expect("The base scope should never be moved");
 
         // Cycle to the next frame and then reset it