mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-11 07:04:13 +00:00
fix: use arcwake instead of rcwake
This commit is contained in:
parent
651ab3e7e3
commit
2fa3fe1fc0
16 changed files with 326 additions and 79 deletions
|
@ -1,4 +1,4 @@
|
|||
use std::marker::PhantomData;
|
||||
use std::{marker::PhantomData, panic::AssertUnwindSafe};
|
||||
|
||||
use crate::{
|
||||
innerlude::Scoped,
|
||||
|
@ -62,12 +62,19 @@ where
|
|||
}
|
||||
|
||||
fn render(&'a self, cx: &'a ScopeState) -> RenderReturn<'a> {
|
||||
let scope: &mut Scoped<P> = cx.bump().alloc(Scoped {
|
||||
props: &self.props,
|
||||
scope: cx,
|
||||
});
|
||||
let res = std::panic::catch_unwind(AssertUnwindSafe(move || {
|
||||
// Call the render function directly
|
||||
let scope: &mut Scoped<P> = cx.bump().alloc(Scoped {
|
||||
props: &self.props,
|
||||
scope: cx,
|
||||
});
|
||||
|
||||
// Call the render function directly
|
||||
(self.render_fn)(scope).into_return(cx)
|
||||
(self.render_fn)(scope).into_return(cx)
|
||||
}));
|
||||
|
||||
match res {
|
||||
Ok(e) => e,
|
||||
Err(_) => RenderReturn::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,12 +81,12 @@ impl VirtualDom {
|
|||
self.ensure_drop_safety(id);
|
||||
|
||||
if let Some(root) = self.scopes[id.0].as_ref().try_root_node() {
|
||||
if let RenderReturn::Sync(Some(node)) = unsafe { root.extend_lifetime_ref() } {
|
||||
if let RenderReturn::Ready(node) = unsafe { root.extend_lifetime_ref() } {
|
||||
self.drop_scope_inner(node)
|
||||
}
|
||||
}
|
||||
if let Some(root) = unsafe { self.scopes[id.0].as_ref().previous_frame().try_load_node() } {
|
||||
if let RenderReturn::Sync(Some(node)) = unsafe { root.extend_lifetime_ref() } {
|
||||
if let RenderReturn::Ready(node) = unsafe { root.extend_lifetime_ref() } {
|
||||
self.drop_scope_inner(node)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -354,8 +354,13 @@ impl<'b> VirtualDom {
|
|||
use RenderReturn::*;
|
||||
|
||||
match return_nodes {
|
||||
Sync(Some(t)) => self.mount_component(scope, template, t, idx),
|
||||
Sync(None) => todo!("Propogate error upwards"),
|
||||
Ready(t) => self.mount_component(scope, template, t, idx),
|
||||
Aborted(t) => {
|
||||
self.mutations
|
||||
.push(Mutation::CreatePlaceholder { id: ElementId(999) });
|
||||
|
||||
1
|
||||
}
|
||||
Async(_) => self.mount_component_placeholder(template, idx, scope),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,29 +30,44 @@ impl<'b> VirtualDom {
|
|||
.try_load_node()
|
||||
.expect("Call rebuild before diffing");
|
||||
|
||||
use RenderReturn::{Async, Sync};
|
||||
use RenderReturn::{Aborted, Async, Ready};
|
||||
|
||||
match (old, new) {
|
||||
(Sync(Some(l)), Sync(Some(r))) => self.diff_node(l, r),
|
||||
// Normal pathway
|
||||
(Ready(l), Ready(r)) => self.diff_node(l, r),
|
||||
|
||||
// Err cases
|
||||
(Sync(Some(l)), Sync(None)) => self.diff_ok_to_err(l),
|
||||
(Sync(None), Sync(Some(r))) => self.diff_err_to_ok(r),
|
||||
(Sync(None), Sync(None)) => { /* nothing */ }
|
||||
// Unwind the mutations if need be
|
||||
(Ready(l), Aborted(p)) => self.diff_ok_to_err(l, p),
|
||||
|
||||
// Async
|
||||
(Sync(Some(_l)), Async(_)) => todo!(),
|
||||
(Sync(None), Async(_)) => todo!(),
|
||||
(Async(_), Sync(Some(_r))) => todo!(),
|
||||
(Async(_), Sync(None)) => { /* nothing */ }
|
||||
(Async(_), Async(_)) => { /* nothing */ }
|
||||
// Just move over the placeholder
|
||||
(Aborted(l), Aborted(r)) => r.id.set(l.id.get()),
|
||||
|
||||
// Becomes async, do nothing while we ait
|
||||
(Ready(nodes), Async(fut)) => todo!(),
|
||||
|
||||
// Placeholder becomes something
|
||||
// We should also clear the error now
|
||||
(Aborted(l), Ready(r)) => self.replace_placeholder(l, [r]),
|
||||
|
||||
(Aborted(_), Async(_)) => todo!("async should not resolve here"),
|
||||
(Async(_), Ready(_)) => todo!("async should not resolve here"),
|
||||
(Async(_), Aborted(_)) => todo!("async should not resolve here"),
|
||||
(Async(_), Async(_)) => {
|
||||
// All suspense should resolve before we diff it again
|
||||
panic!("Should not roll from suspense to suspense.");
|
||||
}
|
||||
};
|
||||
}
|
||||
self.scope_stack.pop();
|
||||
}
|
||||
|
||||
fn diff_ok_to_err(&mut self, _l: &'b VNode<'b>) {}
|
||||
fn diff_err_to_ok(&mut self, _l: &'b VNode<'b>) {}
|
||||
fn diff_ok_to_err(&mut self, _l: &'b VNode<'b>, _p: &'b VPlaceholder) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn diff_err_to_ok(&mut self, _l: &'b VNode<'b>) {
|
||||
todo!("Dioxus cannot currently recover a component after it has been errored. It must be removed from a parent");
|
||||
}
|
||||
|
||||
fn diff_node(&mut self, left_template: &'b VNode<'b>, right_template: &'b VNode<'b>) {
|
||||
// If the templates are the same, we don't need to do anything, nor do we want to
|
||||
|
@ -118,7 +133,7 @@ impl<'b> VirtualDom {
|
|||
(Fragment(left), Fragment(right)) => self.diff_non_empty_fragment(left, right),
|
||||
(Placeholder(left), Placeholder(right)) => right.id.set(left.id.get()),
|
||||
(Component(left), Component(right)) => self.diff_vcomponent(left, right, node, idx),
|
||||
(Placeholder(left), Fragment(right)) => self.replace_placeholder(left, right),
|
||||
(Placeholder(left), Fragment(right)) => self.replace_placeholder(left, *right),
|
||||
(Fragment(left), Placeholder(right)) => self.node_to_placeholder(left, right),
|
||||
_ => todo!("This is an usual custom case for dynamic nodes. We don't know how to handle it yet."),
|
||||
};
|
||||
|
@ -679,7 +694,8 @@ impl<'b> VirtualDom {
|
|||
Component(comp) => {
|
||||
let scope = comp.scope.get().unwrap();
|
||||
match unsafe { self.scopes[scope.0].root_node().extend_lifetime_ref() } {
|
||||
RenderReturn::Sync(Some(node)) => self.push_all_real_nodes(node),
|
||||
RenderReturn::Ready(node) => self.push_all_real_nodes(node),
|
||||
RenderReturn::Aborted(node) => todo!(),
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
@ -707,7 +723,11 @@ impl<'b> VirtualDom {
|
|||
}
|
||||
|
||||
/// Simply replace a placeholder with a list of nodes
|
||||
fn replace_placeholder(&mut self, l: &'b VPlaceholder, r: &'b [VNode<'b>]) {
|
||||
fn replace_placeholder(
|
||||
&mut self,
|
||||
l: &'b VPlaceholder,
|
||||
r: impl IntoIterator<Item = &'b VNode<'b>>,
|
||||
) {
|
||||
let m = self.create_children(r);
|
||||
let id = l.id.get().unwrap();
|
||||
self.mutations.push(Mutation::ReplaceWith { id, m });
|
||||
|
@ -856,7 +876,7 @@ impl<'b> VirtualDom {
|
|||
let scope = comp.scope.take().unwrap();
|
||||
|
||||
match unsafe { self.scopes[scope.0].root_node().extend_lifetime_ref() } {
|
||||
RenderReturn::Sync(Some(t)) => {
|
||||
RenderReturn::Ready(t) => {
|
||||
println!("Removing component node sync {:?}", gen_muts);
|
||||
self.remove_node(t, gen_muts)
|
||||
}
|
||||
|
@ -886,7 +906,7 @@ impl<'b> VirtualDom {
|
|||
Some(Component(comp)) => {
|
||||
let scope = comp.scope.get().unwrap();
|
||||
match unsafe { self.scopes[scope.0].root_node().extend_lifetime_ref() } {
|
||||
RenderReturn::Sync(Some(t)) => self.find_first_element(t),
|
||||
RenderReturn::Ready(t) => self.find_first_element(t),
|
||||
_ => todo!("cannot handle nonstandard nodes"),
|
||||
}
|
||||
}
|
||||
|
@ -902,7 +922,7 @@ impl<'b> VirtualDom {
|
|||
Some(Component(comp)) => {
|
||||
let scope = comp.scope.get().unwrap();
|
||||
match unsafe { self.scopes[scope.0].root_node().extend_lifetime_ref() } {
|
||||
RenderReturn::Sync(Some(t)) => self.find_last_element(t),
|
||||
RenderReturn::Ready(t) => self.find_last_element(t),
|
||||
_ => todo!("cannot handle nonstandard nodes"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,37 @@
|
|||
use std::cell::RefCell;
|
||||
|
||||
use crate::ScopeId;
|
||||
use crate::{ScopeId, ScopeState};
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
cell::RefCell,
|
||||
fmt::Debug,
|
||||
};
|
||||
|
||||
/// A boundary that will capture any errors from child components
|
||||
#[allow(dead_code)]
|
||||
pub struct ErrorBoundary {
|
||||
error: RefCell<Option<ScopeId>>,
|
||||
error: RefCell<Option<CapturedError>>,
|
||||
id: ScopeId,
|
||||
}
|
||||
|
||||
/// An instance of an error captured by a descendant component.
|
||||
pub struct CapturedError {
|
||||
/// The error captured by the error boundary
|
||||
pub error: Box<dyn Debug + 'static>,
|
||||
|
||||
/// The scope that threw the error
|
||||
pub scope: ScopeId,
|
||||
}
|
||||
|
||||
impl CapturedError {
|
||||
/// Downcast the error type into a concrete error type
|
||||
pub fn downcast<T: 'static>(&self) -> Option<&T> {
|
||||
if TypeId::of::<T>() == self.error.type_id() {
|
||||
let raw = self.error.as_ref() as *const _ as *const T;
|
||||
Some(unsafe { &*raw })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ErrorBoundary {
|
||||
pub fn new(id: ScopeId) -> Self {
|
||||
Self {
|
||||
|
@ -16,4 +39,104 @@ impl ErrorBoundary {
|
|||
id,
|
||||
}
|
||||
}
|
||||
|
||||
/// Push an error into this Error Boundary
|
||||
pub fn insert_error(&self, scope: ScopeId, error: Box<dyn Debug + 'static>) {
|
||||
self.error.replace(Some(CapturedError { error, scope }));
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait to allow results to be thrown upwards to the nearest Error Boundary
|
||||
///
|
||||
/// The canonical way of using this trait is to throw results from hooks, aborting rendering
|
||||
/// through question mark synax. The throw method returns an option that evalutes to None
|
||||
/// if there is an error, injecting the error to the nearest error boundary.
|
||||
///
|
||||
/// If the value is `Ok`, then throw returns the value, not aborting the rendering preocess.
|
||||
///
|
||||
/// The call stack is saved for this component and provided to the error boundary
|
||||
///
|
||||
/// ```rust, ignore
|
||||
///
|
||||
/// #[inline_props]
|
||||
/// fn app(cx: Scope, count: String) -> Element {
|
||||
/// let id: i32 = count.parse().throw(cx)?;
|
||||
///
|
||||
/// cx.render(rsx! {
|
||||
/// div { "Count {}" }
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
pub trait Throw {
|
||||
/// The value that will be returned in if the given value is `Ok`.
|
||||
type Out;
|
||||
|
||||
/// Returns an option that evalutes to None if there is an error, injecting the error to the nearest error boundary.
|
||||
///
|
||||
/// If the value is `Ok`, then throw returns the value, not aborting the rendering preocess.
|
||||
///
|
||||
/// The call stack is saved for this component and provided to the error boundary
|
||||
///
|
||||
///
|
||||
/// Note that you can also manually throw errors using the throw method on `ScopeState` directly,
|
||||
/// which is what this trait shells out to.
|
||||
///
|
||||
///
|
||||
/// ```rust, ignore
|
||||
///
|
||||
/// #[inline_props]
|
||||
/// fn app(cx: Scope, count: String) -> Element {
|
||||
/// let id: i32 = count.parse().throw(cx)?;
|
||||
///
|
||||
/// cx.render(rsx! {
|
||||
/// div { "Count {}" }
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
fn throw(self, cx: &ScopeState) -> Option<Self::Out>;
|
||||
}
|
||||
|
||||
/// We call clone on any errors that can be owned out of a reference
|
||||
impl<'a, T, O: Debug + 'static, E: ToOwned<Owned = O>> Throw for &'a Result<T, E> {
|
||||
type Out = &'a T;
|
||||
|
||||
fn throw(self, cx: &ScopeState) -> Option<Self::Out> {
|
||||
match self {
|
||||
Ok(t) => Some(t),
|
||||
Err(e) => {
|
||||
cx.throw(e.to_owned());
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Or just throw errors we know about
|
||||
impl<T, E: Debug + 'static> Throw for Result<T, E> {
|
||||
type Out = T;
|
||||
|
||||
fn throw(self, cx: &ScopeState) -> Option<T> {
|
||||
match self {
|
||||
Ok(t) => Some(t),
|
||||
Err(e) => {
|
||||
cx.throw(e);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Or just throw errors we know about
|
||||
impl<T> Throw for Option<T> {
|
||||
type Out = T;
|
||||
|
||||
fn throw(self, cx: &ScopeState) -> Option<T> {
|
||||
match self {
|
||||
Some(t) => Some(t),
|
||||
None => {
|
||||
cx.throw("None error.");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,10 +70,10 @@ pub(crate) mod innerlude {
|
|||
}
|
||||
|
||||
pub use crate::innerlude::{
|
||||
fc_to_builder, Attribute, AttributeValue, Component, DynamicNode, Element, ElementId, Event,
|
||||
Fragment, IntoDynNode, LazyNodes, Mutation, Mutations, Properties, RenderReturn, Scope,
|
||||
ScopeId, ScopeState, Scoped, SuspenseContext, TaskId, Template, TemplateAttribute,
|
||||
TemplateNode, VComponent, VNode, VText, VirtualDom,
|
||||
fc_to_builder, Attribute, AttributeValue, CapturedError, Component, DynamicNode, Element,
|
||||
ElementId, Event, Fragment, IntoDynNode, LazyNodes, Mutation, Mutations, Properties,
|
||||
RenderReturn, Scope, ScopeId, ScopeState, Scoped, SuspenseContext, TaskId, Template,
|
||||
TemplateAttribute, TemplateNode, VComponent, VNode, VText, VirtualDom,
|
||||
};
|
||||
|
||||
/// The purpose of this module is to alleviate imports of many common types
|
||||
|
@ -83,7 +83,7 @@ pub mod prelude {
|
|||
pub use crate::innerlude::{
|
||||
fc_to_builder, Component, Element, Event, EventHandler, Fragment, LazyNodes, Properties,
|
||||
Scope, ScopeId, ScopeState, Scoped, TaskId, Template, TemplateAttribute, TemplateNode,
|
||||
VNode, VirtualDom,
|
||||
Throw, VNode, VirtualDom,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -21,12 +21,26 @@ pub type TemplateId = &'static str;
|
|||
/// you might need to handle the case where there's no node immediately ready.
|
||||
pub enum RenderReturn<'a> {
|
||||
/// A currently-available element
|
||||
Sync(Element<'a>),
|
||||
Ready(VNode<'a>),
|
||||
|
||||
/// The component aborted rendering early. It might've thrown an error.
|
||||
///
|
||||
/// In its place we've produced a placeholder to locate its spot in the dom when
|
||||
/// it recovers.
|
||||
///
|
||||
/// The old nodes are kept around
|
||||
Aborted(VPlaceholder),
|
||||
|
||||
/// An ongoing future that will resolve to a [`Element`]
|
||||
Async(BumpBox<'a, dyn Future<Output = Element<'a>> + 'a>),
|
||||
}
|
||||
|
||||
impl<'a> Default for RenderReturn<'a> {
|
||||
fn default() -> Self {
|
||||
RenderReturn::Aborted(VPlaceholder::default())
|
||||
}
|
||||
}
|
||||
|
||||
/// A reference to a template along with any context needed to hydrate it
|
||||
///
|
||||
/// The dynamic parts of the template are stored separately from the static parts. This allows faster diffing by skipping
|
||||
|
@ -416,7 +430,10 @@ pub trait ComponentReturn<'a, A = ()> {
|
|||
|
||||
impl<'a> ComponentReturn<'a> for Element<'a> {
|
||||
fn into_return(self, _cx: &ScopeState) -> RenderReturn<'a> {
|
||||
RenderReturn::Sync(self)
|
||||
match self {
|
||||
Some(node) => RenderReturn::Ready(node),
|
||||
None => RenderReturn::Aborted(VPlaceholder::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ mod waker;
|
|||
|
||||
pub use suspense::*;
|
||||
pub use task::*;
|
||||
pub use waker::RcWake;
|
||||
pub use waker::ArcWake;
|
||||
|
||||
/// The type of message that can be sent to the scheduler.
|
||||
///
|
||||
|
@ -25,16 +25,16 @@ pub(crate) enum SchedulerMsg {
|
|||
SuspenseNotified(SuspenseId),
|
||||
}
|
||||
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
use std::{cell::RefCell, rc::Rc, sync::Arc};
|
||||
|
||||
pub(crate) struct Scheduler {
|
||||
pub sender: futures_channel::mpsc::UnboundedSender<SchedulerMsg>,
|
||||
|
||||
/// Tasks created with cx.spawn
|
||||
pub tasks: RefCell<Slab<Rc<LocalTask>>>,
|
||||
pub tasks: RefCell<Slab<Arc<LocalTask>>>,
|
||||
|
||||
/// Async components
|
||||
pub leaves: RefCell<Slab<Rc<SuspenseLeaf>>>,
|
||||
pub leaves: RefCell<Slab<Arc<SuspenseLeaf>>>,
|
||||
}
|
||||
|
||||
impl Scheduler {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use super::{waker::RcWake, SchedulerMsg};
|
||||
use super::{waker::ArcWake, SchedulerMsg};
|
||||
use crate::ElementId;
|
||||
use crate::{innerlude::Mutations, Element, ScopeId};
|
||||
use std::future::Future;
|
||||
use std::sync::Arc;
|
||||
use std::{
|
||||
cell::{Cell, RefCell},
|
||||
collections::HashSet,
|
||||
|
@ -42,8 +43,8 @@ pub(crate) struct SuspenseLeaf {
|
|||
pub(crate) task: *mut dyn Future<Output = Element<'static>>,
|
||||
}
|
||||
|
||||
impl RcWake for SuspenseLeaf {
|
||||
fn wake_by_ref(arc_self: &Rc<Self>) {
|
||||
impl ArcWake for SuspenseLeaf {
|
||||
fn wake_by_ref(arc_self: &Arc<Self>) {
|
||||
arc_self.notified.set(true);
|
||||
_ = arc_self
|
||||
.tx
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use super::{waker::RcWake, Scheduler, SchedulerMsg};
|
||||
use super::{waker::ArcWake, Scheduler, SchedulerMsg};
|
||||
use crate::ScopeId;
|
||||
use std::cell::RefCell;
|
||||
use std::future::Future;
|
||||
use std::sync::Arc;
|
||||
use std::{pin::Pin, rc::Rc};
|
||||
|
||||
/// A task's unique identifier.
|
||||
|
@ -35,7 +36,7 @@ impl Scheduler {
|
|||
let entry = tasks.vacant_entry();
|
||||
let task_id = TaskId(entry.key());
|
||||
|
||||
entry.insert(Rc::new(LocalTask {
|
||||
entry.insert(Arc::new(LocalTask {
|
||||
id: task_id,
|
||||
tx: self.sender.clone(),
|
||||
task: RefCell::new(Box::pin(task)),
|
||||
|
@ -57,8 +58,8 @@ impl Scheduler {
|
|||
}
|
||||
}
|
||||
|
||||
impl RcWake for LocalTask {
|
||||
fn wake_by_ref(arc_self: &Rc<Self>) {
|
||||
impl ArcWake for LocalTask {
|
||||
fn wake_by_ref(arc_self: &Arc<Self>) {
|
||||
_ = arc_self
|
||||
.tx
|
||||
.unbounded_send(SchedulerMsg::TaskNotified(arc_self.id));
|
||||
|
|
|
@ -10,7 +10,7 @@ use crate::{
|
|||
ScopeId, TaskId, VNode, VirtualDom,
|
||||
};
|
||||
|
||||
use super::{waker::RcWake, SuspenseId};
|
||||
use super::{waker::ArcWake, SuspenseId};
|
||||
|
||||
impl VirtualDom {
|
||||
/// Handle notifications by tasks inside the scheduler
|
||||
|
@ -67,18 +67,21 @@ impl VirtualDom {
|
|||
// we should attach them to that component and then render its children
|
||||
// continue rendering the tree until we hit yet another suspended component
|
||||
if let Poll::Ready(new_nodes) = as_pinned_mut.poll_unpin(&mut cx) {
|
||||
// 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);
|
||||
|
||||
let scope = &mut self.scopes[scope_id.0];
|
||||
let arena = scope.current_frame();
|
||||
|
||||
let ret = arena.bump.alloc(RenderReturn::Sync(new_nodes));
|
||||
let ret = arena.bump.alloc(match new_nodes {
|
||||
Some(new) => RenderReturn::Ready(new),
|
||||
None => RenderReturn::default(),
|
||||
});
|
||||
|
||||
arena.node.set(ret);
|
||||
|
||||
fiber.waiting_on.borrow_mut().remove(&id);
|
||||
|
||||
if let RenderReturn::Sync(Some(template)) = ret {
|
||||
if let RenderReturn::Ready(template) = ret {
|
||||
let mutations_ref = &mut fiber.mutations.borrow_mut();
|
||||
let mutations = &mut **mutations_ref;
|
||||
let template: &VNode = unsafe { std::mem::transmute(template) };
|
||||
|
|
|
@ -1,36 +1,37 @@
|
|||
use std::mem;
|
||||
use std::sync::Arc;
|
||||
use std::task::{RawWaker, RawWakerVTable, Waker};
|
||||
use std::{mem, rc::Rc};
|
||||
|
||||
pub trait RcWake: Sized {
|
||||
pub trait ArcWake: Sized {
|
||||
/// Create a waker from this self-wakening object
|
||||
fn waker(self: &Rc<Self>) -> Waker {
|
||||
unsafe fn rc_vtable<T: RcWake>() -> &'static RawWakerVTable {
|
||||
fn waker(self: &Arc<Self>) -> Waker {
|
||||
unsafe fn rc_vtable<T: ArcWake>() -> &'static RawWakerVTable {
|
||||
&RawWakerVTable::new(
|
||||
|data| {
|
||||
let arc = mem::ManuallyDrop::new(Rc::<T>::from_raw(data.cast::<T>()));
|
||||
let arc = mem::ManuallyDrop::new(Arc::<T>::from_raw(data.cast::<T>()));
|
||||
let _rc_clone: mem::ManuallyDrop<_> = arc.clone();
|
||||
RawWaker::new(data, rc_vtable::<T>())
|
||||
},
|
||||
|data| Rc::from_raw(data.cast::<T>()).wake(),
|
||||
|data| Arc::from_raw(data.cast::<T>()).wake(),
|
||||
|data| {
|
||||
let arc = mem::ManuallyDrop::new(Rc::<T>::from_raw(data.cast::<T>()));
|
||||
RcWake::wake_by_ref(&arc);
|
||||
let arc = mem::ManuallyDrop::new(Arc::<T>::from_raw(data.cast::<T>()));
|
||||
ArcWake::wake_by_ref(&arc);
|
||||
},
|
||||
|data| drop(Rc::<T>::from_raw(data.cast::<T>())),
|
||||
|data| drop(Arc::<T>::from_raw(data.cast::<T>())),
|
||||
)
|
||||
}
|
||||
|
||||
unsafe {
|
||||
Waker::from_raw(RawWaker::new(
|
||||
Rc::into_raw(self.clone()).cast(),
|
||||
Arc::into_raw(self.clone()).cast(),
|
||||
rc_vtable::<Self>(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn wake_by_ref(arc_self: &Rc<Self>);
|
||||
fn wake_by_ref(arc_self: &Arc<Self>);
|
||||
|
||||
fn wake(self: Rc<Self>) {
|
||||
fn wake(self: Arc<Self>) {
|
||||
Self::wake_by_ref(&self)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use crate::{
|
||||
any_props::AnyProps,
|
||||
bump_frame::BumpFrame,
|
||||
innerlude::DirtyScope,
|
||||
innerlude::{DirtyScope, VPlaceholder},
|
||||
innerlude::{SuspenseId, SuspenseLeaf},
|
||||
nodes::RenderReturn,
|
||||
scheduler::RcWake,
|
||||
scheduler::ArcWake,
|
||||
scopes::{ScopeId, ScopeState},
|
||||
virtual_dom::VirtualDom,
|
||||
};
|
||||
|
@ -14,6 +14,7 @@ use std::{
|
|||
mem,
|
||||
pin::Pin,
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
|
@ -79,6 +80,7 @@ impl VirtualDom {
|
|||
// safety: due to how we traverse the tree, we know that the scope is not currently aliased
|
||||
let props: &dyn AnyProps = scope.props.as_ref().unwrap().as_ref();
|
||||
let props: &dyn AnyProps = mem::transmute(props);
|
||||
|
||||
props.render(scope).extend_lifetime()
|
||||
};
|
||||
|
||||
|
@ -89,7 +91,7 @@ impl VirtualDom {
|
|||
let entry = leaves.vacant_entry();
|
||||
let suspense_id = SuspenseId(entry.key());
|
||||
|
||||
let leaf = Rc::new(SuspenseLeaf {
|
||||
let leaf = Arc::new(SuspenseLeaf {
|
||||
scope_id,
|
||||
task: task.as_mut(),
|
||||
id: suspense_id,
|
||||
|
@ -108,7 +110,11 @@ impl VirtualDom {
|
|||
match pinned.poll_unpin(&mut cx) {
|
||||
// If nodes are produced, then set it and we can break
|
||||
Poll::Ready(nodes) => {
|
||||
new_nodes = RenderReturn::Sync(nodes);
|
||||
new_nodes = match nodes {
|
||||
Some(nodes) => RenderReturn::Ready(nodes),
|
||||
None => RenderReturn::default(),
|
||||
};
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -150,6 +156,6 @@ impl VirtualDom {
|
|||
});
|
||||
|
||||
// rebind the lifetime now that its stored internally
|
||||
unsafe { mem::transmute(allocated) }
|
||||
unsafe { allocated.extend_lifetime_ref() }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::{
|
|||
arena::ElementId,
|
||||
bump_frame::BumpFrame,
|
||||
innerlude::{DynamicNode, EventHandler, VComponent, VText},
|
||||
innerlude::{Scheduler, SchedulerMsg},
|
||||
innerlude::{ErrorBoundary, Scheduler, SchedulerMsg},
|
||||
lazynodes::LazyNodes,
|
||||
nodes::{ComponentReturn, IntoAttributeValue, IntoDynNode, RenderReturn},
|
||||
Attribute, AttributeValue, Element, Event, Properties, TaskId,
|
||||
|
@ -14,7 +14,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
|
|||
use std::{
|
||||
any::{Any, TypeId},
|
||||
cell::{Cell, RefCell},
|
||||
fmt::Arguments,
|
||||
fmt::{Arguments, Debug},
|
||||
future::Future,
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
|
@ -509,6 +509,19 @@ impl<'src> ScopeState {
|
|||
AttributeValue::Listener(RefCell::new(Some(boxed)))
|
||||
}
|
||||
|
||||
/// Inject an error into the nearest error boundary and quit rendering
|
||||
///
|
||||
/// The error doesn't need to implement Error or any specific traits since the boundary
|
||||
/// itself will downcast the error into a trait object.
|
||||
pub fn throw(&self, error: impl Debug + 'static) -> Option<()> {
|
||||
if let Some(cx) = self.consume_context::<Rc<ErrorBoundary>>() {
|
||||
cx.insert_error(self.scope_id(), Box::new(error));
|
||||
}
|
||||
|
||||
// Always return none during a throw
|
||||
None
|
||||
}
|
||||
|
||||
/// Store a value between renders. The foundational hook for all other hooks.
|
||||
///
|
||||
/// Accepts an `initializer` closure, which is run on the first use of the hook (typically the initial render). The return value of this closure is stored for the lifetime of the component, and a mutable reference to it is provided on every render as the return value of `use_hook`.
|
||||
|
|
|
@ -477,7 +477,7 @@ impl VirtualDom {
|
|||
pub fn rebuild(&mut self) -> Mutations {
|
||||
match unsafe { self.run_scope(ScopeId(0)).extend_lifetime_ref() } {
|
||||
// Rebuilding implies we append the created elements to the root
|
||||
RenderReturn::Sync(Some(node)) => {
|
||||
RenderReturn::Ready(node) => {
|
||||
let m = self.create_scope(ScopeId(0), node);
|
||||
self.mutations.edits.push(Mutation::AppendChildren {
|
||||
id: ElementId(0),
|
||||
|
@ -485,7 +485,7 @@ impl VirtualDom {
|
|||
});
|
||||
}
|
||||
// If an error occurs, we should try to render the default error component and context where the error occured
|
||||
RenderReturn::Sync(None) => panic!("Cannot catch errors during rebuild"),
|
||||
RenderReturn::Aborted(placeholder) => panic!("Cannot catch errors during rebuild"),
|
||||
RenderReturn::Async(_) => unreachable!("Root scope cannot be an async component"),
|
||||
}
|
||||
|
||||
|
|
50
packages/core/tests/error_boundary.rs
Normal file
50
packages/core/tests/error_boundary.rs
Normal file
|
@ -0,0 +1,50 @@
|
|||
use dioxus::prelude::*;
|
||||
use futures_util::Future;
|
||||
|
||||
#[test]
|
||||
fn catches_panic() {
|
||||
let mut dom = VirtualDom::new(app);
|
||||
|
||||
let a = dom.rebuild();
|
||||
}
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
PanicChild {}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn PanicChild(cx: Scope) -> Element {
|
||||
panic!("Rendering panicked for whatever reason");
|
||||
|
||||
cx.render(rsx! {
|
||||
h1 { "It works!" }
|
||||
})
|
||||
}
|
||||
|
||||
fn ThrowChild(cx: Scope) -> Element {
|
||||
cx.throw(std::io::Error::new(std::io::ErrorKind::AddrInUse, "asd"))?;
|
||||
|
||||
let g: i32 = "123123".parse().throw(cx)?;
|
||||
|
||||
cx.render(rsx! {
|
||||
div {}
|
||||
})
|
||||
}
|
||||
|
||||
fn custom_allocator(cx: Scope) -> Element {
|
||||
let g = String::new();
|
||||
|
||||
let p = g.as_str();
|
||||
|
||||
let g2 = cx.use_hook(|| 123);
|
||||
// cx.spawn(async move {
|
||||
|
||||
// //
|
||||
// // println!("Thig is {p}");
|
||||
// });
|
||||
|
||||
None
|
||||
}
|
Loading…
Reference in a new issue