wip: stop transmuting reference

This commit is contained in:
Jonathan Kelley committed 2022-11-21 17:00:34 -08:00
1 parent d407035931
commit 491bf4332d
8 files changed
+263 -244

No files matched your search

+1 -6
View File
@@ -2,12 +2,7 @@ use dioxus::prelude::*;
use std::collections::HashMap; use std::collections::HashMap;
fn main() { fn main() {
dioxus_desktop::launch(|cx| { dioxus_desktop::launch(|cx| render!(app_root {}));
cx.render(rsx! {
h1 {"Loading...."}
app_root {}
})
});
} }
#[derive(Debug, Clone, PartialEq, serde::Deserialize)] #[derive(Debug, Clone, PartialEq, serde::Deserialize)]
+1 -1
View File
@@ -1,6 +1,6 @@
use std::cell::Cell;
use crate::factory::RenderReturn; use crate::factory::RenderReturn;
use bumpalo::Bump; use bumpalo::Bump;
use std::cell::Cell;
pub struct BumpFrame { pub struct BumpFrame {
pub bump: Bump, pub bump: Bump,
+203 -117
View File
@@ -1,22 +1,36 @@
use std::cell::Cell;
use crate::factory::RenderReturn; use crate::factory::RenderReturn;
use crate::innerlude::{Mutations, SuspenseContext, VText}; use crate::innerlude::{Mutations, VComponent, VFragment, VText};
use crate::mutations::Mutation; use crate::mutations::Mutation;
use crate::mutations::Mutation::*; use crate::mutations::Mutation::*;
use crate::nodes::VNode; use crate::nodes::VNode;
use crate::nodes::{DynamicNode, TemplateNode}; use crate::nodes::{DynamicNode, TemplateNode};
use crate::virtual_dom::VirtualDom; use crate::virtual_dom::VirtualDom;
use crate::{AttributeValue, ScopeId, TemplateAttribute}; use crate::{AttributeValue, ElementId, ScopeId, SuspenseContext, TemplateAttribute};
impl VirtualDom { impl VirtualDom {
/// Create a new template [`VNode`] and write it to the [`Mutations`] buffer.
///
/// This method pushes the ScopeID to the internal scopestack and returns the number of nodes created.
pub(crate) fn create_scope<'a>( pub(crate) fn create_scope<'a>(
&mut self, &mut self,
scope: ScopeId, scope: ScopeId,
mutations: &mut Mutations<'a>, mutations: &mut Mutations<'a>,
template: &'a VNode<'a>, template: &'a VNode<'a>,
) -> usize { ) -> usize {
let mutations_to_this_point = mutations.len();
self.scope_stack.push(scope); self.scope_stack.push(scope);
let out = self.create(mutations, template); let out = self.create(mutations, template);
self.scope_stack.pop(); self.scope_stack.pop();
if !self.collected_leaves.is_empty() {
if let Some(boundary) = self.scopes[scope.0].has_context::<SuspenseContext>() {
println!("Boundary detected and pending leaves!");
}
}
out out
} }
@@ -29,18 +43,7 @@ impl VirtualDom {
// The best renderers will have templates prehydrated and registered // The best renderers will have templates prehydrated and registered
// Just in case, let's create the template using instructions anyways // Just in case, let's create the template using instructions anyways
if !self.templates.contains_key(&template.template.id) { if !self.templates.contains_key(&template.template.id) {
for node in template.template.roots { self.register_template(template, mutations);
let mutations = &mut mutations.template_mutations;
self.create_static_node(mutations, template, node);
}
mutations.template_mutations.push(SaveTemplate {
name: template.template.id,
m: template.template.roots.len(),
});
self.templates
.insert(template.template.id, template.template.clone());
} }
// Walk the roots, creating nodes and assigning IDs // Walk the roots, creating nodes and assigning IDs
@@ -167,6 +170,21 @@ impl VirtualDom {
on_stack on_stack
} }
/// Insert a new template into the VirtualDom's template registry
fn register_template<'a>(&mut self, template: &'a VNode<'a>, mutations: &mut Mutations<'a>) {
for node in template.template.roots {
self.create_static_node(&mut mutations.template_mutations, template, node);
}
mutations.template_mutations.push(SaveTemplate {
name: template.template.id,
m: template.template.roots.len(),
});
self.templates
.insert(template.template.id, template.template);
}
pub(crate) fn create_static_node<'a>( pub(crate) fn create_static_node<'a>(
&mut self, &mut self,
mutations: &mut Vec<Mutation<'a>>, mutations: &mut Vec<Mutation<'a>>,
@@ -234,125 +252,193 @@ impl VirtualDom {
node: &'a DynamicNode<'a>, node: &'a DynamicNode<'a>,
idx: usize, idx: usize,
) -> usize { ) -> usize {
match &node { use DynamicNode::*;
DynamicNode::Text(VText { id, value }) => { match node {
let new_id = self.next_element(template, template.template.node_paths[idx]); Text(text) => self.create_dynamic_text(mutations, template, text, idx),
id.set(new_id); Placeholder(slot) => self.create_placeholder(template, idx, slot, mutations),
mutations.push(HydrateText { Fragment(frag) => self.create_fragment(frag, mutations),
id: new_id, Component(component) => self.create_component_node(mutations, template, component, idx),
path: &template.template.node_paths[idx][1..], }
value, }
});
0
}
DynamicNode::Component(component) => { fn create_dynamic_text<'a>(
let props = component.props.replace(None).unwrap(); &mut self,
let prop_ptr = unsafe { std::mem::transmute(props.as_ref()) }; mutations: &mut Mutations<'a>,
let scope = self.new_scope(prop_ptr).id; template: &VNode<'a>,
component.props.replace(Some(props)); text: &VText<'a>,
idx: usize,
) -> usize {
// Allocate a dynamic element reference for this text node
let new_id = self.next_element(template, template.template.node_paths[idx]);
component.scope.set(Some(scope)); // Make sure the text node is assigned to the correct element
text.id.set(new_id);
let return_nodes = unsafe { self.run_scope(scope).extend_lifetime_ref() }; // Add the mutation to the list
mutations.push(HydrateText {
id: new_id,
path: &template.template.node_paths[idx][1..],
value: text.value,
});
match return_nodes { // Since we're hydrating an existing node, we don't create any new nodes
RenderReturn::Sync(None) => { 0
todo!() }
}
RenderReturn::Async(_) => { fn create_placeholder(
let new_id = self.next_element(template, template.template.node_paths[idx]); &mut self,
component.placeholder.set(Some(new_id)); template: &VNode,
self.scopes[scope.0].placeholder.set(Some(new_id)); idx: usize,
slot: &Cell<ElementId>,
mutations: &mut Mutations,
) -> usize {
// Allocate a dynamic element reference for this text node
let id = self.next_element(template, template.template.node_paths[idx]);
mutations.push(AssignId { // Make sure the text node is assigned to the correct element
id: new_id, slot.set(id);
path: &template.template.node_paths[idx][1..],
});
let boudary = self.scopes[scope.0] // Assign the ID to the existing node in the template
.consume_context::<SuspenseContext>() mutations.push(AssignId {
.unwrap(); path: &template.template.node_paths[idx][1..],
id,
});
if boudary.placeholder.get().is_none() { // Since the placeholder is already in the DOM, we don't create any new nodes
boudary.placeholder.set(Some(new_id)); 0
} }
boudary
.waiting_on
.borrow_mut()
.extend(self.collected_leaves.drain(..));
0 fn create_fragment<'a>(
} &mut self,
frag: &'a VFragment<'a>,
mutations: &mut Mutations<'a>,
) -> usize {
frag.nodes
.iter()
.fold(0, |acc, child| acc + self.create(mutations, child))
}
RenderReturn::Sync(Some(template)) => { fn create_component_node<'a>(
let mutations_to_this_point = mutations.len(); &mut self,
mutations: &mut Mutations<'a>,
template: &'a VNode<'a>,
component: &'a VComponent<'a>,
idx: usize,
) -> usize {
let props = component.props.replace(None).unwrap();
self.scope_stack.push(scope); let prop_ptr = unsafe { std::mem::transmute(props.as_ref()) };
let mut created = self.create(mutations, template); let scope = self.new_scope(prop_ptr).id;
self.scope_stack.pop();
if !self.collected_leaves.is_empty() { component.props.replace(Some(props));
if let Some(boundary) = component.scope.set(Some(scope));
self.scopes[scope.0].has_context::<SuspenseContext>()
{
// Since this is a boundary, use it as a placeholder
let new_id =
self.next_element(template, template.template.node_paths[idx]);
component.placeholder.set(Some(new_id));
self.scopes[scope.0].placeholder.set(Some(new_id));
mutations.push(AssignId {
id: new_id,
path: &template.template.node_paths[idx][1..],
});
// Now connect everything to the boundary let return_nodes = unsafe { self.run_scope(scope).extend_lifetime_ref() };
let boundary_mut = boundary;
let split_off = mutations.split_off(mutations_to_this_point);
let split_off: Vec<Mutation> =
unsafe { std::mem::transmute(split_off) };
if boundary_mut.placeholder.get().is_none() { use RenderReturn::*;
boundary_mut.placeholder.set(Some(new_id));
}
// In the generated edits, we want to pick off from where we left off. match return_nodes {
boundary_mut.mutations.borrow_mut().edits.extend(split_off); Sync(Some(t)) => self.create_component(mutations, scope, t, idx, component),
Sync(None) | Async(_) => {
boundary_mut self.create_component_placeholder(template, idx, component, scope, mutations)
.waiting_on
.borrow_mut()
.extend(self.collected_leaves.drain(..));
created = 0;
}
}
// handle any waiting on futures accumulated by async calls down the tree
// if this is a boundary, we split off the tree
created
}
}
}
DynamicNode::Fragment(frag) => {
// Todo: if there's no children create a placeholder instead ?
frag.nodes
.iter()
.fold(0, |acc, child| acc + self.create(mutations, child))
}
DynamicNode::Placeholder(slot) => {
let id = self.next_element(template, template.template.node_paths[idx]);
slot.set(id);
mutations.push(AssignId {
path: &template.template.node_paths[idx][1..],
id,
});
0
} }
} }
} }
fn create_component<'a>(
&mut self,
mutations: &mut Mutations<'a>,
scope: ScopeId,
template: &'a VNode<'a>,
idx: usize,
component: &'a VComponent<'a>,
) -> usize {
// // Keep track of how many mutations are in the buffer in case we need to split them out if a suspense boundary
// // is encountered
// let mutations_to_this_point = mutations.len();
// Create the component's root element
let created = self.create_scope(scope, mutations, template);
if !self.collected_leaves.is_empty() {
println!("collected leaves: {:?}", self.collected_leaves);
}
created
// // If running the scope has collected some leaves and *this* component is a boundary, then handle the suspense
// let boundary = match self.scopes[scope.0].has_context::<SuspenseContext>() {
// Some(boundary) if !self.collected_leaves.is_empty() => boundary,
// _ => return created,
// };
// // Since this is a boundary, use it as a placeholder
// let new_id = self.next_element(template, template.template.node_paths[idx]);
// component.placeholder.set(Some(new_id));
// self.scopes[scope.0].placeholder.set(Some(new_id));
// mutations.push(AssignId {
// id: new_id,
// path: &template.template.node_paths[idx][1..],
// });
// // Now connect everything to the boundary
// let boundary_mut = boundary;
// let split_off = mutations.split_off(mutations_to_this_point);
// let split_off: Vec<Mutation> = unsafe { std::mem::transmute(split_off) };
// if boundary_mut.placeholder.get().is_none() {
// boundary_mut.placeholder.set(Some(new_id));
// }
// // In the generated edits, we want to pick off from where we left off.
// boundary_mut.mutations.borrow_mut().edits.extend(split_off);
// boundary_mut
// .waiting_on
// .borrow_mut()
// .extend(self.collected_leaves.drain(..));
// 0
// let boudary = self.scopes[scope.0]
// .consume_context::<SuspenseContext>()
// .unwrap();
// boudary
// .waiting_on
// .borrow_mut()
// .extend(self.collected_leaves.drain(..));
// if boudary.placeholder.get().is_none() {
// boudary.placeholder.set(Some(new_id));
// }
}
/// Take the rendered nodes from a component and handle them if they were async
///
/// IE simply assign an ID to the placeholder
fn create_component_placeholder(
&mut self,
template: &VNode,
idx: usize,
component: &VComponent,
scope: ScopeId,
mutations: &mut Mutations,
) -> usize {
let new_id = self.next_element(template, template.template.node_paths[idx]);
// Set the placeholder of the component
component.placeholder.set(Some(new_id));
// Set the placeholder of the scope
self.scopes[scope.0].placeholder.set(Some(new_id));
// Since the placeholder is already in the DOM, we don't create any new nodes
mutations.push(AssignId {
id: new_id,
path: &template.template.node_paths[idx][1..],
});
0
}
} }
+3 -1
View File
@@ -1,5 +1,4 @@
use std::{ use std::{
any::Any,
cell::{Cell, RefCell}, cell::{Cell, RefCell},
fmt::Arguments, fmt::Arguments,
}; };
@@ -130,7 +129,10 @@ where
} }
pub enum RenderReturn<'a> { pub enum RenderReturn<'a> {
/// A currently-available element
Sync(Element<'a>), Sync(Element<'a>),
/// An ongoing future that will resolve to a [`Element`]
Async(BumpBox<'a, dyn Future<Output = Element<'a>> + 'a>), Async(BumpBox<'a, dyn Future<Output = Element<'a>> + 'a>),
} }
+3
View File
@@ -95,7 +95,10 @@ pub use crate::innerlude::{
TemplateAttribute, TemplateAttribute,
TemplateNode, TemplateNode,
UiEvent, UiEvent,
VComponent,
VFragment,
VNode, VNode,
VText,
VirtualDom, VirtualDom,
}; };
-73
View File
@@ -1,73 +0,0 @@
use std::{cell::RefCell, rc::Rc, sync::Arc};
use futures_task::ArcWake;
use slab::Slab;
use std::future::Future;
use crate::{innerlude::Mutation, ScopeId};
type Shared<T> = Rc<RefCell<T>>;
struct LocalTask {}
pub struct Fiber {
// The work-in progress of this suspended tree
pub mutations: Vec<Mutation<'static>>,
}
#[derive(Clone)]
pub struct SchedulerHandle {
tasks: Shared<Slab<LocalTask>>,
suspended: Shared<Slab<LocalTask>>,
fibers: Shared<Slab<Fiber>>,
tx: futures_channel::mpsc::UnboundedSender<SchedulerMsg>,
}
struct TaskEntry {}
struct LocalTaskWaker<T> {
future: T,
id: TaskId,
tx: futures_channel::mpsc::UnboundedSender<SchedulerMsg>,
}
unsafe impl<T> Send for LocalTaskWaker<T> {}
unsafe impl<T> Sync for LocalTaskWaker<T> {}
impl<T> ArcWake for LocalTaskWaker<T> {
fn wake(self: Arc<Self>) {
Self::wake_by_ref(&self)
}
fn wake_by_ref(arc_self: &Arc<Self>) {
arc_self
.tx
.unbounded_send(SchedulerMsg::TaskNotified(arc_self.id))
.unwrap();
}
}
impl SchedulerHandle {
fn spawn(&self, fut: impl Future<Output = ()> + 'static) -> TaskId {
use futures_task::waker;
let tasks = self.tasks.borrow_mut();
let entry = tasks.vacant_entry();
let id = TaskId(entry.key());
let task = Arc::new(LocalTaskWaker {
future: fut,
id,
tx: self.tx.clone(),
});
let local_task = waker(task.clone());
entry.insert(val);
//
todo!()
}
fn remove(&self, id: TaskId) {
//
}
}
+2 -1
View File
@@ -2,6 +2,7 @@ use crate::{
any_props::AnyProps, any_props::AnyProps,
arena::ElementId, arena::ElementId,
bump_frame::BumpFrame, bump_frame::BumpFrame,
factory::RenderReturn,
innerlude::{Scheduler, SchedulerMsg}, innerlude::{Scheduler, SchedulerMsg},
lazynodes::LazyNodes, lazynodes::LazyNodes,
nodes::VNode, nodes::VNode,
@@ -116,7 +117,7 @@ impl ScopeState {
/// Get a handle to the currently active head node arena for this Scope /// Get a handle to the currently active head node arena for this Scope
/// ///
/// This is useful for traversing the tree outside of the VirtualDom, such as in a custom renderer or in SSR. /// This is useful for traversing the tree outside of the VirtualDom, such as in a custom renderer or in SSR.
pub fn root_node<'a>(&'a self) -> &'a VNode<'a> { pub fn root_node<'a>(&'a self) -> &'a RenderReturn<'a> {
let r = unsafe { &*self.current_frame().node.get() }; let r = unsafe { &*self.current_frame().node.get() };
unsafe { std::mem::transmute(r) } unsafe { std::mem::transmute(r) }
} }
+50 -45
View File
@@ -1,5 +1,5 @@
use super::cache::Segment; use super::cache::Segment;
use dioxus_core::{prelude::*, AttributeValue, DynamicNode}; use dioxus_core::{prelude::*, AttributeValue, DynamicNode, VText};
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::Write; use std::fmt::Write;
use std::rc::Rc; use std::rc::Rc;
@@ -18,7 +18,7 @@ impl SsrRender {
let root = scope.root_node(); let root = scope.root_node();
let mut out = String::new(); let mut out = String::new();
self.render_template(&mut out, dom, root).unwrap(); // self.render_template(&mut out, dom, root).unwrap();
out out
} }
@@ -46,32 +46,37 @@ impl SsrRender {
}; };
} }
Segment::Node(idx) => match &template.dynamic_nodes[*idx] { Segment::Node(idx) => match &template.dynamic_nodes[*idx] {
DynamicNode::Text { value, inner, .. } => { DynamicNode::Component(_) => todo!(),
// in SSR, we are concerned that we can't hunt down the right text node since they might get merged DynamicNode::Text(_) => todo!(),
if !*inner { DynamicNode::Fragment(_) => todo!(),
write!(buf, "<!--#-->")?; DynamicNode::Placeholder(_) => todo!(),
} // todo!()
// DynamicNode::Text(VText { id, value }) => {
// // in SSR, we are concerned that we can't hunt down the right text node since they might get merged
// // if !*inner {
// write!(buf, "<!--#-->")?;
// // }
// todo: escape the text // // todo: escape the text
write!(buf, "{}", value)?; // write!(buf, "{}", value)?;
if !*inner { // // if !*inner {
write!(buf, "<!--/#-->")?; // write!(buf, "<!--/#-->")?;
} // // }
} // }
DynamicNode::Fragment { nodes, .. } => { // DynamicNode::Fragment { nodes, .. } => {
for child in *nodes { // for child in *nodes {
self.render_template(buf, dom, child)?; // self.render_template(buf, dom, child)?;
} // }
} // }
DynamicNode::Component { scope, .. } => { // DynamicNode::Component { scope, .. } => {
let id = scope.get().unwrap(); // let id = scope.get().unwrap();
let scope = dom.get_scope(id).unwrap(); // let scope = dom.get_scope(id).unwrap();
self.render_template(buf, dom, scope.root_node())?; // self.render_template(buf, dom, scope.root_node())?;
} // }
DynamicNode::Placeholder(_el) => { // DynamicNode::Placeholder(_el) => {
write!(buf, "<!--placeholder-->")?; // write!(buf, "<!--placeholder-->")?;
} // }
}, },
Segment::PreRendered(contents) => buf.push_str(contents), Segment::PreRendered(contents) => buf.push_str(contents),
@@ -107,24 +112,24 @@ fn to_string_works() {
use Segment::*; use Segment::*;
assert_eq!( // assert_eq!(
StringCache::from_template(&dom.base_scope().root_node()) // StringCache::from_template(&dom.base_scope().root_node())
.unwrap() // .unwrap()
.segments, // .segments,
vec![ // vec![
PreRendered("<div class=\"asdasdasd\" class=\"asdasdasd\"".into(),), // PreRendered("<div class=\"asdasdasd\" class=\"asdasdasd\"".into(),),
Attr(0,), // Attr(0,),
PreRendered(">Hello world 1 -->".into(),), // PreRendered(">Hello world 1 -->".into(),),
Node(0,), // Node(0,),
PreRendered("<-- Hello world 2<div>nest 1</div><div></div><div>nest 2</div>".into(),), // PreRendered("<-- Hello world 2<div>nest 1</div><div></div><div>nest 2</div>".into(),),
Node(1,), // Node(1,),
Node(2,), // Node(2,),
PreRendered("</div>".into(),), // PreRendered("</div>".into(),),
] // ]
); // );
assert_eq!( // assert_eq!(
SsrRender::default().render_vdom(&dom), // SsrRender::default().render_vdom(&dom),
"<div class=\"asdasdasd\" class=\"asdasdasd\" id=\"id-123\">Hello world 1 --><!--#-->123<!--/#--><-- Hello world 2<div>nest 1</div><div></div><div>nest 2</div><!--#--></diiiiiiiiv><!--/#--><div><!--#-->finalize 0<!--/#--></div><div><!--#-->finalize 1<!--/#--></div><div><!--#-->finalize 2<!--/#--></div><div><!--#-->finalize 3<!--/#--></div><div><!--#-->finalize 4<!--/#--></div></div>" // "<div class=\"asdasdasd\" class=\"asdasdasd\" id=\"id-123\">Hello world 1 --><!--#-->123<!--/#--><-- Hello world 2<div>nest 1</div><div></div><div>nest 2</div><!--#--></diiiiiiiiv><!--/#--><div><!--#-->finalize 0<!--/#--></div><div><!--#-->finalize 1<!--/#--></div><div><!--#-->finalize 2<!--/#--></div><div><!--#-->finalize 3<!--/#--></div><div><!--#-->finalize 4<!--/#--></div></div>"
); // );
} }