wip: remove bumpframe
This commit is contained in:
1 parent
fd0c384dac
commit
e4cda7c2cb
7 files changed
+236
-231
No files matched your search
@@ -125,7 +125,7 @@ impl<'bump> DiffState<'bump> {
|
|||||||
impl<'bump> ScopeArena {
|
impl<'bump> ScopeArena {
|
||||||
pub fn diff_scope(&'bump self, state: &mut DiffState<'bump>, id: &ScopeId) {
|
pub fn diff_scope(&'bump self, state: &mut DiffState<'bump>, id: &ScopeId) {
|
||||||
if let Some(component) = self.get_scope(&id) {
|
if let Some(component) = self.get_scope(&id) {
|
||||||
let (old, new) = (component.frames.wip_head(), component.frames.fin_head());
|
let (old, new) = (component.wip_head(), component.fin_head());
|
||||||
state.stack.push(DiffInstruction::Diff { new, old });
|
state.stack.push(DiffInstruction::Diff { new, old });
|
||||||
self.work(state, || false);
|
self.work(state, || false);
|
||||||
}
|
}
|
||||||
@@ -392,9 +392,7 @@ impl<'bump> ScopeArena {
|
|||||||
|
|
||||||
if !vcomponent.can_memoize {
|
if !vcomponent.can_memoize {
|
||||||
let cur_scope = self.get_scope(&parent_idx).unwrap();
|
let cur_scope = self.get_scope(&parent_idx).unwrap();
|
||||||
let extended = vcomponent as *const VComponent;
|
let extended = unsafe { std::mem::transmute(vcomponent) };
|
||||||
let extended: *const VComponent<'static> = unsafe { std::mem::transmute(extended) };
|
|
||||||
|
|
||||||
cur_scope.items.get_mut().borrowed_props.push(extended);
|
cur_scope.items.get_mut().borrowed_props.push(extended);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1296,12 +1294,8 @@ impl<'bump> ScopeArena {
|
|||||||
listener: &'bump Listener<'bump>,
|
listener: &'bump Listener<'bump>,
|
||||||
scope: &ScopeState,
|
scope: &ScopeState,
|
||||||
) {
|
) {
|
||||||
let long_listener: &'bump Listener<'static> = unsafe { std::mem::transmute(listener) };
|
let long_listener = unsafe { std::mem::transmute(listener) };
|
||||||
scope
|
scope.items.borrow_mut().listeners.push(long_listener)
|
||||||
.items
|
|
||||||
.borrow_mut()
|
|
||||||
.listeners
|
|
||||||
.push(long_listener as *const _)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn attach_suspended_node_to_scope(
|
fn attach_suspended_node_to_scope(
|
||||||
@@ -1315,12 +1309,12 @@ impl<'bump> ScopeArena {
|
|||||||
.and_then(|id| self.get_scope(&id))
|
.and_then(|id| self.get_scope(&id))
|
||||||
{
|
{
|
||||||
// safety: this lifetime is managed by the logic on scope
|
// safety: this lifetime is managed by the logic on scope
|
||||||
let extended: &VSuspended<'static> = unsafe { std::mem::transmute(suspended) };
|
let extended = unsafe { std::mem::transmute(suspended) };
|
||||||
scope
|
scope
|
||||||
.items
|
.items
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.suspended_nodes
|
.suspended_nodes
|
||||||
.insert(suspended.task_id, extended as *const _);
|
.insert(suspended.task_id, extended);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,7 +12,6 @@ Navigating this crate:
|
|||||||
|
|
||||||
Some utilities
|
Some utilities
|
||||||
*/
|
*/
|
||||||
pub(crate) mod bumpframe;
|
|
||||||
pub(crate) mod component;
|
pub(crate) mod component;
|
||||||
pub(crate) mod diff;
|
pub(crate) mod diff;
|
||||||
pub(crate) mod diff_stack;
|
pub(crate) mod diff_stack;
|
||||||
@@ -31,7 +30,6 @@ pub(crate) mod virtual_dom;
|
|||||||
pub mod debug_dom;
|
pub mod debug_dom;
|
||||||
|
|
||||||
pub(crate) mod innerlude {
|
pub(crate) mod innerlude {
|
||||||
pub(crate) use crate::bumpframe::*;
|
|
||||||
pub use crate::component::*;
|
pub use crate::component::*;
|
||||||
pub(crate) use crate::diff::*;
|
pub(crate) use crate::diff::*;
|
||||||
pub use crate::diff_stack::*;
|
pub use crate::diff_stack::*;
|
||||||
|
|||||||
@@ -21,9 +21,8 @@ use std::{
|
|||||||
/// It is used during the diffing/rendering process as a runtime key into an existing set of nodes. The "render" key
|
/// 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.
|
/// is essentially a unique key to guarantee safe usage of the Node.
|
||||||
pub struct NodeLink {
|
pub struct NodeLink {
|
||||||
frame_id: u32,
|
pub(crate) gen_id: u32,
|
||||||
gen_id: u32,
|
pub(crate) scope_id: ScopeId,
|
||||||
scope_id: ScopeId,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A composable "VirtualNode" to declare a User Interface in the Dioxus VirtualDOM.
|
/// A composable "VirtualNode" to declare a User Interface in the Dioxus VirtualDOM.
|
||||||
@@ -197,7 +196,6 @@ impl<'src> VNode<'src> {
|
|||||||
key: f.key,
|
key: f.key,
|
||||||
}),
|
}),
|
||||||
VNode::Linked(c) => VNode::Linked(NodeLink {
|
VNode::Linked(c) => VNode::Linked(NodeLink {
|
||||||
frame_id: c.frame_id,
|
|
||||||
gen_id: c.gen_id,
|
gen_id: c.gen_id,
|
||||||
scope_id: c.scope_id,
|
scope_id: c.scope_id,
|
||||||
}),
|
}),
|
||||||
@@ -222,8 +220,8 @@ impl Debug for VNode<'_> {
|
|||||||
VNode::Component(comp) => write!(s, "VComponent {{ fc: {:?}}}", comp.user_fc),
|
VNode::Component(comp) => write!(s, "VComponent {{ fc: {:?}}}", comp.user_fc),
|
||||||
VNode::Linked(c) => write!(
|
VNode::Linked(c) => write!(
|
||||||
s,
|
s,
|
||||||
"VCached {{ frame_id: {}, gen_id: {}, scope_id: {:?} }}",
|
"VCached {{ gen_id: {}, scope_id: {:?} }}",
|
||||||
c.frame_id, c.gen_id, c.scope_id
|
c.gen_id, c.scope_id
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,67 +3,51 @@ use bumpalo::Bump;
|
|||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
|
|
||||||
pub(crate) struct ActiveFrame {
|
pub(crate) struct ActiveFrame {
|
||||||
// We use a "generation" for users of contents in the bump frames to ensure their data isn't broken
|
pub cur_generation: Cell<usize>,
|
||||||
pub generation: Cell<usize>,
|
|
||||||
|
|
||||||
// The double-buffering situation that we will use
|
// The double-buffering situation that we will use
|
||||||
pub frames: [BumpFrame; 2],
|
pub frames: [BumpFrame; 2],
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct BumpFrame {
|
|
||||||
pub bump: Bump,
|
|
||||||
pub(crate) head_node: VNode<'static>,
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
// used internally for debugging
|
|
||||||
_name: &'static str,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ActiveFrame {
|
impl ActiveFrame {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let b1 = Bump::new();
|
let b1 = Bump::new();
|
||||||
let b2 = Bump::new();
|
let b2 = Bump::new();
|
||||||
|
|
||||||
let frame_a = BumpFrame {
|
let frame_a = BumpFrame {
|
||||||
head_node: VNode::Fragment(VFragment {
|
|
||||||
key: None,
|
|
||||||
children: &[],
|
|
||||||
}),
|
|
||||||
bump: b1,
|
bump: b1,
|
||||||
|
generation: 0.into(),
|
||||||
#[cfg(test)]
|
|
||||||
_name: "wip",
|
|
||||||
};
|
};
|
||||||
let frame_b = BumpFrame {
|
let frame_b = BumpFrame {
|
||||||
head_node: VNode::Fragment(VFragment {
|
|
||||||
key: None,
|
|
||||||
children: &[],
|
|
||||||
}),
|
|
||||||
bump: b2,
|
bump: b2,
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
_name: "fin",
|
|
||||||
};
|
|
||||||
Self {
|
|
||||||
generation: 0.into(),
|
generation: 0.into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
frames: [frame_a, frame_b],
|
frames: [frame_a, frame_b],
|
||||||
|
cur_generation: 0.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn reset_wip_frame(&mut self) {
|
pub unsafe fn reset_wip_frame(&self) {
|
||||||
self.wip_frame_mut().bump.reset()
|
// todo: unsafecell or something
|
||||||
|
let bump = self.wip_frame() as *const _ as *mut BumpFrame;
|
||||||
|
let g = &mut *bump;
|
||||||
|
g.bump.reset();
|
||||||
|
|
||||||
|
// self.wip_frame_mut().bump.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The "work in progress frame" represents the frame that is currently being worked on.
|
/// The "work in progress frame" represents the frame that is currently being worked on.
|
||||||
pub fn wip_frame(&self) -> &BumpFrame {
|
pub fn wip_frame(&self) -> &BumpFrame {
|
||||||
match self.generation.get() & 1 == 0 {
|
match self.cur_generation.get() & 1 == 0 {
|
||||||
true => &self.frames[0],
|
true => &self.frames[0],
|
||||||
false => &self.frames[1],
|
false => &self.frames[1],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wip_frame_mut(&mut self) -> &mut BumpFrame {
|
pub fn wip_frame_mut(&mut self) -> &mut BumpFrame {
|
||||||
match self.generation.get() & 1 == 0 {
|
match self.cur_generation.get() & 1 == 0 {
|
||||||
true => &mut self.frames[0],
|
true => &mut self.frames[0],
|
||||||
false => &mut self.frames[1],
|
false => &mut self.frames[1],
|
||||||
}
|
}
|
||||||
@@ -71,26 +55,26 @@ impl ActiveFrame {
|
|||||||
|
|
||||||
/// The finished frame represents the frame that has been "finished" and cannot be modified again
|
/// The finished frame represents the frame that has been "finished" and cannot be modified again
|
||||||
pub fn finished_frame(&self) -> &BumpFrame {
|
pub fn finished_frame(&self) -> &BumpFrame {
|
||||||
match self.generation.get() & 1 == 1 {
|
match self.cur_generation.get() & 1 == 1 {
|
||||||
true => &self.frames[0],
|
true => &self.frames[0],
|
||||||
false => &self.frames[1],
|
false => &self.frames[1],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Give out our self-referential item with our own borrowed lifetime
|
// /// Give out our self-referential item with our own borrowed lifetime
|
||||||
pub fn fin_head<'b>(&'b self) -> &'b VNode<'b> {
|
// pub fn fin_head<'b>(&'b self) -> &'b VNode<'b> {
|
||||||
let cur_head = &self.finished_frame().head_node;
|
// let cur_head = &self.finished_frame().head_node;
|
||||||
unsafe { std::mem::transmute::<&VNode<'static>, &VNode<'b>>(cur_head) }
|
// unsafe { std::mem::transmute::<&VNode<'static>, &VNode<'b>>(cur_head) }
|
||||||
}
|
// }
|
||||||
|
|
||||||
/// Give out our self-referential item with our own borrowed lifetime
|
// /// Give out our self-referential item with our own borrowed lifetime
|
||||||
pub fn wip_head<'b>(&'b self) -> &'b VNode<'b> {
|
// pub fn wip_head<'b>(&'b self) -> &'b VNode<'b> {
|
||||||
let cur_head = &self.wip_frame().head_node;
|
// let cur_head = &self.wip_frame().head_node;
|
||||||
unsafe { std::mem::transmute::<&VNode<'static>, &VNode<'b>>(cur_head) }
|
// unsafe { std::mem::transmute::<&VNode<'static>, &VNode<'b>>(cur_head) }
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn cycle_frame(&mut self) {
|
pub fn cycle_frame(&mut self) {
|
||||||
self.generation.set(self.generation.get() + 1);
|
self.cur_generation.set(self.cur_generation.get() + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,6 +101,6 @@ mod tests {
|
|||||||
assert_eq!(fin._name, "wip");
|
assert_eq!(fin._name, "wip");
|
||||||
frames.cycle_frame();
|
frames.cycle_frame();
|
||||||
}
|
}
|
||||||
assert_eq!(frames.generation.get(), 10);
|
assert_eq!(frames.cur_generation.get(), 10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+134
-140
@@ -47,6 +47,8 @@ pub struct ScopeState {
|
|||||||
// safety:
|
// safety:
|
||||||
//
|
//
|
||||||
// pointers to scopes are *always* valid since they are bump allocated and never freed until this scope is also freed
|
// pointers to scopes are *always* valid since they are bump allocated and never freed until this scope is also freed
|
||||||
|
// this is just a bit of a hack to not need an Rc to the ScopeArena.
|
||||||
|
// todo: replace this will ScopeId and provide a connection to scope arena directly
|
||||||
pub(crate) parent_scope: Option<*mut ScopeState>,
|
pub(crate) parent_scope: Option<*mut ScopeState>,
|
||||||
|
|
||||||
pub(crate) our_arena_idx: ScopeId,
|
pub(crate) our_arena_idx: ScopeId,
|
||||||
@@ -57,11 +59,14 @@ pub struct ScopeState {
|
|||||||
|
|
||||||
pub(crate) is_subtree_root: Cell<bool>,
|
pub(crate) is_subtree_root: Cell<bool>,
|
||||||
|
|
||||||
// Nodes
|
// The double-buffering situation that we will use
|
||||||
pub(crate) frames: ActiveFrame,
|
pub(crate) frames: [Bump; 2],
|
||||||
|
|
||||||
pub(crate) vcomp: *const VComponent<'static>,
|
pub(crate) vcomp: *const VComponent<'static>,
|
||||||
|
|
||||||
|
pub(crate) old_root: RefCell<Option<NodeLink>>,
|
||||||
|
pub(crate) new_root: RefCell<Option<NodeLink>>,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
we care about:
|
we care about:
|
||||||
- listeners (and how to call them when an event is triggered)
|
- listeners (and how to call them when an event is triggered)
|
||||||
@@ -81,136 +86,20 @@ pub struct ScopeState {
|
|||||||
|
|
||||||
pub struct SelfReferentialItems<'a> {
|
pub struct SelfReferentialItems<'a> {
|
||||||
// nodes stored by "cx.render"
|
// nodes stored by "cx.render"
|
||||||
pub(crate) cached_nodes: Vec<VNode<'a>>,
|
pub(crate) cached_nodes_old: Vec<VNode<'a>>,
|
||||||
|
pub(crate) cached_nodes_new: Vec<VNode<'a>>,
|
||||||
|
|
||||||
pub(crate) generation: u32,
|
pub(crate) generation: Cell<u32>,
|
||||||
|
|
||||||
pub(crate) listeners: Vec<*const Listener<'a>>,
|
pub(crate) listeners: Vec<&'a Listener<'a>>,
|
||||||
pub(crate) borrowed_props: Vec<*const VComponent<'a>>,
|
pub(crate) borrowed_props: Vec<&'a VComponent<'a>>,
|
||||||
pub(crate) suspended_nodes: FxHashMap<u64, *const VSuspended<'a>>,
|
pub(crate) suspended_nodes: FxHashMap<u64, &'a VSuspended<'a>>,
|
||||||
pub(crate) tasks: Vec<BumpBox<'a, dyn Future<Output = ()>>>,
|
pub(crate) tasks: Vec<BumpBox<'a, dyn Future<Output = ()>>>,
|
||||||
pub(crate) pending_effects: Vec<BumpBox<'a, dyn FnMut()>>,
|
pub(crate) pending_effects: Vec<BumpBox<'a, dyn FnMut()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ScopeVcomp {
|
// Public methods exposed to libraries and components
|
||||||
// important things
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ScopeState {
|
impl ScopeState {
|
||||||
/// This method cleans up any references to data held within our hook list. This prevents mutable aliasing from
|
|
||||||
/// causing UB in our tree.
|
|
||||||
///
|
|
||||||
/// This works by cleaning up our references from the bottom of the tree to the top. The directed graph of components
|
|
||||||
/// essentially forms a dependency tree that we can traverse from the bottom to the top. As we traverse, we remove
|
|
||||||
/// any possible references to the data in the hook list.
|
|
||||||
///
|
|
||||||
/// References to hook data can only be stored in listeners and component props. During diffing, we make sure to log
|
|
||||||
/// all listeners and borrowed props so we can clear them here.
|
|
||||||
///
|
|
||||||
/// This also makes sure that drop order is consistent and predictable. All resources that rely on being dropped will
|
|
||||||
/// be dropped.
|
|
||||||
pub(crate) fn ensure_drop_safety(&self) {
|
|
||||||
// make sure we drop all borrowed props manually to guarantee that their drop implementation is called before we
|
|
||||||
// run the hooks (which hold an &mut Reference)
|
|
||||||
// right now, we don't drop
|
|
||||||
self.items
|
|
||||||
.borrow_mut()
|
|
||||||
.borrowed_props
|
|
||||||
.drain(..)
|
|
||||||
.map(|li| unsafe { &*li })
|
|
||||||
.for_each(|comp| {
|
|
||||||
// First drop the component's undropped references
|
|
||||||
let scope_id = comp
|
|
||||||
.associated_scope
|
|
||||||
.get()
|
|
||||||
.expect("VComponents should be associated with a valid Scope");
|
|
||||||
|
|
||||||
todo!("move this onto virtualdom");
|
|
||||||
// let scope = unsafe { &mut *scope_id };
|
|
||||||
|
|
||||||
// scope.ensure_drop_safety();
|
|
||||||
|
|
||||||
todo!("drop the component's props");
|
|
||||||
// let mut drop_props = comp.drop_props.borrow_mut().take().unwrap();
|
|
||||||
// drop_props();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Now that all the references are gone, we can safely drop our own references in our listeners.
|
|
||||||
self.items
|
|
||||||
.borrow_mut()
|
|
||||||
.listeners
|
|
||||||
.drain(..)
|
|
||||||
.map(|li| unsafe { &*li })
|
|
||||||
.for_each(|listener| drop(listener.callback.borrow_mut().take()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A safe wrapper around calling listeners
|
|
||||||
pub(crate) fn call_listener(&self, event: UserEvent, element: ElementId) {
|
|
||||||
let listners = &mut self.items.borrow_mut().listeners;
|
|
||||||
|
|
||||||
let raw_listener = listners.iter().find(|lis| {
|
|
||||||
let search = unsafe { &***lis };
|
|
||||||
if search.event == event.name {
|
|
||||||
let search_id = search.mounted_node.get();
|
|
||||||
search_id.map(|f| f == element).unwrap_or(false)
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(raw_listener) = raw_listener {
|
|
||||||
let listener = unsafe { &**raw_listener };
|
|
||||||
let mut cb = listener.callback.borrow_mut();
|
|
||||||
if let Some(cb) = cb.as_mut() {
|
|
||||||
(cb)(event.event);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log::warn!("An event was triggered but there was no listener to handle it");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// General strategy here is to load up the appropriate suspended task and then run it.
|
|
||||||
// Suspended nodes cannot be called repeatedly.
|
|
||||||
pub(crate) fn call_suspended_node<'a>(&'a mut self, task_id: u64) {
|
|
||||||
let mut nodes = &mut self.items.get_mut().suspended_nodes;
|
|
||||||
|
|
||||||
if let Some(suspended) = nodes.remove(&task_id) {
|
|
||||||
let sus: &'a VSuspended<'static> = unsafe { &*suspended };
|
|
||||||
let sus: &'a VSuspended<'a> = unsafe { std::mem::transmute(sus) };
|
|
||||||
let mut boxed = sus.callback.borrow_mut().take().unwrap();
|
|
||||||
let new_node: Element = boxed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// run the list of effects
|
|
||||||
pub(crate) fn run_effects(&mut self) {
|
|
||||||
// pub(crate) fn run_effects(&mut self, pool: &ResourcePool) {
|
|
||||||
for mut effect in self.items.get_mut().pending_effects.drain(..) {
|
|
||||||
effect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn new_subtree(&self) -> Option<u32> {
|
|
||||||
todo!()
|
|
||||||
// if self.is_subtree_root.get() {
|
|
||||||
// None
|
|
||||||
// } else {
|
|
||||||
// let cur = self.shared.cur_subtree.get();
|
|
||||||
// self.shared.cur_subtree.set(cur + 1);
|
|
||||||
// Some(cur)
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn update_vcomp(&self, vcomp: &VComponent) {
|
|
||||||
let f: *const _ = vcomp;
|
|
||||||
todo!()
|
|
||||||
// self.vcomp = unsafe { std::mem::transmute(f) };
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn load_vcomp<'a>(&'a mut self) -> &'a VComponent<'a> {
|
|
||||||
unsafe { std::mem::transmute(&*self.vcomp) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the root VNode for this Scope.
|
/// Get the root VNode for this Scope.
|
||||||
///
|
///
|
||||||
/// This VNode is the "entrypoint" VNode. If the component renders multiple nodes, then this VNode will be a fragment.
|
/// This VNode is the "entrypoint" VNode. If the component renders multiple nodes, then this VNode will be a fragment.
|
||||||
@@ -227,7 +116,7 @@ impl ScopeState {
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn root_node(&self) -> &VNode {
|
pub fn root_node(&self) -> &VNode {
|
||||||
self.frames.fin_head()
|
self.fin_head()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the subtree ID that this scope belongs to.
|
/// Get the subtree ID that this scope belongs to.
|
||||||
@@ -316,7 +205,7 @@ impl ScopeState {
|
|||||||
let chan = self.sender.clone();
|
let chan = self.sender.clone();
|
||||||
let id = self.scope_id();
|
let id = self.scope_id();
|
||||||
Rc::new(move || {
|
Rc::new(move || {
|
||||||
chan.unbounded_send(SchedulerMsg::Immediate(id));
|
let _ = chan.unbounded_send(SchedulerMsg::Immediate(id));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -328,7 +217,7 @@ impl ScopeState {
|
|||||||
pub fn schedule_update_any(&self) -> Rc<dyn Fn(ScopeId)> {
|
pub fn schedule_update_any(&self) -> Rc<dyn Fn(ScopeId)> {
|
||||||
let chan = self.sender.clone();
|
let chan = self.sender.clone();
|
||||||
Rc::new(move |id| {
|
Rc::new(move |id| {
|
||||||
chan.unbounded_send(SchedulerMsg::Immediate(id));
|
let _ = chan.unbounded_send(SchedulerMsg::Immediate(id));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,17 +232,14 @@ impl ScopeState {
|
|||||||
///
|
///
|
||||||
/// `ScopeId` is not unique for the lifetime of the VirtualDom - a ScopeId will be reused if a component is unmounted.
|
/// `ScopeId` is not unique for the lifetime of the VirtualDom - a ScopeId will be reused if a component is unmounted.
|
||||||
pub fn needs_update_any(&self, id: ScopeId) {
|
pub fn needs_update_any(&self, id: ScopeId) {
|
||||||
self.sender
|
let _ = self.sender.unbounded_send(SchedulerMsg::Immediate(id));
|
||||||
.unbounded_send(SchedulerMsg::Immediate(id))
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the [`ScopeId`] of a mounted component.
|
/// Get the [`ScopeId`] of a mounted component.
|
||||||
///
|
///
|
||||||
/// `ScopeId` is not unique for the lifetime of the VirtualDom - a ScopeId will be reused if a component is unmounted.
|
/// `ScopeId` is not unique for the lifetime of the VirtualDom - a ScopeId will be reused if a component is unmounted.
|
||||||
pub fn bump(&self) -> &Bump {
|
pub fn bump(&self) -> &Bump {
|
||||||
let bump = &self.frames.wip_frame().bump;
|
&self.wip_frame()
|
||||||
bump
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Take a lazy VNode structure and actually build it with the context of the VDom's efficient VNode allocator.
|
/// Take a lazy VNode structure and actually build it with the context of the VDom's efficient VNode allocator.
|
||||||
@@ -363,20 +249,28 @@ impl ScopeState {
|
|||||||
/// ## Example
|
/// ## Example
|
||||||
///
|
///
|
||||||
/// ```ignore
|
/// ```ignore
|
||||||
/// fn Component(cx: Context<()>) -> VNode {
|
/// fn Component(cx: Scope, props: &Props) -> Element {
|
||||||
/// // Lazy assemble the VNode tree
|
/// // Lazy assemble the VNode tree
|
||||||
/// let lazy_tree = html! {<div> "Hello World" </div>};
|
/// let lazy_nodes = rsx!("hello world");
|
||||||
///
|
///
|
||||||
/// // Actually build the tree and allocate it
|
/// // Actually build the tree and allocate it
|
||||||
/// cx.render(lazy_tree)
|
/// cx.render(lazy_tree)
|
||||||
/// }
|
/// }
|
||||||
///```
|
///```
|
||||||
pub fn render<'src>(&'src self, lazy_nodes: Option<LazyNodes<'src, '_>>) -> Option<NodeLink> {
|
pub fn render<'src>(&'src self, lazy_nodes: Option<LazyNodes<'src, '_>>) -> Option<NodeLink> {
|
||||||
todo!()
|
let bump = &self.wip_frame();
|
||||||
// ) -> Option<VNode<'src>> {
|
let factory = NodeFactory { bump };
|
||||||
// let bump = &self.frames.wip_frame().bump;
|
let node = lazy_nodes.map(|f| f.call(factory))?;
|
||||||
// let factory = NodeFactory { bump };
|
|
||||||
// lazy_nodes.map(|f| f.call(factory))
|
self.items
|
||||||
|
.borrow_mut()
|
||||||
|
.cached_nodes_old
|
||||||
|
.push(unsafe { std::mem::transmute(node) });
|
||||||
|
|
||||||
|
Some(NodeLink {
|
||||||
|
gen_id: self.items.borrow().generation.get(),
|
||||||
|
scope_id: self.our_arena_idx,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Push an effect to be ran after the component has been successfully mounted to the dom
|
/// Push an effect to be ran after the component has been successfully mounted to the dom
|
||||||
@@ -544,3 +438,103 @@ Consult the `rules of hooks` to understand how to use hooks properly.
|
|||||||
You likely used the hook in a conditional. Hooks rely on consistent ordering between renders.
|
You likely used the hook in a conditional. Hooks rely on consistent ordering between renders.
|
||||||
Functions prefixed with "use" should never be called conditionally.
|
Functions prefixed with "use" should never be called conditionally.
|
||||||
"###;
|
"###;
|
||||||
|
|
||||||
|
// Important internal methods
|
||||||
|
impl ScopeState {
|
||||||
|
/// Give out our self-referential item with our own borrowed lifetime
|
||||||
|
pub(crate) fn fin_head<'b>(&'b self) -> &'b VNode<'b> {
|
||||||
|
todo!()
|
||||||
|
// let cur_head = &self.finished_frame().head_node;
|
||||||
|
// unsafe { std::mem::transmute::<&VNode<'static>, &VNode<'b>>(cur_head) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Give out our self-referential item with our own borrowed lifetime
|
||||||
|
pub(crate) fn wip_head<'b>(&'b self) -> &'b VNode<'b> {
|
||||||
|
todo!()
|
||||||
|
// let cur_head = &self.wip_frame().head_node;
|
||||||
|
// unsafe { std::mem::transmute::<&VNode<'static>, &VNode<'b>>(cur_head) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The "work in progress frame" represents the frame that is currently being worked on.
|
||||||
|
pub(crate) fn wip_frame(&self) -> &Bump {
|
||||||
|
todo!()
|
||||||
|
// match self.cur_generation.get() & 1 == 0 {
|
||||||
|
// true => &self.frames[0],
|
||||||
|
// false => &self.frames[1],
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn reset_wip_frame(&self) {
|
||||||
|
// todo: unsafecell or something
|
||||||
|
let bump = self.wip_frame() as *const _ as *mut Bump;
|
||||||
|
let g = &mut *bump;
|
||||||
|
g.reset();
|
||||||
|
|
||||||
|
// self.wip_frame_mut().bump.reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A safe wrapper around calling listeners
|
||||||
|
pub(crate) fn call_listener(&self, event: UserEvent, element: ElementId) {
|
||||||
|
let listners = &mut self.items.borrow_mut().listeners;
|
||||||
|
|
||||||
|
let listener = listners.iter().find(|lis| {
|
||||||
|
let search = lis;
|
||||||
|
if search.event == event.name {
|
||||||
|
let search_id = search.mounted_node.get();
|
||||||
|
search_id.map(|f| f == element).unwrap_or(false)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(listener) = listener {
|
||||||
|
let mut cb = listener.callback.borrow_mut();
|
||||||
|
if let Some(cb) = cb.as_mut() {
|
||||||
|
(cb)(event.event);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log::warn!("An event was triggered but there was no listener to handle it");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// General strategy here is to load up the appropriate suspended task and then run it.
|
||||||
|
// Suspended nodes cannot be called repeatedly.
|
||||||
|
pub(crate) fn call_suspended_node<'a>(&'a mut self, task_id: u64) {
|
||||||
|
let mut nodes = &mut self.items.get_mut().suspended_nodes;
|
||||||
|
|
||||||
|
if let Some(suspended) = nodes.remove(&task_id) {
|
||||||
|
let sus: &'a VSuspended<'static> = unsafe { &*suspended };
|
||||||
|
let sus: &'a VSuspended<'a> = unsafe { std::mem::transmute(sus) };
|
||||||
|
let mut boxed = sus.callback.borrow_mut().take().unwrap();
|
||||||
|
let new_node: Element = boxed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// run the list of effects
|
||||||
|
pub(crate) fn run_effects(&mut self) {
|
||||||
|
for mut effect in self.items.get_mut().pending_effects.drain(..) {
|
||||||
|
effect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn new_subtree(&self) -> Option<u32> {
|
||||||
|
todo!()
|
||||||
|
// if self.is_subtree_root.get() {
|
||||||
|
// None
|
||||||
|
// } else {
|
||||||
|
// let cur = self.shared.cur_subtree.get();
|
||||||
|
// self.shared.cur_subtree.set(cur + 1);
|
||||||
|
// Some(cur)
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn update_vcomp(&self, vcomp: &VComponent) {
|
||||||
|
let f: *const _ = vcomp;
|
||||||
|
todo!()
|
||||||
|
// self.vcomp = unsafe { std::mem::transmute(f) };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn load_vcomp<'a>(&'a mut self) -> &'a VComponent<'a> {
|
||||||
|
unsafe { std::mem::transmute(&*self.vcomp) }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -65,7 +65,7 @@ impl ScopeArena {
|
|||||||
height,
|
height,
|
||||||
subtree: Cell::new(subtree),
|
subtree: Cell::new(subtree),
|
||||||
is_subtree_root: Cell::new(false),
|
is_subtree_root: Cell::new(false),
|
||||||
frames: ActiveFrame::new(),
|
frames: [Bump::default(), Bump::default()],
|
||||||
vcomp,
|
vcomp,
|
||||||
|
|
||||||
hooks: Default::default(),
|
hooks: Default::default(),
|
||||||
@@ -77,9 +77,12 @@ impl ScopeArena {
|
|||||||
suspended_nodes: Default::default(),
|
suspended_nodes: Default::default(),
|
||||||
tasks: Default::default(),
|
tasks: Default::default(),
|
||||||
pending_effects: Default::default(),
|
pending_effects: Default::default(),
|
||||||
cached_nodes: Default::default(),
|
cached_nodes_old: Default::default(),
|
||||||
generation: Default::default(),
|
generation: Default::default(),
|
||||||
|
cached_nodes_new: todo!(),
|
||||||
}),
|
}),
|
||||||
|
old_root: todo!(),
|
||||||
|
new_root: todo!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let stable = self.bump.alloc(new_scope);
|
let stable = self.bump.alloc(new_scope);
|
||||||
@@ -101,5 +104,55 @@ impl ScopeArena {
|
|||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
// scopes never get dropepd
|
// These methods would normally exist on `scope` but they need access to *all* of the scopes
|
||||||
|
|
||||||
|
/// This method cleans up any references to data held within our hook list. This prevents mutable aliasing from
|
||||||
|
/// causing UB in our tree.
|
||||||
|
///
|
||||||
|
/// This works by cleaning up our references from the bottom of the tree to the top. The directed graph of components
|
||||||
|
/// essentially forms a dependency tree that we can traverse from the bottom to the top. As we traverse, we remove
|
||||||
|
/// any possible references to the data in the hook list.
|
||||||
|
///
|
||||||
|
/// References to hook data can only be stored in listeners and component props. During diffing, we make sure to log
|
||||||
|
/// all listeners and borrowed props so we can clear them here.
|
||||||
|
///
|
||||||
|
/// This also makes sure that drop order is consistent and predictable. All resources that rely on being dropped will
|
||||||
|
/// be dropped.
|
||||||
|
pub(crate) fn ensure_drop_safety(&self, scope_id: &ScopeId) {
|
||||||
|
let scope = self.get_scope(scope_id).unwrap();
|
||||||
|
|
||||||
|
// make sure we drop all borrowed props manually to guarantee that their drop implementation is called before we
|
||||||
|
// run the hooks (which hold an &mut Reference)
|
||||||
|
// right now, we don't drop
|
||||||
|
scope
|
||||||
|
.items
|
||||||
|
.borrow_mut()
|
||||||
|
.borrowed_props
|
||||||
|
.drain(..)
|
||||||
|
.for_each(|comp| {
|
||||||
|
// First drop the component's undropped references
|
||||||
|
let scope_id = comp
|
||||||
|
.associated_scope
|
||||||
|
.get()
|
||||||
|
.expect("VComponents should be associated with a valid Scope");
|
||||||
|
|
||||||
|
todo!("move this onto virtualdom");
|
||||||
|
// let scope = unsafe { &mut *scope_id };
|
||||||
|
|
||||||
|
// scope.ensure_drop_safety();
|
||||||
|
|
||||||
|
todo!("drop the component's props");
|
||||||
|
// let mut drop_props = comp.drop_props.borrow_mut().take().unwrap();
|
||||||
|
// drop_props();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Now that all the references are gone, we can safely drop our own references in our listeners.
|
||||||
|
scope
|
||||||
|
.items
|
||||||
|
.borrow_mut()
|
||||||
|
.listeners
|
||||||
|
.drain(..)
|
||||||
|
.map(|li| unsafe { &*li })
|
||||||
|
.for_each(|listener| drop(listener.callback.borrow_mut().take()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -36,8 +36,6 @@ use std::{
|
|||||||
rc::Rc,
|
rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::innerlude::*;
|
|
||||||
|
|
||||||
/// 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.
|
||||||
@@ -423,7 +421,7 @@ impl VirtualDom {
|
|||||||
|
|
||||||
if self.run_scope(&scopeid) {
|
if self.run_scope(&scopeid) {
|
||||||
let scope = self.scopes.get_scope(&scopeid).unwrap();
|
let scope = self.scopes.get_scope(&scopeid).unwrap();
|
||||||
let (old, new) = (scope.frames.wip_head(), scope.frames.fin_head());
|
let (old, new) = (scope.wip_head(), scope.fin_head());
|
||||||
diff_state.stack.scope_stack.push(scopeid);
|
diff_state.stack.scope_stack.push(scopeid);
|
||||||
diff_state.stack.push(DiffInstruction::Diff { new, old });
|
diff_state.stack.push(DiffInstruction::Diff { new, old });
|
||||||
}
|
}
|
||||||
@@ -543,7 +541,7 @@ impl VirtualDom {
|
|||||||
// Cycle to the next frame and then reset it
|
// Cycle to the next frame and then reset it
|
||||||
// This breaks any latent references, invalidating every pointer referencing into it.
|
// This breaks any latent references, invalidating every pointer referencing into it.
|
||||||
// Remove all the outdated listeners
|
// Remove all the outdated listeners
|
||||||
scope.ensure_drop_safety();
|
self.scopes.ensure_drop_safety(id);
|
||||||
|
|
||||||
// Safety:
|
// Safety:
|
||||||
// - We dropped the listeners, so no more &mut T can be used while these are held
|
// - We dropped the listeners, so no more &mut T can be used while these are held
|
||||||
@@ -551,11 +549,10 @@ impl VirtualDom {
|
|||||||
unsafe { scope.hooks.reset() };
|
unsafe { scope.hooks.reset() };
|
||||||
|
|
||||||
// Safety:
|
// Safety:
|
||||||
// - We've dropped all references to the wip bump frame
|
// - We've dropped all references to the wip bump frame with "ensure_drop_safety"
|
||||||
todo!("reset wip frame");
|
unsafe { scope.reset_wip_frame() };
|
||||||
// unsafe { scope.frames.reset_wip_frame() };
|
|
||||||
|
|
||||||
let items = scope.items.get_mut();
|
let mut items = scope.items.borrow_mut();
|
||||||
|
|
||||||
// just forget about our suspended nodes while we're at it
|
// just forget about our suspended nodes while we're at it
|
||||||
items.suspended_nodes.clear();
|
items.suspended_nodes.clear();
|
||||||
@@ -568,12 +565,12 @@ impl VirtualDom {
|
|||||||
log::debug!("Borrowed stuff is successfully cleared");
|
log::debug!("Borrowed stuff is successfully cleared");
|
||||||
|
|
||||||
// temporarily cast the vcomponent to the right lifetime
|
// temporarily cast the vcomponent to the right lifetime
|
||||||
let vcomp = scope.load_vcomp();
|
// let vcomp = scope.load_vcomp();
|
||||||
|
|
||||||
let render: &dyn Fn(&ScopeState) -> Element = todo!();
|
let render: &dyn Fn(&ScopeState) -> Element = todo!();
|
||||||
|
|
||||||
// Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders.
|
// Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders.
|
||||||
if let Some(builder) = render(scope) {
|
if let Some(key) = render(scope) {
|
||||||
todo!("attach the niode");
|
todo!("attach the niode");
|
||||||
// let new_head = builder.into_vnode(NodeFactory {
|
// let new_head = builder.into_vnode(NodeFactory {
|
||||||
// bump: &scope.frames.wip_frame().bump,
|
// bump: &scope.frames.wip_frame().bump,
|
||||||
@@ -589,19 +586,6 @@ impl VirtualDom {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reserve_node(&self, node: &VNode) -> ElementId {
|
|
||||||
todo!()
|
|
||||||
// self.node_reservations.insert(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn collect_garbage(&self, id: ElementId) {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn try_remove(&self, id: &ScopeId) -> Option<ScopeState> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum SchedulerMsg {
|
pub enum SchedulerMsg {
|
||||||
@@ -627,7 +611,7 @@ pub struct UserEvent {
|
|||||||
/// The name that the renderer will use to mount the listener.
|
/// The name that the renderer will use to mount the listener.
|
||||||
pub name: &'static str,
|
pub name: &'static str,
|
||||||
|
|
||||||
/// The type of event
|
/// Event Data
|
||||||
pub event: Box<dyn Any + Send>,
|
pub event: Box<dyn Any + Send>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user