From d2ce57ba6e78fdfa20cd7e2f0c75dc54894a3d19 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 3 Nov 2022 20:56:31 -0700 Subject: [PATCH] feat: poll async once --- packages/core/src/any_props.rs | 25 ++++-------- packages/core/src/bump_frame.rs | 8 ++-- packages/core/src/create.rs | 67 ++++++++++++++++++++++++++------ packages/core/src/factory.rs | 31 +++++++++++---- packages/core/src/lib.rs | 16 -------- packages/core/src/scope_arena.rs | 7 ++-- packages/core/src/virtualdom.rs | 19 +++++---- packages/rsx/src/lib.rs | 13 ++++++- packages/ssr/src/template.rs | 31 ++++++++++++++- 9 files changed, 150 insertions(+), 67 deletions(-) diff --git a/packages/core/src/any_props.rs b/packages/core/src/any_props.rs index aeecad2ff..2da03686a 100644 --- a/packages/core/src/any_props.rs +++ b/packages/core/src/any_props.rs @@ -3,20 +3,20 @@ use std::marker::PhantomData; use futures_util::Future; use crate::{ - factory::ReturnType, + factory::{ComponentReturn, RenderReturn}, scopes::{Scope, ScopeState}, Element, }; pub trait AnyProps<'a> { fn as_ptr(&self) -> *const (); - fn render(&'a self, bump: &'a ScopeState) -> Element<'a>; + fn render(&'a self, bump: &'a ScopeState) -> RenderReturn<'a>; unsafe fn memoize(&self, other: &dyn AnyProps) -> bool; } pub(crate) struct VComponentProps<'a, P, A, F = Element<'a>> where - F: ReturnType<'a, A>, + F: ComponentReturn<'a, A>, { pub render_fn: fn(Scope<'a, P>) -> F, pub memo: unsafe fn(&P, &P) -> bool, @@ -35,7 +35,7 @@ impl<'a> VComponentProps<'a, (), ()> { } } -impl<'a, P, A, F: ReturnType<'a, A>> VComponentProps<'a, P, A, F> { +impl<'a, P, A, F: ComponentReturn<'a, A>> VComponentProps<'a, P, A, F> { pub(crate) fn new( render_fn: fn(Scope<'a, P>) -> F, memo: unsafe fn(&P, &P) -> bool, @@ -50,7 +50,7 @@ impl<'a, P, A, F: ReturnType<'a, A>> VComponentProps<'a, P, A, F> { } } -impl<'a, P, A, F: ReturnType<'a, A>> AnyProps<'a> for VComponentProps<'a, P, A, F> { +impl<'a, P, A, F: ComponentReturn<'a, A>> AnyProps<'a> for VComponentProps<'a, P, A, F> { fn as_ptr(&self) -> *const () { &self.props as *const _ as *const () } @@ -65,25 +65,16 @@ impl<'a, P, A, F: ReturnType<'a, A>> AnyProps<'a> for VComponentProps<'a, P, A, (self.memo)(real_us, real_other) } - fn render<'b>(&'b self, scope: &'b ScopeState) -> Element<'b> { + fn render(&self, cx: &'a ScopeState) -> RenderReturn<'a> { // Make sure the scope ptr is not null // self.props.state.set(scope); let scope = Scope { props: unsafe { &*self.props }, - scope, + scope: cx, }; // Call the render function directly - // todo: implement async - // let res = match self.render_fn { - // ComponentFn::Sync(f) => { - // let f = unsafe { std::mem::transmute(f) }; - // f(scope) - // } - // ComponentFn::Async(_) => todo!(), - // }; - - todo!() + (self.render_fn)(scope).as_return(cx) } } diff --git a/packages/core/src/bump_frame.rs b/packages/core/src/bump_frame.rs index 8b3f3b86b..d1b3a2027 100644 --- a/packages/core/src/bump_frame.rs +++ b/packages/core/src/bump_frame.rs @@ -2,23 +2,23 @@ use std::cell::Cell; use bumpalo::Bump; -use crate::nodes::VNode; +use crate::factory::RenderReturn; pub struct BumpFrame { pub bump: Bump, - pub node: Cell<*const VNode<'static>>, + pub node: Cell<*mut RenderReturn<'static>>, } impl BumpFrame { pub fn new(capacity: usize) -> Self { let bump = Bump::with_capacity(capacity); Self { bump, - node: Cell::new(std::ptr::null()), + node: Cell::new(std::ptr::null_mut()), } } pub fn reset(&mut self) { self.bump.reset(); - self.node.set(std::ptr::null()); + self.node.set(std::ptr::null_mut()); } } diff --git a/packages/core/src/create.rs b/packages/core/src/create.rs index ff94aed1c..93ef63d60 100644 --- a/packages/core/src/create.rs +++ b/packages/core/src/create.rs @@ -1,9 +1,15 @@ +use std::task::Context; + +use futures_util::task::noop_waker_ref; +use futures_util::{pin_mut, Future}; + +use crate::factory::RenderReturn; use crate::mutations::Mutation; use crate::mutations::Mutation::*; use crate::nodes::VNode; use crate::nodes::{DynamicNode, TemplateNode}; use crate::virtualdom::VirtualDom; -use crate::{AttributeValue, ElementId, TemplateAttribute}; +use crate::{AttributeValue, Element, ElementId, TemplateAttribute}; impl VirtualDom { /// Create this template and write its mutations @@ -96,10 +102,12 @@ impl VirtualDom { while let Some((idx, path)) = dynamic_nodes.next_if(|(_, p)| p[0] == root_idx as u8) { let node = &template.dynamic_nodes[idx]; let m = self.create_dynamic_node(mutations, template, node, idx); - mutations.push(ReplacePlaceholder { - m, - path: &path[1..], - }); + if m > 0 { + mutations.push(ReplacePlaceholder { + m, + path: &path[1..], + }); + } } } @@ -172,16 +180,53 @@ impl VirtualDom { DynamicNode::Component { props, .. } => { let id = self.new_scope(unsafe { std::mem::transmute(props.get()) }); - let template = self.run_scope(id); + let render_ret = self.run_scope(id); // shut up about lifetimes please, I know what I'm doing - let template: &VNode = unsafe { std::mem::transmute(template) }; + let render_ret: &mut RenderReturn = unsafe { std::mem::transmute(render_ret) }; - self.scope_stack.push(id); - let created = self.create(mutations, template); - self.scope_stack.pop(); + match render_ret { + RenderReturn::Sync(Some(template)) => { + self.scope_stack.push(id); + let created = self.create(mutations, template); + self.scope_stack.pop(); + created + } + RenderReturn::Sync(None) => todo!("nodes that return nothing"), + RenderReturn::Async(fut) => { + use futures_util::FutureExt; - created + // Poll the suspense node once to see if we can get any nodes from it + let mut cx = Context::from_waker(&noop_waker_ref()); + let res = fut.poll_unpin(&mut cx); + + match res { + std::task::Poll::Ready(Some(val)) => { + let scope = self.get_scope(id).unwrap(); + let ready = &*scope.bump().alloc(val); + let ready = unsafe { std::mem::transmute(ready) }; + + self.scope_stack.push(id); + let created = self.create(mutations, ready); + self.scope_stack.pop(); + created + } + std::task::Poll::Ready(None) => { + todo!("Pending suspense") + } + std::task::Poll::Pending => { + let new_id = self.next_element(template); + // id.set(new_id); + mutations.push(AssignId { + id: new_id, + path: &template.template.node_paths[idx][1..], + }); + + 0 + } + } + } + } } DynamicNode::Fragment(children) => children diff --git a/packages/core/src/factory.rs b/packages/core/src/factory.rs index 8f53b9503..a25923ca2 100644 --- a/packages/core/src/factory.rs +++ b/packages/core/src/factory.rs @@ -1,5 +1,6 @@ -use std::{cell::Cell, fmt::Arguments}; +use std::{cell::Cell, fmt::Arguments, pin::Pin}; +use bumpalo::boxed::Box as BumpBox; use bumpalo::Bump; use futures_util::Future; @@ -70,7 +71,7 @@ impl ScopeState { } /// Create a new [`VNode::Component`] - pub fn component<'a, P, A, F: ReturnType<'a, A>>( + pub fn component<'a, P, A, F: ComponentReturn<'a, A>>( &'a self, component: fn(Scope<'a, P>) -> F, props: P, @@ -100,11 +101,27 @@ impl ScopeState { } } -pub trait ReturnType<'a, A = ()> {} -impl<'a> ReturnType<'a> for Element<'a> {} +pub trait ComponentReturn<'a, A = ()> { + fn as_return(self, cx: &'a ScopeState) -> RenderReturn<'a>; +} +impl<'a> ComponentReturn<'a> for Element<'a> { + fn as_return(self, _cx: &ScopeState) -> RenderReturn<'a> { + RenderReturn::Sync(self) + } +} pub struct AsyncMarker; -impl<'a, F> ReturnType<'a, AsyncMarker> for F where F: Future> + 'a {} +impl<'a, F> ComponentReturn<'a, AsyncMarker> for F +where + F: Future> + 'a, +{ + fn as_return(self, cx: &'a ScopeState) -> RenderReturn<'a> { + let f: &mut dyn Future> = cx.bump().alloc(self); + let boxed = unsafe { BumpBox::from_raw(f) }; + let pined: Pin> = boxed.into(); + RenderReturn::Async(pined) + } +} #[test] fn takes_it() { @@ -113,9 +130,9 @@ fn takes_it() { } } -enum RenderReturn<'a> { +pub enum RenderReturn<'a> { Sync(Element<'a>), - Async(*mut dyn Future>), + Async(Pin> + 'a>>), } pub trait IntoVnode<'a, A = ()> { diff --git a/packages/core/src/lib.rs b/packages/core/src/lib.rs index 1f99ab43b..f91b1402e 100644 --- a/packages/core/src/lib.rs +++ b/packages/core/src/lib.rs @@ -132,19 +132,3 @@ macro_rules! to_owned { let mut $es = $es.to_owned(); )*} } - -/// get the code location of the code that called this function -#[macro_export] -macro_rules! get_line_num { - () => { - concat!( - file!(), - ":", - line!(), - ":", - column!(), - ":", - env!("CARGO_MANIFEST_DIR") - ) - }; -} diff --git a/packages/core/src/scope_arena.rs b/packages/core/src/scope_arena.rs index 8cb3aa5a1..c1c44712b 100644 --- a/packages/core/src/scope_arena.rs +++ b/packages/core/src/scope_arena.rs @@ -2,6 +2,7 @@ use crate::{ any_props::AnyProps, arena::ElementId, bump_frame::BumpFrame, + factory::RenderReturn, nodes::VNode, scopes::{ScopeId, ScopeState}, virtualdom::VirtualDom, @@ -48,15 +49,15 @@ impl VirtualDom { .and_then(|id| self.scopes.get_mut(id.0).map(|f| f as *mut ScopeState)) } - pub fn run_scope(&mut self, id: ScopeId) -> &VNode { + pub fn run_scope(&mut self, id: ScopeId) -> &mut RenderReturn { let scope = &mut self.scopes[id.0]; scope.hook_idx.set(0); let res = { let props = unsafe { &mut *scope.props }; let props: &mut dyn AnyProps = unsafe { std::mem::transmute(props) }; - let res: VNode = props.render(scope).unwrap(); - let res: VNode<'static> = unsafe { std::mem::transmute(res) }; + let res: RenderReturn = props.render(scope); + let res: RenderReturn<'static> = unsafe { std::mem::transmute(res) }; res }; diff --git a/packages/core/src/virtualdom.rs b/packages/core/src/virtualdom.rs index 314115b24..d2ee2dd93 100644 --- a/packages/core/src/virtualdom.rs +++ b/packages/core/src/virtualdom.rs @@ -2,6 +2,7 @@ use crate::any_props::VComponentProps; use crate::arena::ElementPath; use crate::component::Component; use crate::diff::DirtyScope; +use crate::factory::RenderReturn; use crate::future_container::FutureQueue; use crate::innerlude::SchedulerMsg; use crate::mutations::Mutation; @@ -57,13 +58,17 @@ impl VirtualDom { pub fn rebuild<'a>(&'a mut self, mutations: &mut Vec>) { // let root = self.scopes.get(0).unwrap(); - let root_node = unsafe { std::mem::transmute(self.run_scope(ScopeId(0))) }; - - // let root_node = unsafe { std::mem::transmute(root.root_node()) }; - - self.scope_stack.push(ScopeId(0)); - self.create(mutations, root_node); - self.scope_stack.pop(); + let root_node: &RenderReturn = self.run_scope(ScopeId(0)); + let root_node: &RenderReturn = unsafe { std::mem::transmute(root_node) }; + match root_node { + RenderReturn::Sync(Some(node)) => { + self.scope_stack.push(ScopeId(0)); + self.create(mutations, node); + self.scope_stack.pop(); + } + RenderReturn::Sync(None) => todo!("Handle empty root node"), + RenderReturn::Async(_) => unreachable!(), + } } /// Render what you can given the timeline and then move on diff --git a/packages/rsx/src/lib.rs b/packages/rsx/src/lib.rs index 97731d162..876fcc745 100644 --- a/packages/rsx/src/lib.rs +++ b/packages/rsx/src/lib.rs @@ -110,6 +110,9 @@ impl<'a> ToTokens for TemplateRenderer<'a> { None => quote! { None }, }; + let spndbg = format!("{:?}", self.roots[0].span()); + let root_col = spndbg[9..].split("..").next().unwrap(); + let root_printer = self.roots.iter().enumerate().map(|(idx, root)| { context.current_path.push(idx as u8); let out = context.render_static_node(root); @@ -126,7 +129,15 @@ impl<'a> ToTokens for TemplateRenderer<'a> { out_tokens.append_all(quote! { static TEMPLATE: ::dioxus::core::Template = ::dioxus::core::Template { - id: ::dioxus::core::get_line_num!(), + id: concat!( + file!(), + ":", + line!(), + ":", + column!(), + ":", + #root_col + ), roots: &[ #roots ], node_paths: &[ #(#node_paths),* ], attr_paths: &[ #(#attr_paths),* ], diff --git a/packages/ssr/src/template.rs b/packages/ssr/src/template.rs index 3114bd815..3081670f4 100644 --- a/packages/ssr/src/template.rs +++ b/packages/ssr/src/template.rs @@ -131,7 +131,7 @@ impl SsrRender { // todo: escape the text write!(buf, "{}", value)? } - DynamicNode::Fragment { children } => { + DynamicNode::Fragment(children) => { for child in *children { self.render_template(buf, child)?; } @@ -140,6 +140,9 @@ impl SsrRender { DynamicNode::Component { .. } => { // } + DynamicNode::Placeholder(el) => { + // + } }, Segment::PreRendered(text) => buf.push_str(&text), @@ -217,3 +220,29 @@ fn children_processes_properly() { dom.rebuild(&mut mutations); dbg!(mutations); } + +#[test] +fn async_children() { + use dioxus::prelude::*; + + fn app(cx: Scope) -> Element { + let d = 123; + + render! { + div { + async_child {} + } + } + } + + async fn async_child(cx: Scope<'_>) -> Element { + let d = 123; + render! { p { "{d}" "hii" } } + } + + let mut dom = VirtualDom::new(app); + + let mut mutations = vec![]; + dom.rebuild(&mut mutations); + dbg!(mutations); +}