diff --git a/packages/core/Cargo.toml b/packages/core/Cargo.toml
index f779e6ba2..827968655 100644
--- a/packages/core/Cargo.toml
+++ b/packages/core/Cargo.toml
@@ -35,6 +35,8 @@ indexmap = "1.7"
serde = { version = "1", features = ["derive"], optional = true }
anyhow = "1.0.66"
+smallbox = "0.8.1"
+
[dev-dependencies]
tokio = { version = "*", features = ["full"] }
dioxus = { path = "../dioxus" }
diff --git a/packages/core/src/arena.rs b/packages/core/src/arena.rs
index d6af9b051..024ad5375 100644
--- a/packages/core/src/arena.rs
+++ b/packages/core/src/arena.rs
@@ -69,7 +69,6 @@ impl VirtualDom {
)
}
- println!("reclaiming {:?}", el);
self.elements.try_remove(el.0)
}
@@ -95,9 +94,7 @@ impl VirtualDom {
// Drop all the hooks once the children are dropped
// this means we'll drop hooks bottom-up
for hook in scope.hook_list.get_mut().drain(..) {
- println!("dropping hook !");
drop(unsafe { BumpBox::from_raw(hook) });
- println!("hook dropped !");
}
}
diff --git a/packages/core/src/create.rs b/packages/core/src/create.rs
index feaa276e4..b0a463c9e 100644
--- a/packages/core/src/create.rs
+++ b/packages/core/src/create.rs
@@ -388,11 +388,6 @@ impl<'b> VirtualDom {
// Set the placeholder of the scope
self.scopes[scope.0].placeholder.set(Some(new_id));
- println!(
- "assigning id {:?} to path {:?}, template: {:?}",
- new_id, &template.template.node_paths, template.template
- );
-
// Since the placeholder is already in the DOM, we don't create any new nodes
self.mutations.push(AssignId {
id: new_id,
diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs
index c0b14d0bd..28ffb2404 100644
--- a/packages/core/src/diff.rs
+++ b/packages/core/src/diff.rs
@@ -61,17 +61,10 @@ impl<'b> VirtualDom {
}
fn diff_node(&mut self, left_template: &'b VNode<'b>, right_template: &'b VNode<'b>) {
- println!(
- "diffing node {:#?},\n\n{:#?}",
- left_template.template.name, right_template.template.name
- );
-
if left_template.template.name != right_template.template.name {
return self.light_diff_templates(left_template, right_template);
}
- println!("diffing attributs...");
-
for (left_attr, right_attr) in left_template
.dynamic_attrs
.iter()
@@ -106,8 +99,6 @@ impl<'b> VirtualDom {
}
}
- println!("diffing dyn nodes...");
-
for (idx, (left_node, right_node)) in left_template
.dynamic_nodes
.iter()
@@ -133,8 +124,6 @@ impl<'b> VirtualDom {
};
}
- println!("applying roots...");
-
// Make sure the roots get transferred over
for (left, right) in left_template
.root_ids
@@ -178,7 +167,6 @@ impl<'b> VirtualDom {
) {
// Replace components that have different render fns
if left.render_fn != right.render_fn {
- dbg!(&left, &right);
let created = self.create_component_node(right_template, right, idx);
let head = unsafe {
self.scopes[left.scope.get().unwrap().0]
@@ -396,8 +384,6 @@ impl<'b> VirtualDom {
"all siblings must be keyed or all siblings must be non-keyed"
);
- println!("Diffing fragment {:?}, {:?}", old.len(), new.len());
-
if new_is_keyed && old_is_keyed {
self.diff_keyed_children(old, new);
} else {
@@ -420,9 +406,7 @@ impl<'b> VirtualDom {
debug_assert!(!new.is_empty());
debug_assert!(!old.is_empty());
- println!("Diffing non keyed children");
-
- match dbg!(old.len().cmp(&new.len())) {
+ match old.len().cmp(&new.len()) {
Ordering::Greater => self.remove_nodes(&old[new.len()..]),
Ordering::Less => self.create_and_insert_after(&new[old.len()..], old.last().unwrap()),
Ordering::Equal => {}
diff --git a/packages/core/src/lazynodes.rs b/packages/core/src/lazynodes.rs
index 020b307d9..72b5662fd 100644
--- a/packages/core/src/lazynodes.rs
+++ b/packages/core/src/lazynodes.rs
@@ -11,8 +11,13 @@
//! The logic for this was borrowed from . Unfortunately, this crate does not
//! support non-static closures, so we've implemented the core logic of `ValueA` in this module.
+use smallbox::{
+ smallbox,
+ space::{S1, S16, S4, S8},
+ SmallBox,
+};
+
use crate::{innerlude::VNode, ScopeState};
-use std::mem;
/// A concrete type provider for closures that build [`VNode`] structures.
///
@@ -24,14 +29,7 @@ use std::mem;
/// LazyNodes::new(|f| f.element("div", [], [], [] None))
/// ```
pub struct LazyNodes<'a, 'b> {
- inner: StackNodeStorage<'a, 'b>,
-}
-
-type StackHeapSize = [usize; 16];
-
-enum StackNodeStorage<'a, 'b> {
- Stack(LazyStack),
- Heap(Box) -> Option> + 'b>),
+ inner: SmallBox VNode<'a> + 'b, S16>,
}
impl<'a, 'b> LazyNodes<'a, 'b> {
@@ -44,114 +42,11 @@ impl<'a, 'b> LazyNodes<'a, 'b> {
// there's no way to call FnOnce without a box, so we need to store it in a slot and use static dispatch
let mut slot = Some(val);
- let val = move |fac: Option<&'a ScopeState>| {
- fac.map(
- slot.take()
- .expect("LazyNodes closure to be called only once"),
- )
- };
-
- // miri does not know how to work with mucking directly into bytes
- // just use a heap allocated type when miri is running
- if cfg!(miri) {
- Self {
- inner: StackNodeStorage::Heap(Box::new(val)),
- }
- } else {
- unsafe { LazyNodes::new_inner(val) }
- }
- }
-
- /// Create a new [`LazyNodes`] closure, but force it onto the heap.
- pub fn new_boxed(inner: F) -> Self
- where
- F: FnOnce(&'a ScopeState) -> VNode<'a> + 'b,
- {
- // there's no way to call FnOnce without a box, so we need to store it in a slot and use static dispatch
- let mut slot = Some(inner);
-
Self {
- inner: StackNodeStorage::Heap(Box::new(move |fac: Option<&'a ScopeState>| {
- fac.map(
- slot.take()
- .expect("LazyNodes closure to be called only once"),
- )
- })),
- }
- }
-
- unsafe fn new_inner(val: F) -> Self
- where
- F: FnMut(Option<&'a ScopeState>) -> Option> + 'b,
- {
- let mut ptr: *const _ = &val as &dyn FnMut(Option<&'a ScopeState>) -> Option>;
-
- assert_eq!(
- ptr as *const u8, &val as *const _ as *const u8,
- "MISUSE: Closure returned different pointer"
- );
- assert_eq!(
- std::mem::size_of_val(&*ptr),
- std::mem::size_of::(),
- "MISUSE: Closure returned a subset pointer"
- );
-
- let words = ptr_as_slice(&mut ptr);
- assert!(
- words[0] == &val as *const _ as usize,
- "BUG: Pointer layout is not (data_ptr, info...)"
- );
-
- // - Ensure that Self is aligned same as data requires
- assert!(
- std::mem::align_of::() <= std::mem::align_of::(),
- "TODO: Enforce alignment >{} (requires {})",
- std::mem::align_of::(),
- std::mem::align_of::()
- );
-
- let info = &words[1..];
- let data = words[0] as *mut ();
- let size = mem::size_of::();
-
- let stored_size = info.len() * mem::size_of::() + size;
- let max_size = mem::size_of::();
-
- if stored_size > max_size {
- Self {
- inner: StackNodeStorage::Heap(Box::new(val)),
- }
- } else {
- let mut buf: StackHeapSize = StackHeapSize::default();
-
- assert!(info.len() + round_to_words(size) <= buf.as_ref().len());
-
- // Place pointer information at the end of the region
- // - Allows the data to be at the start for alignment purposes
- {
- let info_ofs = buf.as_ref().len() - info.len();
- let info_dst = &mut buf.as_mut()[info_ofs..];
- for (d, v) in Iterator::zip(info_dst.iter_mut(), info.iter()) {
- *d = *v;
- }
- }
-
- let src_ptr = data as *const u8;
- let dataptr = buf.as_mut_ptr().cast::();
-
- for i in 0..size {
- *dataptr.add(i) = *src_ptr.add(i);
- }
-
- std::mem::forget(val);
-
- Self {
- inner: StackNodeStorage::Stack(LazyStack {
- _align: [],
- buf,
- dropped: false,
- }),
- }
+ inner: smallbox!(move |f| {
+ let val = slot.take().expect("cannot call LazyNodes twice");
+ val(f)
+ }),
}
}
@@ -163,88 +58,10 @@ impl<'a, 'b> LazyNodes<'a, 'b> {
/// let node = f.call(cac);
/// ```
#[must_use]
- pub fn call(self, f: &'a ScopeState) -> VNode<'a> {
- match self.inner {
- StackNodeStorage::Heap(mut lazy) => {
- lazy(Some(f)).expect("Closure should not be called twice")
- }
- StackNodeStorage::Stack(mut stack) => stack.call(f),
+ pub fn call(mut self, f: &'a ScopeState) -> VNode<'a> {
+ if self.inner.is_heap() {
+ panic!();
}
+ (self.inner)(f)
}
}
-
-struct LazyStack {
- _align: [u64; 0],
- buf: StackHeapSize,
- dropped: bool,
-}
-
-impl LazyStack {
- fn call<'a>(&mut self, f: &'a ScopeState) -> VNode<'a> {
- let LazyStack { buf, .. } = self;
- let data = buf.as_ref();
-
- let info_size =
- mem::size_of::<*mut dyn FnMut(Option<&'a ScopeState>) -> Option>>()
- / mem::size_of::()
- - 1;
-
- let info_ofs = data.len() - info_size;
-
- let g: *mut dyn FnMut(Option<&'a ScopeState>) -> Option> =
- unsafe { make_fat_ptr(data[..].as_ptr() as usize, &data[info_ofs..]) };
-
- self.dropped = true;
-
- let clos = unsafe { &mut *g };
- clos(Some(f)).unwrap()
- }
-}
-impl Drop for LazyStack {
- fn drop(&mut self) {
- if !self.dropped {
- let LazyStack { buf, .. } = self;
- let data = buf.as_ref();
-
- let info_size =
- mem::size_of::<*mut dyn FnMut(Option<&ScopeState>) -> Option>>()
- / mem::size_of::()
- - 1;
-
- let info_ofs = data.len() - info_size;
-
- let g: *mut dyn FnMut(Option<&ScopeState>) -> Option> =
- unsafe { make_fat_ptr(data[..].as_ptr() as usize, &data[info_ofs..]) };
-
- self.dropped = true;
-
- let clos = unsafe { &mut *g };
- clos(None);
- }
- }
-}
-
-/// Obtain mutable access to a pointer's words
-fn ptr_as_slice(ptr: &mut T) -> &mut [usize] {
- assert!(mem::size_of::() % mem::size_of::() == 0);
- let words = mem::size_of::() / mem::size_of::();
- // SAFE: Points to valid memory (a raw pointer)
- unsafe { core::slice::from_raw_parts_mut(ptr as *mut _ as *mut usize, words) }
-}
-
-/// Re-construct a fat pointer
-unsafe fn make_fat_ptr(data_ptr: usize, meta_vals: &[usize]) -> *mut T {
- let mut rv = mem::MaybeUninit::<*mut T>::uninit();
- {
- let s = ptr_as_slice(&mut rv);
- s[0] = data_ptr;
- s[1..].copy_from_slice(meta_vals);
- }
- let rv = rv.assume_init();
- assert_eq!(rv as *const (), data_ptr as *const ());
- rv
-}
-
-fn round_to_words(len: usize) -> usize {
- (len + mem::size_of::() - 1) / mem::size_of::()
-}
diff --git a/packages/core/src/mutations.rs b/packages/core/src/mutations.rs
index 3687cd956..2343c4548 100644
--- a/packages/core/src/mutations.rs
+++ b/packages/core/src/mutations.rs
@@ -1,6 +1,6 @@
use fxhash::FxHashSet;
-use crate::{arena::ElementId, ScopeId, Template};
+use crate::{arena::ElementId, ScopeId, ScopeState, Template};
/// A container for all the relevant steps to modify the Real DOM
///
@@ -257,3 +257,9 @@ pub enum Mutation<'a> {
id: ElementId,
},
}
+
+#[test]
+fn sizes() {
+ println!("mutations: {}", std::mem::size_of::());
+ println!("scope: {}", std::mem::size_of::());
+}
diff --git a/packages/core/src/scheduler/wait.rs b/packages/core/src/scheduler/wait.rs
index e13b0fd45..f8fdfb595 100644
--- a/packages/core/src/scheduler/wait.rs
+++ b/packages/core/src/scheduler/wait.rs
@@ -40,8 +40,6 @@ impl VirtualDom {
}
pub(crate) fn handle_suspense_wakeup(&mut self, id: SuspenseId) {
- println!("suspense notified");
-
let leaf = self
.scheduler
.leaves
@@ -67,13 +65,6 @@ impl VirtualDom {
// safety: we're not going to modify the suspense context but we don't want to make a clone of it
let fiber = self.acquire_suspense_boundary(leaf.scope_id);
- println!("ready pool");
-
- println!(
- "Existing mutations {:?}, scope {:?}",
- fiber.mutations, fiber.id
- );
-
let scope = &mut self.scopes[scope_id.0];
let arena = scope.current_frame();
@@ -106,16 +97,9 @@ impl VirtualDom {
std::mem::swap(&mut self.mutations, mutations);
if fiber.waiting_on.borrow().is_empty() {
- println!("fiber is finished!");
self.finished_fibers.push(fiber.id);
- } else {
- println!("fiber is not finished {:?}", fiber.waiting_on);
}
- } else {
- println!("nodes arent right");
}
- } else {
- println!("not ready");
}
}
}
diff --git a/packages/core/src/scope_arena.rs b/packages/core/src/scope_arena.rs
index e94818fee..b4cc54c19 100644
--- a/packages/core/src/scope_arena.rs
+++ b/packages/core/src/scope_arena.rs
@@ -30,8 +30,8 @@ impl VirtualDom {
height,
props: Some(props),
placeholder: Default::default(),
- node_arena_1: BumpFrame::new(50),
- node_arena_2: BumpFrame::new(50),
+ node_arena_1: BumpFrame::new(0),
+ node_arena_2: BumpFrame::new(0),
spawned_tasks: Default::default(),
render_cnt: Default::default(),
hook_arena: Default::default(),
diff --git a/packages/core/src/scopes.rs b/packages/core/src/scopes.rs
index bb2776861..54402f159 100644
--- a/packages/core/src/scopes.rs
+++ b/packages/core/src/scopes.rs
@@ -10,6 +10,7 @@ use crate::{
Attribute, AttributeValue, Element, Event, Properties, TaskId,
};
use bumpalo::{boxed::Box as BumpBox, Bump};
+use fxhash::{FxHashMap, FxHashSet};
use std::{
any::{Any, TypeId},
cell::{Cell, RefCell},
@@ -81,10 +82,10 @@ pub struct ScopeState {
pub(crate) hook_list: RefCell>,
pub(crate) hook_idx: Cell,
- pub(crate) shared_contexts: RefCell>>,
+ pub(crate) shared_contexts: RefCell>>,
pub(crate) tasks: Rc,
- pub(crate) spawned_tasks: HashSet,
+ pub(crate) spawned_tasks: FxHashSet,
pub(crate) props: Option>>,
pub(crate) placeholder: Cell