wip: compiling again with runtime safety

This commit is contained in:
Jonathan Kelley 2021-11-06 20:59:46 -04:00
parent 95bd17e38f
commit 857c92f7f0
8 changed files with 461 additions and 290 deletions

View file

@ -40,8 +40,6 @@ indexmap = "1.7.0"
# Serialize the Edits for use in Webview/Liveview instances
serde = { version = "1", features = ["derive"], optional = true }
serde_repr = { version = "0.1.7", optional = true }
[dev-dependencies]
anyhow = "1.0.42"
dioxus-html = { path = "../html" }
@ -54,7 +52,7 @@ dioxus-core-macro = { path = "../core-macro", version = "0.1.2" }
[features]
default = []
serialize = ["serde", "serde_repr"]
serialize = ["serde"]
debug_vdom = []
[[bench]]

File diff suppressed because it is too large Load diff

View file

@ -34,7 +34,7 @@ impl HookList {
///
/// This should only be ran by Dioxus itself before "running scope".
/// Dioxus knows how to descend through the tree to prevent mutable aliasing.
pub(crate) unsafe fn reset(&mut self) {
pub(crate) unsafe fn reset(&self) {
self.idx.set(0);
}

View file

@ -319,7 +319,8 @@ pub struct Listener<'bump> {
pub struct VComponent<'src> {
pub key: Option<&'src str>,
pub associated_scope: Cell<Option<*mut ScopeInner>>,
pub associated_scope: Cell<Option<ScopeId>>,
// pub associated_scope: Cell<Option<*mut ScopeInner>>,
// Function pointer to the FC that was used to generate this component
pub user_fc: *const (),

View file

@ -43,14 +43,18 @@ pub type Context<'a> = &'a ScopeInner;
/// use case they might have.
pub struct ScopeInner {
// Book-keeping about our spot in the arena
// yes, a raw pointer
// it's bump allocated so it's stable
// the safety of parent pointers is guaranteed by the logic in this crate
// safety:
//
// pointers to scopes are *always* valid since they are bump allocated and never freed until this scope is also freed
pub(crate) parent_scope: Option<*mut ScopeInner>,
pub(crate) our_arena_idx: ScopeId,
pub(crate) height: u32,
pub(crate) subtree: Cell<u32>,
pub(crate) is_subtree_root: Cell<bool>,
// Nodes
@ -100,12 +104,12 @@ impl ScopeInner {
///
/// 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(&mut self) {
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
.get_mut()
.borrow_mut()
.borrowed_props
.drain(..)
.map(|li| unsafe { &*li })
@ -116,9 +120,10 @@ impl ScopeInner {
.get()
.expect("VComponents should be associated with a valid Scope");
let scope = unsafe { &mut *scope_id };
todo!("move this onto virtualdom");
// let scope = unsafe { &mut *scope_id };
scope.ensure_drop_safety();
// scope.ensure_drop_safety();
todo!("drop the component's props");
// let mut drop_props = comp.drop_props.borrow_mut().take().unwrap();
@ -127,7 +132,7 @@ impl ScopeInner {
// Now that all the references are gone, we can safely drop our own references in our listeners.
self.items
.get_mut()
.borrow_mut()
.listeners
.drain(..)
.map(|li| unsafe { &*li })
@ -135,8 +140,8 @@ impl ScopeInner {
}
/// A safe wrapper around calling listeners
pub(crate) fn call_listener(&mut self, event: UserEvent, element: ElementId) {
let listners = &mut self.items.get_mut().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 };
@ -191,9 +196,10 @@ impl ScopeInner {
// }
}
pub(crate) fn update_vcomp(&mut self, vcomp: &VComponent) {
pub(crate) fn update_vcomp(&self, vcomp: &VComponent) {
let f: *const _ = vcomp;
self.vcomp = unsafe { std::mem::transmute(f) };
todo!()
// self.vcomp = unsafe { std::mem::transmute(f) };
}
pub(crate) fn load_vcomp<'a>(&'a mut self) -> &'a VComponent<'a> {

View file

@ -32,8 +32,8 @@ impl ScopeArena {
}
}
pub fn get_mut(&mut self, id: &ScopeId) -> Option<&mut ScopeInner> {
unsafe { Some(&mut *self.scopes[id.0]) }
pub fn get_mut(&self, id: &ScopeId) -> Option<&ScopeInner> {
unsafe { Some(&*self.scopes[id.0]) }
}
pub fn new_with_key(

View file

@ -33,7 +33,9 @@ impl TestDom {
pub fn diff<'a>(&'a self, old: &'a VNode<'a>, new: &'a VNode<'a>) -> Mutations<'a> {
let mutations = Mutations::new();
let mut machine = DiffMachine::new(mutations);
let mut machine: DiffState = todo!();
// let mut machine = DiffState::new(mutations);
// let mut machine = DiffState::new(mutations);
machine.stack.push(DiffInstruction::Diff { new, old });
machine.mutations
}
@ -41,13 +43,17 @@ impl TestDom {
pub fn create<'a>(&'a self, left: Option<LazyNodes<'a, '_>>) -> Mutations<'a> {
let old = self.bump.alloc(self.render_direct(left));
let mut machine = DiffMachine::new(Mutations::new());
let mut machine: DiffState = todo!();
// let mut machine = DiffState::new(Mutations::new());
// let mut machine = DiffState::new(Mutations::new());
machine.stack.create_node(old, MountType::Append);
machine.work(&mut || false);
todo!()
machine.mutations
// machine.work(&mut || false);
// machine.mutations
}
pub fn lazy_diff<'a>(
@ -57,22 +63,26 @@ impl TestDom {
) -> (Mutations<'a>, Mutations<'a>) {
let (old, new) = (self.render(left), self.render(right));
let mut machine = DiffMachine::new(Mutations::new());
let mut machine: DiffState = todo!();
// let mut machine = DiffState::new(Mutations::new());
machine.stack.create_node(old, MountType::Append);
machine.work(|| false);
let create_edits = machine.mutations;
todo!()
let mut machine = DiffMachine::new(Mutations::new());
// machine.work(|| false);
// let create_edits = machine.mutations;
machine.stack.push(DiffInstruction::Diff { old, new });
// let mut machine: DiffState = todo!();
// // let mut machine = DiffState::new(Mutations::new());
machine.work(&mut || false);
// machine.stack.push(DiffInstruction::Diff { old, new });
let edits = machine.mutations;
// machine.work(&mut || false);
(create_edits, edits)
// let edits = machine.mutations;
// (create_edits, edits)
}
}

View file

@ -78,7 +78,7 @@ pub struct VirtualDom {
// we need to keep the allocation around, but we don't necessarily use it
_root_caller: Box<dyn Any>,
pub scopes: ScopeArena,
pub(crate) scopes: ScopeArena,
pub receiver: UnboundedReceiver<SchedulerMsg>,
pub sender: UnboundedSender<SchedulerMsg>,
@ -92,7 +92,7 @@ pub struct VirtualDom {
pub dirty_scopes: IndexSet<ScopeId>,
pub saved_state: Option<SavedDiffWork<'static>>,
pub(crate) saved_state: Option<SavedDiffWork<'static>>,
pub in_progress: bool,
}
@ -250,7 +250,8 @@ impl VirtualDom {
where
P: 'static,
{
let root_scope = self.pool.get_scope_mut(&self.base_scope).unwrap();
let base = self.base_scope;
let root_scope = self.get_scope_mut(&base).unwrap();
// Pre-emptively drop any downstream references of the old props
root_scope.ensure_drop_safety();
@ -532,7 +533,7 @@ For the rest, we defer to the rIC period and work down each queue from high to l
impl VirtualDom {
// returns true if the event is discrete
pub fn handle_ui_event(&mut self, event: UserEvent) -> bool {
let (discrete, priority) = event_meta(&event);
// let (discrete, priority) = event_meta(&event);
if let Some(scope) = self.get_scope_mut(&event.scope) {
if let Some(element) = event.mounted_dom_id {
@ -555,7 +556,8 @@ impl VirtualDom {
// Low => todo!(),
// }
discrete
todo!()
// discrete
}
fn prepare_work(&mut self) {
@ -588,7 +590,7 @@ impl VirtualDom {
SchedulerMsg::UiEvent(event) => {
//
let (discrete, priority) = event_meta(&event);
// let (discrete, priority) = event_meta(&event);
if let Some(scope) = self.get_scope_mut(&event.scope) {
if let Some(element) = event.mounted_dom_id {
@ -602,7 +604,7 @@ impl VirtualDom {
}
}
discrete;
// discrete;
}
}
}
@ -625,12 +627,13 @@ impl VirtualDom {
let mut ran_scopes = FxHashSet::default();
if machine.stack.is_empty() {
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()
});
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);
@ -640,21 +643,23 @@ impl VirtualDom {
// if let Some(component) = self.get_scope_mut(&scopeid) {
if self.run_scope(&scopeid) {
let (old, new) = (component.frames.wip_head(), component.frames.fin_head());
todo!("diff the scope")
// 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 (old, new) = (component.frames.wip_head(), component.frames.fin_head());
// machine.stack.scope_stack.push(scopeid);
// machine.stack.push(DiffInstruction::Diff { new, old });
}
// }
}
}
}
let work_completed = machine.work(deadline_reached);
let work_completed: bool = todo!();
// let work_completed = machine.work(deadline_reached);
// log::debug!("raw edits {:?}", machine.mutations.edits);
let mut machine: DiffMachine<'static> = unsafe { std::mem::transmute(machine) };
let mut machine: DiffState<'static> = unsafe { std::mem::transmute(machine) };
// let mut saved = machine.save();
if work_completed {
@ -681,8 +686,9 @@ impl VirtualDom {
// log::debug!("saved edits {:?}", mutations);
let mut saved = machine.save();
self.save_work(saved);
todo!();
// let mut saved = machine.save();
// self.save_work(saved);
true
// self.save_work(saved);
@ -801,24 +807,26 @@ impl VirtualDom {
///
/// Typically used to kickstart the VirtualDOM after initialization.
pub fn rebuild_inner(&mut self, base_scope: ScopeId) -> Mutations {
let mut diff_machine = DiffMachine::new(Mutations::new());
// TODO: drain any in-flight work
let cur_component = self
.get_scope_mut(&base_scope)
.expect("The base scope should never be moved");
log::debug!("rebuild {:?}", base_scope);
// 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");
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);
diff_machine.work(|| false);
todo!()
// self.work(&mut diff_machine, || false);
// diff_machine.work(|| false);
} else {
// todo: should this be a hard error?
log::warn!(
@ -827,7 +835,8 @@ impl VirtualDom {
);
}
unsafe { std::mem::transmute(diff_machine.mutations) }
todo!()
// unsafe { std::mem::transmute(diff_machine.mutations) }
}
pub fn hard_diff(&mut self, base_scope: ScopeId) -> Mutations {
@ -838,16 +847,17 @@ impl VirtualDom {
log::debug!("hard diff {:?}", base_scope);
if self.run_scope(&base_scope) {
let mut diff_machine = DiffMachine::new(Mutations::new());
let mut diff_machine = DiffState::new(Mutations::new());
diff_machine.cfg.force_diff = true;
diff_machine.diff_scope(base_scope);
self.diff_scope(&mut diff_machine, base_scope);
// diff_machine.diff_scope(base_scope);
diff_machine.mutations
} else {
Mutations::new()
}
}
pub fn get_scope_mut(&mut self, id: &ScopeId) -> Option<&mut ScopeInner> {
pub fn get_scope_mut<'a>(&'a self, id: &ScopeId) -> Option<&'a ScopeInner> {
self.scopes.get_mut(id)
}
@ -868,7 +878,8 @@ impl VirtualDom {
// Safety:
// - We've dropped all references to the wip bump frame
unsafe { scope.frames.reset_wip_frame() };
todo!("reset wip frame");
// unsafe { scope.frames.reset_wip_frame() };
let items = scope.items.get_mut();
@ -903,6 +914,19 @@ impl VirtualDom {
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<ScopeInner> {
todo!()
}
}
// impl<'a> Future for PollAllTasks<'a> {