wip: start to refactor out hot reloading from packages

Also implement alternative simpler template system
This commit is contained in:
Jonathan Kelley committed 2022-10-20 09:56:09 -07:00
1 parent 3b7b503333
commit 90982e0ccb
44 files changed
+3865 -5133

No files matched your search

+90 -185
View File
@@ -1,10 +1,11 @@
use crate::{innerlude::*, template::TemplateNodeId, unsafe_utils::extend_vnode};
use crate::{innerlude::*, unsafe_utils::extend_vnode};
use bumpalo::Bump;
use futures_channel::mpsc::UnboundedSender;
use rustc_hash::FxHashMap;
use fxhash::FxHashMap;
use slab::Slab;
use std::{
any::{Any, TypeId},
borrow::Borrow,
cell::{Cell, RefCell},
collections::{HashMap, HashSet},
future::Future,
@@ -13,17 +14,6 @@ use std::{
sync::Arc,
};
pub(crate) enum NodePtr {
VNode(*const VNode<'static>),
TemplateNode {
template_ref: TemplateRefId,
node_id: TemplateNodeId,
},
Phantom,
}
pub(crate) type NodeSlab = Slab<NodePtr>;
/// for traceability, we use the raw fn pointer to identify the function
/// we also get the component name, but that's not necessarily unique in the app
pub(crate) type ComponentPtr = *mut std::os::raw::c_void;
@@ -43,16 +33,9 @@ pub(crate) struct ScopeArena {
pub scopes: RefCell<FxHashMap<ScopeId, *mut ScopeState>>,
pub heuristics: RefCell<FxHashMap<ComponentPtr, Heuristic>>,
pub free_scopes: RefCell<Vec<*mut ScopeState>>,
// All nodes are stored here. This mimics the allocations needed on the renderer side.
// Some allocations are needed on the renderer to render nodes in templates that are
// not needed in dioxus, for these allocations None is used so that the id is preseved and passive memory managment is preserved.
pub nodes: RefCell<NodeSlab>,
pub nodes: RefCell<Slab<*const VNode<'static>>>,
pub tasks: Rc<TaskQueue>,
pub template_resolver: RefCell<TemplateResolver>,
pub templates: Rc<RefCell<FxHashMap<TemplateId, Rc<RefCell<Template>>>>>,
// this is used to store intermidiate artifacts of creating templates, so that the lifetime aligns with Mutations<'bump>.
pub template_bump: Bump,
pub template_refs: RefCell<Slab<*const VTemplateRef<'static>>>,
pub template_cache: RefCell<HashSet<Template<'static>>>,
}
impl ScopeArena {
@@ -75,9 +58,7 @@ impl ScopeArena {
let node = bump.alloc(VNode::Element(el));
let mut nodes = Slab::new();
let root_id = nodes.insert(NodePtr::VNode(unsafe {
std::mem::transmute(node as *const _)
}));
let root_id = nodes.insert(unsafe { std::mem::transmute(node as *const _) });
debug_assert_eq!(root_id, 0);
@@ -94,10 +75,7 @@ impl ScopeArena {
gen: Cell::new(0),
sender,
}),
template_resolver: RefCell::new(TemplateResolver::default()),
templates: Rc::new(RefCell::new(FxHashMap::default())),
template_bump: Bump::new(),
template_refs: RefCell::new(Slab::new()),
template_cache: RefCell::new(HashSet::new()),
}
}
@@ -118,6 +96,7 @@ impl ScopeArena {
vcomp: Box<dyn AnyProps>,
parent_scope: Option<ScopeId>,
container: ElementId,
subtree: u32,
) -> ScopeId {
// Increment the ScopeId system. ScopeIDs are never reused
let new_scope_id = ScopeId(self.scope_gen.get());
@@ -147,6 +126,7 @@ impl ScopeArena {
scope.height = height;
scope.fnptr = fc_ptr;
scope.props.get_mut().replace(vcomp);
scope.subtree.set(subtree);
scope.frames[0].reset();
scope.frames[1].reset();
scope.shared_contexts.get_mut().clear();
@@ -177,6 +157,10 @@ impl ScopeArena {
props: RefCell::new(Some(vcomp)),
frames: [BumpFrame::new(node_capacity), BumpFrame::new(node_capacity)],
// todo: subtrees
subtree: Cell::new(0),
is_subtree_root: Cell::default(),
generation: 0.into(),
tasks: self.tasks.clone(),
@@ -190,8 +174,6 @@ impl ScopeArena {
hook_arena: Bump::new(),
hook_vals: RefCell::new(Vec::with_capacity(hook_capacity)),
hook_idx: Cell::default(),
templates: self.templates.clone(),
}),
);
}
@@ -227,61 +209,13 @@ impl ScopeArena {
let key = entry.key();
let id = ElementId(key);
let node = unsafe { extend_vnode(node) };
entry.insert(NodePtr::VNode(node as *const _));
id
}
pub fn reserve_template_ref<'a>(&self, template_ref: &'a VTemplateRef<'a>) -> TemplateRefId {
let mut refs = self.template_refs.borrow_mut();
let entry = refs.vacant_entry();
let key = entry.key();
let id = TemplateRefId(key);
let static_ref: &VTemplateRef<'static> = unsafe { std::mem::transmute(template_ref) };
entry.insert(static_ref as *const _);
id
}
pub fn reserve_template_node(
&self,
template_ref_id: TemplateRefId,
node_id: TemplateNodeId,
) -> ElementId {
let mut els = self.nodes.borrow_mut();
let entry = els.vacant_entry();
let key = entry.key();
let id = ElementId(key);
entry.insert(NodePtr::TemplateNode {
template_ref: template_ref_id,
node_id,
});
id
}
pub fn reserve_phantom_node(&self) -> ElementId {
let mut els = self.nodes.borrow_mut();
let entry = els.vacant_entry();
let key = entry.key();
let id = ElementId(key);
entry.insert(NodePtr::Phantom);
entry.insert(node as *const _);
id
}
pub fn update_node<'a>(&self, node: &'a VNode<'a>, id: ElementId) {
let node = unsafe { extend_vnode(node) };
*self.nodes.borrow_mut().get_mut(id.0).unwrap() = NodePtr::VNode(node);
}
pub fn update_template_ref<'a>(
&self,
template_ref_id: TemplateRefId,
template_ref: &'a VTemplateRef<'a>,
) {
let template_ref = unsafe { std::mem::transmute(template_ref) };
*self
.template_refs
.borrow_mut()
.get_mut(template_ref_id.0)
.unwrap() = template_ref;
*self.nodes.borrow_mut().get_mut(id.0).unwrap() = node;
}
pub fn collect_garbage(&self, id: ElementId) {
@@ -369,11 +303,11 @@ impl ScopeArena {
frame.node.set(unsafe { extend_vnode(node) });
} else {
let frame = scope.wip_frame();
let node = frame
.bump
.alloc(VNode::Placeholder(frame.bump.alloc(VPlaceholder {
id: Cell::default(),
})));
let node = frame.bump.alloc(VNode::Text(frame.bump.alloc(VText {
id: Cell::default(),
text: "asd",
is_static: false,
})));
frame.node.set(unsafe { extend_vnode(node) });
}
@@ -389,92 +323,16 @@ impl ScopeArena {
let state = Rc::new(BubbleState::new());
while let Some(id) = cur_el.take() {
if state.canceled.get() {
// stop bubbling if canceled
return;
}
if let Some(ptr) = nodes.get(id.0) {
match ptr {
NodePtr::VNode(ptr) => {
let real_el = unsafe { &**ptr };
log::trace!("looking for listener on {:?}", real_el);
if let Some(el) = nodes.get(id.0) {
let real_el = unsafe { &**el };
if let VNode::Element(real_el) = real_el {
for listener in real_el.listeners.iter() {
if listener.event == event.name {
log::trace!("calling listener {:?}", listener.event);
let mut cb = listener.callback.borrow_mut();
if let Some(cb) = cb.as_mut() {
// todo: arcs are pretty heavy to clone
// we really want to convert arc to rc
// unfortunately, the SchedulerMsg must be send/sync to be sent across threads
// we could convert arc to rc internally or something
(cb)(AnyEvent {
bubble_state: state.clone(),
data: event.data.clone(),
});
}
break;
}
}
cur_el = real_el.parent.get();
}
}
NodePtr::TemplateNode {
node_id,
template_ref,
} => {
let template_refs = self.template_refs.borrow();
let template_ptr = template_refs.get(template_ref.0).unwrap();
let template_ref = unsafe { &**template_ptr };
log::trace!("looking for listener in node {:?}", node_id);
let templates = self.templates.borrow();
let template = templates.get(&template_ref.template_id).unwrap();
cur_el = template.borrow().with_nodes(
bubble_template,
bubble_template,
(*node_id, template_ref, event, &state),
);
}
_ => panic!("Expected Real Node"),
}
}
if !event.bubbles {
return;
}
}
fn bubble_template<'b, Attributes, V, Children, Listeners, TextSegments, Text, Nodes>(
nodes: &Nodes,
ctx: (
TemplateNodeId,
&VTemplateRef<'b>,
&UserEvent,
&Rc<BubbleState>,
),
) -> Option<ElementId>
where
Nodes: AsRef<[TemplateNode<Attributes, V, Children, Listeners, TextSegments, Text>]>,
Attributes: AsRef<[TemplateAttribute<V>]>,
V: TemplateValue,
Children: AsRef<[TemplateNodeId]>,
Listeners: AsRef<[usize]>,
TextSegments: AsRef<[TextTemplateSegment<Text>]>,
Text: AsRef<str>,
{
let (start, template_ref, event, state) = ctx;
let dynamic_context = &template_ref.dynamic_context;
let mut current = &nodes.as_ref()[start.0];
loop {
if let TemplateNodeType::Element(el) = &current.node_type {
let TemplateElement { listeners, .. } = el;
for listener_idx in listeners.as_ref() {
let listener = dynamic_context.resolve_listener(*listener_idx);
if let VNode::Element(real_el) = real_el {
for listener in real_el.listeners.borrow().iter() {
if listener.event == event.name {
log::trace!("calling listener {:?}", listener.event);
if state.canceled.get() {
// stop bubbling if canceled
return;
}
let mut cb = listener.callback.borrow_mut();
if let Some(cb) = cb.as_mut() {
@@ -487,17 +345,14 @@ impl ScopeArena {
data: event.data.clone(),
});
}
break;
if !event.bubbles {
return;
}
}
}
if let Some(id) = current.parent {
current = &nodes.as_ref()[id.0];
} else {
return template_ref.parent.get();
}
} else {
return None;
cur_el = real_el.parent.get();
}
}
}
@@ -525,10 +380,11 @@ impl ScopeArena {
// this is totally okay since all our nodes are always in a valid state
pub fn get_element(&self, id: ElementId) -> Option<&VNode> {
self.nodes.borrow().get(id.0).and_then(|ptr| match ptr {
NodePtr::VNode(ptr) => Some(unsafe { extend_vnode(&**ptr) }),
_ => None,
})
self.nodes
.borrow()
.get(id.0)
.copied()
.map(|ptr| unsafe { extend_vnode(&*ptr) })
}
}
@@ -613,6 +469,10 @@ pub struct ScopeState {
pub(crate) our_arena_idx: ScopeId,
pub(crate) height: u32,
pub(crate) fnptr: ComponentPtr,
// todo: subtrees
pub(crate) is_subtree_root: Cell<bool>,
pub(crate) subtree: Cell<u32>,
pub(crate) props: RefCell<Option<Box<dyn AnyProps>>>,
// nodes, items
@@ -628,9 +488,6 @@ pub struct ScopeState {
// shared state -> todo: move this out of scopestate
pub(crate) shared_contexts: RefCell<HashMap<TypeId, Box<dyn Any>>>,
pub(crate) tasks: Rc<TaskQueue>,
// templates
pub(crate) templates: Rc<RefCell<FxHashMap<TemplateId, Rc<RefCell<Template>>>>>,
}
pub struct SelfReferentialItems<'a> {
@@ -640,6 +497,52 @@ pub struct SelfReferentialItems<'a> {
// Public methods exposed to libraries and components
impl ScopeState {
/// Get the subtree ID that this scope belongs to.
///
/// Each component has its own subtree ID - the root subtree has an ID of 0. This ID is used by the renderer to route
/// the mutations to the correct window/portal/subtree.
///
///
/// # Example
///
/// ```rust, ignore
/// let mut dom = VirtualDom::new(|cx| cx.render(rsx!{ div {} }));
/// dom.rebuild();
///
/// let base = dom.base_scope();
///
/// assert_eq!(base.subtree(), 0);
/// ```
///
/// todo: enable
pub(crate) fn _subtree(&self) -> u32 {
self.subtree.get()
}
/// Create a new subtree with this scope as the root of the subtree.
///
/// Each component has its own subtree ID - the root subtree has an ID of 0. This ID is used by the renderer to route
/// the mutations to the correct window/portal/subtree.
///
/// This method
///
/// # Example
///
/// ```rust, ignore
/// fn App(cx: Scope) -> Element {
/// render!(div { "Subtree {id}"})
/// };
/// ```
///
/// todo: enable subtree
pub(crate) fn _create_subtree(&self) -> Option<u32> {
if self.is_subtree_root.get() {
None
} else {
todo!()
}
}
/// Get the height of this Scope - IE the number of scopes above it.
///
/// A Scope with a height of `0` is the root scope - there are no other scopes above it.
@@ -1002,6 +905,8 @@ impl ScopeState {
self.hook_idx.set(0);
self.parent_scope = None;
self.generation.set(0);
self.is_subtree_root.set(false);
self.subtree.set(0);
// next: shared context data
self.shared_contexts.get_mut().clear();