rip out more lifetimes

This commit is contained in:
Evan Almloff 2024-01-04 18:33:01 -06:00
parent c9ff449e45
commit 3ad16ddd37
18 changed files with 165 additions and 456 deletions

View file

@ -10,9 +10,6 @@ homepage = "https://dioxuslabs.com"
keywords = ["dom", "ui", "gui", "react"]
[dependencies]
# Bumpalo is used as a micro heap backing each component
bumpalo = { version = "3.6", features = ["collections", "boxed"] }
# faster hashmaps
rustc-hash = { workspace = true }

View file

@ -11,9 +11,9 @@ use std::panic::AssertUnwindSafe;
/// # Safety
///
/// This should not be implemented outside this module
pub(crate) unsafe trait AnyProps<'a> {
pub(crate) unsafe trait AnyProps {
fn props_ptr(&self) -> *const ();
fn render(&'a self, bump: &'a ScopeState) -> RenderReturn<'a>;
fn render<'a>(&'a self, bump: &'a ScopeState) -> RenderReturn<'a>;
unsafe fn memoize(&self, other: &dyn AnyProps) -> bool;
}
@ -37,7 +37,7 @@ impl<'a, P> VProps<'a, P> {
}
}
unsafe impl<'a, P> AnyProps<'a> for VProps<'a, P> {
unsafe impl<'a, P> AnyProps for VProps<'a, P> {
fn props_ptr(&self) -> *const () {
&self.props as *const _ as *const ()
}

View file

@ -1,76 +0,0 @@
use crate::nodes::RenderReturn;
use crate::{Attribute, AttributeValue, VComponent};
use bumpalo::Bump;
use std::cell::RefCell;
use std::cell::{Cell, UnsafeCell};
pub(crate) struct BumpFrame {
pub bump: UnsafeCell<Bump>,
pub node: Cell<*const RenderReturn<'static>>,
// The bump allocator will not call the destructor of the objects it allocated. Attributes and props need to have there destructor called, so we keep a list of them to drop before the bump allocator is reset.
pub(crate) attributes_to_drop_before_reset: RefCell<Vec<*const Attribute<'static>>>,
pub(crate) props_to_drop_before_reset: RefCell<Vec<*const VComponent<'static>>>,
}
impl BumpFrame {
pub(crate) fn new(capacity: usize) -> Self {
let bump = Bump::with_capacity(capacity);
Self {
bump: UnsafeCell::new(bump),
node: Cell::new(std::ptr::null()),
attributes_to_drop_before_reset: Default::default(),
props_to_drop_before_reset: Default::default(),
}
}
/// Creates a new lifetime out of thin air
pub(crate) unsafe fn try_load_node<'b>(&self) -> Option<&'b RenderReturn<'b>> {
let node = self.node.get();
if node.is_null() {
return None;
}
unsafe { std::mem::transmute(&*node) }
}
pub(crate) fn bump(&self) -> &Bump {
unsafe { &*self.bump.get() }
}
pub(crate) fn add_attribute_to_drop(&self, attribute: *const Attribute<'static>) {
self.attributes_to_drop_before_reset
.borrow_mut()
.push(attribute);
}
/// Reset the bump allocator and drop all the attributes and props that were allocated in it.
///
/// # Safety
/// The caller must insure that no reference to anything allocated in the bump allocator is available after this function is called.
pub(crate) unsafe fn reset(&self) {
let mut attributes = self.attributes_to_drop_before_reset.borrow_mut();
attributes.drain(..).for_each(|attribute| {
let attribute = unsafe { &*attribute };
if let AttributeValue::Any(l) = &attribute.value {
_ = l.take();
}
});
let mut props = self.props_to_drop_before_reset.borrow_mut();
props.drain(..).for_each(|prop| {
let prop = unsafe { &*prop };
_ = prop.props.borrow_mut().take();
});
unsafe {
let bump = &mut *self.bump.get();
bump.reset();
}
}
}
impl Drop for BumpFrame {
fn drop(&mut self) {
unsafe { self.reset() }
}
}

View file

@ -67,7 +67,7 @@ impl<'b> 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(&mut self, scope: ScopeId, template: &'b VNode<'b>) -> usize {
pub(crate) fn create_scope(&mut self, scope: ScopeId, template: &'b VNode) -> usize {
self.runtime.scope_stack.borrow_mut().push(scope);
let nodes = self.create(template);
self.runtime.scope_stack.borrow_mut().pop();
@ -75,7 +75,7 @@ impl<'b> VirtualDom {
}
/// Create this template and write its mutations
pub(crate) fn create(&mut self, node: &'b VNode<'b>) -> usize {
pub(crate) fn create(&mut self, node: &'b VNode) -> usize {
// check for a overriden template
#[cfg(debug_assertions)]
{
@ -182,7 +182,7 @@ impl<'b> VirtualDom {
1
}
fn write_dynamic_root(&mut self, template: &'b VNode<'b>, idx: usize) -> usize {
fn write_dynamic_root(&mut self, template: &'b VNode, idx: usize) -> usize {
use DynamicNode::*;
match &template.dynamic_nodes[idx] {
node @ Component { .. } | node @ Fragment(_) => {
@ -232,7 +232,7 @@ impl<'b> VirtualDom {
/// We want to make sure we write these nodes while on top of the root
fn write_element_root(
&mut self,
template: &'b VNode<'b>,
template: &'b VNode,
root_idx: usize,
dynamic_attrs: &mut Peekable<impl Iterator<Item = (usize, &'static [u8])>>,
dynamic_nodes_iter: &mut Peekable<impl Iterator<Item = ((usize, usize), &'static [u8])>>,
@ -269,7 +269,7 @@ impl<'b> VirtualDom {
dynamic_nodes_iter: &mut Peekable<impl Iterator<Item = ((usize, usize), &'static [u8])>>,
dynamic_nodes: &[(usize, &'static [u8])],
root_idx: u8,
template: &'b VNode<'b>,
template: &'b VNode,
) {
let (start, end) = match collect_dyn_node_range(dynamic_nodes_iter, root_idx) {
Some((a, b)) => (a, b),
@ -306,7 +306,7 @@ impl<'b> VirtualDom {
attrs: &mut Peekable<impl Iterator<Item = (usize, &'static [u8])>>,
root_idx: u8,
root: ElementId,
node: &'b VNode<'b>,
node: &'b VNode,
) {
while let Some((mut attr_id, path)) =
attrs.next_if(|(_, p)| p.first().copied() == Some(root_idx))
@ -327,7 +327,7 @@ impl<'b> VirtualDom {
fn write_attribute(
&mut self,
template: &'b VNode<'b>,
template: &'b VNode,
idx: usize,
attribute: &'b crate::Attribute<'b>,
id: ElementId,
@ -490,7 +490,7 @@ impl<'b> VirtualDom {
}
}
fn create_dynamic_text(&mut self, parent: ElementRef, text: &'b VText<'b>) -> usize {
fn create_dynamic_text(&mut self, parent: ElementRef, text: &'b VText) -> usize {
// Allocate a dynamic element reference for this text node
let new_id = self.next_element();
@ -538,7 +538,7 @@ impl<'b> VirtualDom {
pub(super) fn create_component_node(
&mut self,
parent: Option<ElementRef>,
component: &'b VComponent<'b>,
component: &'b VComponent,
) -> usize {
use RenderReturn::*;

View file

@ -59,7 +59,7 @@ impl<'b> VirtualDom {
self.runtime.scope_stack.borrow_mut().pop();
}
fn diff_ok_to_err(&mut self, l: &'b VNode<'b>, p: &'b VPlaceholder) {
fn diff_ok_to_err(&mut self, l: &'b VNode, p: &'b VPlaceholder) {
let id = self.next_element();
p.id.set(Some(id));
p.parent.set(l.parent.get());
@ -82,7 +82,7 @@ impl<'b> VirtualDom {
};
}
fn diff_node(&mut self, left_template: &'b VNode<'b>, right_template: &'b VNode<'b>) {
fn diff_node(&mut self, left_template: &'b VNode, right_template: &'b VNode) {
// If hot reloading is enabled, we need to make sure we're using the latest template
#[cfg(debug_assertions)]
{
@ -198,8 +198,8 @@ impl<'b> VirtualDom {
fn diff_vcomponent(
&mut self,
left: &'b VComponent<'b>,
right: &'b VComponent<'b>,
left: &'b VComponent,
right: &'b VComponent,
parent: Option<ElementRef>,
) {
if std::ptr::eq(left, right) {
@ -249,8 +249,8 @@ impl<'b> VirtualDom {
fn replace_vcomponent(
&mut self,
right: &'b VComponent<'b>,
left: &'b VComponent<'b>,
right: &'b VComponent,
left: &'b VComponent,
parent: Option<ElementRef>,
) {
let m = self.create_component_node(parent, right);
@ -307,7 +307,7 @@ impl<'b> VirtualDom {
/// Component { ..props }
/// }
/// ```
fn light_diff_templates(&mut self, left: &'b VNode<'b>, right: &'b VNode<'b>) {
fn light_diff_templates(&mut self, left: &'b VNode, right: &'b VNode) {
let parent = left.parent.take();
match matching_components(left, right) {
None => self.replace(left, [right], parent),
@ -321,7 +321,7 @@ impl<'b> VirtualDom {
///
/// This just moves the ID of the old node over to the new node, and then sets the text of the new node if it's
/// different.
fn diff_vtext(&mut self, left: &'b VText<'b>, right: &'b VText<'b>) {
fn diff_vtext(&mut self, left: &'b VText, right: &'b VText) {
let id = left.id.get().unwrap_or_else(|| self.next_element());
right.id.set(Some(id));
@ -331,12 +331,7 @@ impl<'b> VirtualDom {
}
}
fn diff_non_empty_fragment(
&mut self,
old: &'b [VNode<'b>],
new: &'b [VNode<'b>],
parent: ElementRef,
) {
fn diff_non_empty_fragment(&mut self, old: &'b [VNode], new: &'b [VNode], parent: ElementRef) {
let new_is_keyed = new[0].key.is_some();
let old_is_keyed = old[0].key.is_some();
debug_assert!(
@ -363,12 +358,7 @@ impl<'b> VirtualDom {
// [... parent]
//
// the change list stack is in the same state when this function returns.
fn diff_non_keyed_children(
&mut self,
old: &'b [VNode<'b>],
new: &'b [VNode<'b>],
parent: ElementRef,
) {
fn diff_non_keyed_children(&mut self, old: &'b [VNode], new: &'b [VNode], parent: ElementRef) {
use std::cmp::Ordering;
// Handled these cases in `diff_children` before calling this function.
@ -404,15 +394,10 @@ impl<'b> VirtualDom {
// https://github.com/infernojs/inferno/blob/36fd96/packages/inferno/src/DOM/patching.ts#L530-L739
//
// The stack is empty upon entry.
fn diff_keyed_children(
&mut self,
old: &'b [VNode<'b>],
new: &'b [VNode<'b>],
parent: ElementRef,
) {
fn diff_keyed_children(&mut self, old: &'b [VNode], new: &'b [VNode], parent: ElementRef) {
if cfg!(debug_assertions) {
let mut keys = rustc_hash::FxHashSet::default();
let mut assert_unique_keys = |children: &'b [VNode<'b>]| {
let mut assert_unique_keys = |children: &'b [VNode]| {
keys.clear();
for child in children {
let key = child.key;
@ -485,8 +470,8 @@ impl<'b> VirtualDom {
/// If there is no offset, then this function returns None and the diffing is complete.
fn diff_keyed_ends(
&mut self,
old: &'b [VNode<'b>],
new: &'b [VNode<'b>],
old: &'b [VNode],
new: &'b [VNode],
parent: ElementRef,
) -> Option<(usize, usize)> {
let mut left_offset = 0;
@ -542,12 +527,7 @@ impl<'b> VirtualDom {
//
// Upon exit from this function, it will be restored to that same self.
#[allow(clippy::too_many_lines)]
fn diff_keyed_middle(
&mut self,
old: &'b [VNode<'b>],
new: &'b [VNode<'b>],
parent: ElementRef,
) {
fn diff_keyed_middle(&mut self, old: &'b [VNode], new: &'b [VNode], parent: ElementRef) {
/*
1. Map the old keys into a numerical ordering based on indices.
2. Create a map of old key to its index
@ -729,7 +709,7 @@ impl<'b> VirtualDom {
}
/// Push all the real nodes on the stack
fn push_all_real_nodes(&mut self, node: &'b VNode<'b>) -> usize {
fn push_all_real_nodes(&mut self, node: &'b VNode) -> usize {
node.template
.get()
.roots
@ -783,7 +763,7 @@ impl<'b> VirtualDom {
pub(crate) fn create_children(
&mut self,
nodes: impl IntoIterator<Item = &'b VNode<'b>>,
nodes: impl IntoIterator<Item = &'b VNode>,
parent: Option<ElementRef>,
) -> usize {
nodes
@ -797,8 +777,8 @@ impl<'b> VirtualDom {
fn create_and_insert_before(
&mut self,
new: &'b [VNode<'b>],
before: &'b VNode<'b>,
new: &'b [VNode],
before: &'b VNode,
parent: ElementRef,
) {
let m = self.create_children(new, Some(parent));
@ -806,12 +786,7 @@ impl<'b> VirtualDom {
self.mutations.push(Mutation::InsertBefore { id, m })
}
fn create_and_insert_after(
&mut self,
new: &'b [VNode<'b>],
after: &'b VNode<'b>,
parent: ElementRef,
) {
fn create_and_insert_after(&mut self, new: &'b [VNode], after: &'b VNode, parent: ElementRef) {
let m = self.create_children(new, Some(parent));
let id = self.find_last_element(after);
self.mutations.push(Mutation::InsertAfter { id, m })
@ -821,7 +796,7 @@ impl<'b> VirtualDom {
fn replace_placeholder(
&mut self,
l: &'b VPlaceholder,
r: impl IntoIterator<Item = &'b VNode<'b>>,
r: impl IntoIterator<Item = &'b VNode>,
parent: ElementRef,
) {
let m = self.create_children(r, Some(parent));
@ -832,8 +807,8 @@ impl<'b> VirtualDom {
fn replace(
&mut self,
left: &'b VNode<'b>,
right: impl IntoIterator<Item = &'b VNode<'b>>,
left: &'b VNode,
right: impl IntoIterator<Item = &'b VNode>,
parent: Option<ElementRef>,
) {
let m = self.create_children(right, parent);
@ -855,7 +830,7 @@ impl<'b> VirtualDom {
};
}
fn node_to_placeholder(&mut self, l: &'b [VNode<'b>], r: &'b VPlaceholder, parent: ElementRef) {
fn node_to_placeholder(&mut self, l: &'b [VNode], r: &'b VPlaceholder, parent: ElementRef) {
// Create the placeholder first, ensuring we get a dedicated ID for the placeholder
let placeholder = self.next_element();
@ -878,14 +853,14 @@ impl<'b> VirtualDom {
/// Remove these nodes from the dom
/// Wont generate mutations for the inner nodes
fn remove_nodes(&mut self, nodes: &'b [VNode<'b>]) {
fn remove_nodes(&mut self, nodes: &'b [VNode]) {
nodes
.iter()
.rev()
.for_each(|node| self.remove_node(node, true));
}
fn remove_node(&mut self, node: &'b VNode<'b>, gen_muts: bool) {
fn remove_node(&mut self, node: &'b VNode, gen_muts: bool) {
// Clean up any attributes that have claimed a static node as dynamic for mount/unmounta
// Will not generate mutations!
self.reclaim_attributes(node);
@ -903,7 +878,7 @@ impl<'b> VirtualDom {
self.reclaim_vnode_id(node);
}
fn reclaim_vnode_id(&mut self, node: &'b VNode<'b>) {
fn reclaim_vnode_id(&mut self, node: &'b VNode) {
// Clean up the vnode id
if let Some(id) = node.stable_id() {
self.element_refs.remove(id.0);
@ -1025,7 +1000,7 @@ impl<'b> VirtualDom {
self.drop_scope(scope, false);
}
fn find_first_element(&self, node: &'b VNode<'b>) -> ElementId {
fn find_first_element(&self, node: &'b VNode) -> ElementId {
match node.dynamic_root(0) {
None => node.root_ids.borrow()[0],
Some(Text(t)) => t.id.get().unwrap(),
@ -1046,7 +1021,7 @@ impl<'b> VirtualDom {
}
}
fn find_last_element(&self, node: &'b VNode<'b>) -> ElementId {
fn find_last_element(&self, node: &'b VNode) -> ElementId {
match node.dynamic_root(node.template.get().roots.len() - 1) {
None => *node.root_ids.borrow().last().unwrap(),
Some(Text(t)) => t.id.get().unwrap(),
@ -1067,7 +1042,7 @@ impl<'b> VirtualDom {
}
}
pub(crate) fn assign_boundary_ref(&mut self, parent: Option<ElementRef>, child: &'b VNode<'b>) {
pub(crate) fn assign_boundary_ref(&mut self, parent: Option<ElementRef>, child: &'b VNode) {
if let Some(parent) = parent {
// assign the parent of the child
child.parent.set(Some(parent));
@ -1080,7 +1055,7 @@ impl<'b> VirtualDom {
/// We need to check for the obvious case, and the non-obvious case where the template as cloned
///
/// We use the pointer of the dynamic_node list in this case
fn templates_are_the_same<'b>(left_template: &'b VNode<'b>, right_template: &'b VNode<'b>) -> bool {
fn templates_are_the_same<'b>(left_template: &'b VNode, right_template: &'b VNode) -> bool {
std::ptr::eq(left_template, right_template)
}
@ -1092,9 +1067,9 @@ fn templates_are_different(left_template: &VNode, right_template: &VNode) -> boo
}
fn matching_components<'a>(
left: &'a VNode<'a>,
right: &'a VNode<'a>,
) -> Option<Vec<(&'a VComponent<'a>, &'a VComponent<'a>)>> {
left: &'a VNode,
right: &'a VNode,
) -> Option<Vec<(&'a VComponent, &'a VComponent)>> {
let left_template = left.template.get();
let right_template = right.template.get();
if left_template.roots.len() != right_template.roots.len() {

View file

@ -262,7 +262,7 @@ impl<T> Throw for Option<T> {
}
}
pub struct ErrorHandler<'a>(Box<dyn Fn(CapturedError) -> LazyNodes<'a, 'a> + 'a>);
pub struct ErrorHandler(Box<dyn Fn(CapturedError) -> LazyNodes<'a, 'a>>);
impl<'a, F: Fn(CapturedError) -> LazyNodes<'a, 'a> + 'a> From<F> for ErrorHandler<'a> {
fn from(value: F) -> Self {
Self(Box::new(value))
@ -290,7 +290,7 @@ fn default_handler<'a>(error: CapturedError) -> LazyNodes<'a, 'a> {
stable_id: Default::default(),
key: None,
template: std::cell::Cell::new(TEMPLATE),
root_ids: bumpalo::collections::Vec::with_capacity_in(1usize, __cx.bump()).into(),
root_ids: Vec::with_capacity(1usize).into(),
dynamic_nodes: __cx
.bump()
.alloc([__cx.text_node(format_args!("{0}", error))]),
@ -298,45 +298,39 @@ fn default_handler<'a>(error: CapturedError) -> LazyNodes<'a, 'a> {
}
})
}
pub struct ErrorBoundaryProps<'a> {
children: Element<'a>,
handle_error: ErrorHandler<'a>,
pub struct ErrorBoundaryProps {
children: Element,
handle_error: ErrorHandler,
}
impl<'a> ErrorBoundaryProps<'a> {
impl ErrorBoundaryProps {
/**
Create a builder for building `ErrorBoundaryProps`.
On the builder, call `.children(...)`(optional), `.handle_error(...)`(optional) to set the values of the fields.
Finally, call `.build()` to create the instance of `ErrorBoundaryProps`.
*/
#[allow(dead_code)]
pub fn builder() -> ErrorBoundaryPropsBuilder<'a, ((), ())> {
ErrorBoundaryPropsBuilder {
fields: ((), ()),
_phantom: ::core::default::Default::default(),
}
pub fn builder() -> ErrorBoundaryPropsBuilder<((), ())> {
ErrorBoundaryPropsBuilder { fields: ((), ()) }
}
}
#[must_use]
#[doc(hidden)]
#[allow(dead_code, non_camel_case_types, non_snake_case)]
pub struct ErrorBoundaryPropsBuilder<'a, TypedBuilderFields> {
pub struct ErrorBoundaryPropsBuilder<TypedBuilderFields> {
fields: TypedBuilderFields,
_phantom: ::core::marker::PhantomData<&'a ()>,
}
impl<'a, TypedBuilderFields> Clone for ErrorBoundaryPropsBuilder<'a, TypedBuilderFields>
impl<TypedBuilderFields> Clone for ErrorBoundaryPropsBuilder<TypedBuilderFields>
where
TypedBuilderFields: Clone,
{
fn clone(&self) -> Self {
Self {
fields: self.fields.clone(),
_phantom: ::core::default::Default::default(),
}
}
}
impl<'a> Properties for ErrorBoundaryProps<'a> {
type Builder = ErrorBoundaryPropsBuilder<'a, ((), ())>;
const IS_STATIC: bool = false;
impl Properties for ErrorBoundaryProps {
type Builder = ErrorBoundaryPropsBuilder<((), ())>;
fn builder() -> Self::Builder {
ErrorBoundaryProps::builder()
}

View file

@ -147,7 +147,7 @@ impl<T> Default for EventHandler<'_, T> {
}
}
type ExternalListenerCallback<'bump, T> = bumpalo::boxed::Box<'bump, dyn FnMut(T) + 'bump>;
type ExternalListenerCallback<'bump, T> = Box<dyn FnMut(T)>;
impl<T> EventHandler<'_, T> {
/// Call this event handler with the appropriate event type

View file

@ -26,7 +26,7 @@ use crate::innerlude::*;
///
/// You want to use this free-function when your fragment needs a key and simply returning multiple nodes from rsx! won't cut it.
#[allow(non_upper_case_globals, non_snake_case)]
pub fn Fragment<'a>(cx: Scope<'a, FragmentProps<'a>>) -> Element {
pub fn Fragment<'a>(cx: Scope<'a, FragmentProps>) -> Element {
let children = cx.props.0.as_ref()?;
Some(VNode {
key: children.key,
@ -92,8 +92,8 @@ impl<const A: bool> FragmentBuilder<A> {
/// })
/// }
/// ```
impl<'a> Properties for FragmentProps<'a> {
type Builder = FragmentBuilder<'a, false>;
impl<'a> Properties for FragmentProps {
type Builder = FragmentBuilder<false>;
fn builder() -> Self::Builder {
FragmentBuilder(None)
}

View file

@ -1,101 +0,0 @@
//! Support for storing lazy-nodes on the stack
//!
//! This module provides support for a type called `LazyNodes` which is a micro-heap located on the stack to make calls
//! to `rsx!` more efficient.
//!
//! To support returning rsx! from branches in match statements, we need to use dynamic dispatch on [`ScopeState`] closures.
//!
//! This can be done either through boxing directly, or by using dynamic-sized-types and a custom allocator. In our case,
//! we build a tiny alloactor in the stack and allocate the closure into that.
//!
//! The logic for this was borrowed from <https://docs.rs/stack_dst/0.6.1/stack_dst/>. Unfortunately, this crate does not
//! support non-static closures, so we've implemented the core logic of `ValueA` in this module.
#[allow(unused_imports)]
use smallbox::{smallbox, space::S16, SmallBox};
use crate::{innerlude::VNode, ScopeState};
/// A concrete type provider for closures that build [`VNode`] structures.
///
/// This struct wraps lazy structs that build [`VNode`] trees. Normally, we cannot perform a blanket implementation over
/// closures, but if we wrap the closure in a concrete type, we can use it for different branches in matching.
///
///
/// ```rust, ignore
/// LazyNodes::new(|f| {
/// static TEMPLATE: dioxus::core::Template = dioxus::core::Template {
/// name: "main.rs:5:5:20", // Source location of the template for hot reloading
/// roots: &[
/// dioxus::core::TemplateNode::Element {
/// tag: dioxus_elements::div::TAG_NAME,
/// namespace: dioxus_elements::div::NAME_SPACE,
/// attrs: &[],
/// children: &[],
/// },
/// ],
/// node_paths: &[],
/// attr_paths: &[],
/// };
/// dioxus::core::VNode {
/// parent: None,
/// key: None,
/// template: std::cell::Cell::new(TEMPLATE),
/// root_ids: dioxus::core::exports::bumpalo::collections::Vec::with_capacity_in(
/// 1usize,
/// f.bump(),
/// )
/// .into(),
/// dynamic_nodes: f.bump().alloc([]),
/// dynamic_attrs: f.bump().alloc([]),
/// })
/// }
/// ```
///
/// Find more information about how to construct [`VNode`] at <https://dioxuslabs.com/learn/0.4/contributing/walkthrough_readme#the-rsx-macro>
pub struct LazyNodes<'a, 'b> {
#[cfg(not(miri))]
inner: SmallBox<dyn FnMut(&'a ScopeState) -> VNode<'a> + 'b, S16>,
#[cfg(miri)]
inner: Box<dyn FnMut(&'a ScopeState) -> VNode<'a> + 'b>,
}
impl<'a, 'b> LazyNodes<'a, 'b> {
/// Create a new [`LazyNodes`] closure, optimistically placing it onto the stack.
///
/// If the closure cannot fit into the stack allocation (16 bytes), then it
/// is placed on the heap. Most closures will fit into the stack, and is
/// the most optimal way to use the creation function.
pub fn new(val: impl FnOnce(&'a ScopeState) -> VNode<'a> + 'b) -> Self {
// there's no way to call FnOnce without a box, so we need to store it in a slot and use static dispatch
let mut slot = Some(val);
Self {
#[cfg(not(miri))]
inner: smallbox!(move |f| {
let val = slot.take().expect("cannot call LazyNodes twice");
val(f)
}),
#[cfg(miri)]
inner: Box::new(move |f| {
let val = slot.take().expect("cannot call LazyNodes twice");
val(f)
}),
}
}
/// Call the closure with the given factory to produce real [`VNode`].
///
/// ```rust, ignore
/// let f = LazyNodes::new(/* Closure for creating VNodes */);
///
/// let node = f.call(cac);
/// ```
#[must_use]
pub fn call(mut self, f: &'a ScopeState) -> VNode<'a> {
(self.inner)(f)
}
}

View file

@ -5,7 +5,6 @@
mod any_props;
mod arena;
mod bump_frame;
mod create;
mod diff;
mod dirty_scope;
@ -43,7 +42,7 @@ pub(crate) mod innerlude {
/// An [`Element`] is a possibly-none [`VNode`] created by calling `render` on [`Scope`] or [`ScopeState`].
///
/// An Errored [`Element`] will propagate the error to the nearest error boundary.
pub type Element<'a> = Option<VNode<'a>>;
pub type Element = Option<VNode>;
/// A [`Component`] is a function that takes a [`Scope`] and returns an [`Element`].
///
@ -97,9 +96,3 @@ pub mod prelude {
VNode, VirtualDom,
};
}
pub mod exports {
//! Important dependencies that are used by the rest of the library
//! Feel free to just add the dependencies in your own Crates.toml
pub use bumpalo;
}

View file

@ -19,9 +19,9 @@ pub type TemplateId = &'static str;
///
/// Dioxus will do its best to immediately resolve any async components into a regular Element, but as an implementor
/// you might need to handle the case where there's no node immediately ready.
pub enum RenderReturn<'a> {
pub enum RenderReturn {
/// A currently-available element
Ready(VNode<'a>),
Ready(VNode),
/// The component aborted rendering early. It might've thrown an error.
///
@ -30,7 +30,7 @@ pub enum RenderReturn<'a> {
Aborted(VPlaceholder),
}
impl<'a> Default for RenderReturn<'a> {
impl Default for RenderReturn {
fn default() -> Self {
RenderReturn::Aborted(VPlaceholder::default())
}
@ -88,11 +88,11 @@ impl VNode {
/// Create a new VNode
pub fn new(
key: Option<&'a str>,
key: Option<String>,
template: Template<'static>,
root_ids: bumpalo::collections::Vec<'a, ElementId>,
dynamic_nodes: &'a [DynamicNode<'a>],
dynamic_attrs: &'a [Attribute<'a>],
root_ids: Vec<ElementId>,
dynamic_nodes: &'a [DynamicNode],
dynamic_attrs: &'a [Attribute],
) -> Self {
Self {
key,
@ -113,7 +113,7 @@ impl VNode {
/// Load a dynamic root at the given index
///
/// Returns [`None`] if the root is actually a static node (Element/Text)
pub fn dynamic_root(&self, idx: usize) -> Option<&'a DynamicNode<'a>> {
pub fn dynamic_root(&self, idx: usize) -> Option<&'a DynamicNode> {
match &self.template.get().roots[idx] {
TemplateNode::Element { .. } | TemplateNode::Text { text: _ } => None,
TemplateNode::Dynamic { id } | TemplateNode::DynamicText { id } => {
@ -147,7 +147,7 @@ pub struct Template<'a> {
///
/// Unlike react, calls to `rsx!` can have multiple roots. This list supports that paradigm.
#[cfg_attr(feature = "serialize", serde(deserialize_with = "deserialize_leaky"))]
pub roots: &'a [TemplateNode<'a>],
pub roots: &'a [TemplateNode],
/// The paths of each node relative to the root of the template.
///
@ -242,7 +242,7 @@ impl<'a> Template<'a> {
derive(serde::Serialize, serde::Deserialize),
serde(tag = "type")
)]
pub enum TemplateNode<'a> {
pub enum TemplateNode {
/// An statically known element in the dom.
///
/// In HTML this would be something like `<div id="123"> </div>`
@ -250,7 +250,7 @@ pub enum TemplateNode<'a> {
/// The name of the element
///
/// IE for a div, it would be the string "div"
tag: &'a str,
tag: &'static str,
/// The namespace of the element
///
@ -260,23 +260,23 @@ pub enum TemplateNode<'a> {
feature = "serialize",
serde(deserialize_with = "deserialize_option_leaky")
)]
namespace: Option<&'a str>,
namespace: Option<&'static str>,
/// A list of possibly dynamic attribues for this element
///
/// An attribute on a DOM node, such as `id="my-thing"` or `href="https://example.com"`.
#[cfg_attr(feature = "serialize", serde(deserialize_with = "deserialize_leaky"))]
attrs: &'a [TemplateAttribute<'a>],
attrs: &'static [TemplateAttribute],
/// A list of template nodes that define another set of template nodes
#[cfg_attr(feature = "serialize", serde(deserialize_with = "deserialize_leaky"))]
children: &'a [TemplateNode<'a>],
children: &'static [TemplateNode],
},
/// This template node is just a piece of static text
Text {
/// The actual text
text: &'a str,
text: &'static str,
},
/// This template node is unknown, and needs to be created at runtime.
@ -298,7 +298,7 @@ pub enum TemplateNode<'a> {
///
/// This node's index in the DynamicNode list on VNode should match its repsective `Dynamic` index
#[derive(Debug)]
pub enum DynamicNode<'a> {
pub enum DynamicNode {
/// A component node
///
/// Most of the time, Dioxus will actually know which component this is as compile time, but the props and
@ -306,10 +306,10 @@ pub enum DynamicNode<'a> {
///
/// The actual VComponent can be dynamic between two VNodes, though, allowing implementations to swap
/// the render function at runtime
Component(VComponent<'a>),
Component(VComponent),
/// A text node
Text(VText<'a>),
Text(VText),
/// A placeholder
///
@ -322,27 +322,20 @@ pub enum DynamicNode<'a> {
///
/// Note that this is not a list of dynamic nodes. These must be VNodes and created through conditional rendering
/// or iterators.
Fragment(&'a [VNode<'a>]),
Fragment(&'static [VNode]),
}
impl Default for DynamicNode<'_> {
impl Default for DynamicNode {
fn default() -> Self {
Self::Placeholder(Default::default())
}
}
/// An instance of a child component
pub struct VComponent<'a> {
pub struct VComponent {
/// The name of this component
pub name: &'static str,
/// Are the props valid for the 'static lifetime?
///
/// Internally, this is used as a guarantee. Externally, this might be incorrect, so don't count on it.
///
/// This flag is assumed by the [`crate::Properties`] trait which is unsafe to implement
pub(crate) static_props: bool,
/// The assigned Scope for this component
pub(crate) scope: Cell<Option<ScopeId>>,
@ -351,17 +344,17 @@ pub struct VComponent<'a> {
/// It is possible that components get folded at compile time, so these shouldn't be really used as a key
pub(crate) render_fn: *const (),
pub(crate) props: RefCell<Option<Box<dyn AnyProps<'a> + 'a>>>,
pub(crate) props: RefCell<Option<Box<dyn AnyProps>>>,
}
impl<'a> VComponent<'a> {
impl<'a> VComponent {
/// Get the scope that this component is mounted to
pub fn mounted_scope(&self) -> Option<ScopeId> {
self.scope.get()
}
}
impl<'a> std::fmt::Debug for VComponent<'a> {
impl<'a> std::fmt::Debug for VComponent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("VComponent")
.field("name", &self.name)
@ -373,17 +366,17 @@ impl<'a> std::fmt::Debug for VComponent<'a> {
/// An instance of some text, mounted to the DOM
#[derive(Debug)]
pub struct VText<'a> {
pub struct VText {
/// The actual text itself
pub value: &'a str,
pub value: String,
/// The ID of this node in the real DOM
pub(crate) id: Cell<Option<ElementId>>,
}
impl<'a> VText<'a> {
impl<'a> VText {
/// Create a new VText
pub fn new(value: &'a str) -> Self {
pub fn new(value: String) -> Self {
Self {
value,
id: Default::default(),
@ -419,21 +412,21 @@ impl VPlaceholder {
derive(serde::Serialize, serde::Deserialize),
serde(tag = "type")
)]
pub enum TemplateAttribute<'a> {
pub enum TemplateAttribute {
/// This attribute is entirely known at compile time, enabling
Static {
/// The name of this attribute.
///
/// For example, the `href` attribute in `href="https://example.com"`, would have the name "href"
name: &'a str,
name: &'static str,
/// The value of this attribute, known at compile time
///
/// Currently this only accepts &str, so values, even if they're known at compile time, are not known
value: &'a str,
value: &'static str,
/// The namespace of this attribute. Does not exist in the HTML spec
namespace: Option<&'a str>,
namespace: Option<&'static str>,
},
/// The attribute in this position is actually determined dynamically at runtime
@ -447,12 +440,12 @@ pub enum TemplateAttribute<'a> {
/// An attribute on a DOM node, such as `id="my-thing"` or `href="https://example.com"`
#[derive(Debug)]
pub struct Attribute<'a> {
pub struct Attribute {
/// The name of the attribute.
pub name: &'a str,
pub name: &'static str,
/// The value of the attribute
pub value: AttributeValue<'a>,
pub value: AttributeValue,
/// The namespace of the attribute.
///
@ -466,11 +459,11 @@ pub struct Attribute<'a> {
pub(crate) mounted_element: Cell<ElementId>,
}
impl<'a> Attribute<'a> {
impl Attribute {
/// Create a new attribute
pub fn new(
name: &'a str,
value: AttributeValue<'a>,
name: &'static str,
value: AttributeValue,
namespace: Option<&'static str>,
volatile: bool,
) -> Self {
@ -493,9 +486,9 @@ impl<'a> Attribute<'a> {
///
/// These are built-in to be faster during the diffing process. To use a custom value, use the [`AttributeValue::Any`]
/// variant.
pub enum AttributeValue<'a> {
pub enum AttributeValue {
/// Text attribute
Text(&'a str),
Text(String),
/// A float
Float(f64),
@ -507,16 +500,16 @@ pub enum AttributeValue<'a> {
Bool(bool),
/// A listener, like "onclick"
Listener(RefCell<Option<ListenerCb<'a>>>),
Listener(RefCell<Option<ListenerCb>>),
/// An arbitrary value that implements PartialEq and is static
Any(RefCell<Option<BumpBox<'a, dyn AnyValue>>>),
Any(RefCell<Option<Box<dyn AnyValue>>>),
/// A "none" value, resulting in the removal of an attribute from the dom
None,
}
pub type ListenerCb<'a> = BumpBox<'a, dyn FnMut(Event<dyn Any>) + 'a>;
pub type ListenerCb = Box<dyn FnMut(Event<dyn Any>)>;
/// Any of the built-in values that the Dioxus VirtualDom supports as dynamic attributes on elements that are borrowed
///
@ -550,8 +543,8 @@ pub enum BorrowedAttributeValue<'a> {
None,
}
impl<'a> From<&'a AttributeValue<'a>> for BorrowedAttributeValue<'a> {
fn from(value: &'a AttributeValue<'a>) -> Self {
impl<'a> From<&'a AttributeValue> for BorrowedAttributeValue<'a> {
fn from(value: &'a AttributeValue) -> Self {
match value {
AttributeValue::Text(value) => BorrowedAttributeValue::Text(value),
AttributeValue::Float(value) => BorrowedAttributeValue::Float(*value),
@ -613,7 +606,7 @@ where
panic!("Any cannot be deserialized")
}
impl<'a> std::fmt::Debug for AttributeValue<'a> {
impl std::fmt::Debug for AttributeValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Text(arg0) => f.debug_tuple("Text").field(arg0).finish(),
@ -627,7 +620,7 @@ impl<'a> std::fmt::Debug for AttributeValue<'a> {
}
}
impl<'a> PartialEq for AttributeValue<'a> {
impl PartialEq for AttributeValue {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Text(l0), Self::Text(r0)) => l0 == r0,
@ -668,42 +661,33 @@ impl<T: Any + PartialEq + 'static> AnyValue for T {
}
}
impl<'a> RenderReturn<'a> {
pub(crate) unsafe fn extend_lifetime_ref<'c>(&self) -> &'c RenderReturn<'c> {
unsafe { std::mem::transmute(self) }
}
pub(crate) unsafe fn extend_lifetime<'c>(self) -> RenderReturn<'c> {
unsafe { std::mem::transmute(self) }
}
}
/// A trait that allows various items to be converted into a dynamic node for the rsx macro
pub trait IntoDynNode<'a, A = ()> {
/// Consume this item along with a scopestate and produce a DynamicNode
///
/// You can use the bump alloactor of the scopestate to creat the dynamic node
fn into_dyn_node(self, cx: &'a ScopeState) -> DynamicNode<'a>;
fn into_dyn_node(self, cx: &'a ScopeState) -> DynamicNode;
}
impl<'a> IntoDynNode<'a> for () {
fn into_dyn_node(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
fn into_dyn_node(self, _cx: &'a ScopeState) -> DynamicNode {
DynamicNode::default()
}
}
impl<'a> IntoDynNode<'a> for VNode<'a> {
fn into_dyn_node(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
impl<'a> IntoDynNode<'a> for VNode {
fn into_dyn_node(self, _cx: &'a ScopeState) -> DynamicNode {
DynamicNode::Fragment(_cx.bump().alloc([self]))
}
}
impl<'a> IntoDynNode<'a> for DynamicNode<'a> {
fn into_dyn_node(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
impl<'a> IntoDynNode<'a> for DynamicNode {
fn into_dyn_node(self, _cx: &'a ScopeState) -> DynamicNode {
self
}
}
impl<'a, T: IntoDynNode<'a>> IntoDynNode<'a> for Option<T> {
fn into_dyn_node(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
fn into_dyn_node(self, _cx: &'a ScopeState) -> DynamicNode {
match self {
Some(val) => val.into_dyn_node(_cx),
None => DynamicNode::default(),
@ -711,8 +695,8 @@ impl<'a, T: IntoDynNode<'a>> IntoDynNode<'a> for Option<T> {
}
}
impl<'a> IntoDynNode<'a> for &Element<'a> {
fn into_dyn_node(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
impl<'a> IntoDynNode<'a> for &Element {
fn into_dyn_node(self, _cx: &'a ScopeState) -> DynamicNode {
match self.as_ref() {
Some(val) => val.clone().into_dyn_node(_cx),
_ => DynamicNode::default(),
@ -721,13 +705,13 @@ impl<'a> IntoDynNode<'a> for &Element<'a> {
}
impl<'a, 'b> IntoDynNode<'a> for LazyNodes<'a, 'b> {
fn into_dyn_node(self, cx: &'a ScopeState) -> DynamicNode<'a> {
fn into_dyn_node(self, cx: &'a ScopeState) -> DynamicNode {
DynamicNode::Fragment(cx.bump().alloc([cx.render(self).unwrap()]))
}
}
impl<'a, 'b> IntoDynNode<'b> for &'a str {
fn into_dyn_node(self, cx: &'b ScopeState) -> DynamicNode<'b> {
fn into_dyn_node(self, cx: &'b ScopeState) -> DynamicNode {
DynamicNode::Text(VText {
value: cx.bump().alloc_str(self),
id: Default::default(),
@ -745,13 +729,13 @@ impl IntoDynNode<'_> for String {
}
impl<'b> IntoDynNode<'b> for Arguments<'_> {
fn into_dyn_node(self, cx: &'b ScopeState) -> DynamicNode<'b> {
fn into_dyn_node(self, cx: &'b ScopeState) -> DynamicNode {
cx.text_node(self)
}
}
impl<'a> IntoDynNode<'a> for &'a VNode<'a> {
fn into_dyn_node(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
impl<'a> IntoDynNode<'a> for &'a VNode {
fn into_dyn_node(self, _cx: &'a ScopeState) -> DynamicNode {
DynamicNode::Fragment(_cx.bump().alloc([VNode {
parent: self.parent.clone(),
stable_id: self.stable_id.clone(),
@ -764,24 +748,24 @@ impl<'a> IntoDynNode<'a> for &'a VNode<'a> {
}
}
pub trait IntoVNode<'a> {
fn into_vnode(self, _cx: &'a ScopeState) -> VNode<'a>;
pub trait IntoVNode {
fn into_vnode(self) -> VNode;
}
impl<'a> IntoVNode<'a> for VNode<'a> {
fn into_vnode(self, _cx: &'a ScopeState) -> VNode<'a> {
impl<'a> IntoVNode for VNode {
fn into_vnode(self) -> VNode {
self
}
}
impl<'a> IntoVNode<'a> for Element<'a> {
fn into_vnode(self, cx: &'a ScopeState) -> VNode<'a> {
impl<'a> IntoVNode for Element {
fn into_vnode(self) -> VNode {
match self {
Some(val) => val.into_vnode(cx),
_ => VNode::empty(cx).unwrap(),
_ => VNode::empty().unwrap(),
}
}
}
impl<'a, 'b> IntoVNode<'a> for LazyNodes<'a, 'b> {
fn into_vnode(self, cx: &'a ScopeState) -> VNode<'a> {
impl<'a, 'b> IntoVNode for LazyNodes<'a, 'b> {
fn into_vnode(self) -> VNode {
cx.render(self).unwrap()
}
}
@ -791,9 +775,9 @@ pub struct FromNodeIterator;
impl<'a, T, I> IntoDynNode<'a, FromNodeIterator> for T
where
T: Iterator<Item = I>,
I: IntoVNode<'a>,
I: IntoVNode,
{
fn into_dyn_node(self, cx: &'a ScopeState) -> DynamicNode<'a> {
fn into_dyn_node(self, cx: &'a ScopeState) -> DynamicNode {
let mut nodes = bumpalo::collections::Vec::new_in(cx.bump());
nodes.extend(self.into_iter().map(|node| node.into_vnode(cx)));

View file

@ -32,7 +32,7 @@ use crate::innerlude::*;
/// data: &'a str
/// }
/// ```
pub trait Properties: Sized + 'static {
pub trait Properties: Clone + Sized + 'static {
/// The type of the builder for this component.
/// Used to create "in-progress" versions of the props.
type Builder;
@ -66,7 +66,7 @@ impl EmptyBuilder {
/// This utility function launches the builder method so rsx! and html! macros can use the typed-builder pattern
/// to initialize a component's props.
pub fn fc_to_builder<'a, T: Properties + 'a>(_: fn(Scope<'a, T>) -> Element<'a>) -> T::Builder {
pub fn fc_to_builder<'a, T: Properties + 'a>(_: fn(Scope<'a, T>) -> Element) -> T::Builder {
T::builder()
}

View file

@ -1,6 +1,5 @@
use crate::{
any_props::AnyProps,
bump_frame::BumpFrame,
innerlude::DirtyScope,
nodes::RenderReturn,
scope_context::ScopeContext,
@ -11,7 +10,7 @@ use crate::{
impl VirtualDom {
pub(super) fn new_scope(
&mut self,
props: Box<dyn AnyProps<'static>>,
props: Box<dyn AnyProps>,
name: &'static str,
) -> &ScopeState {
let parent_id = self.runtime.current_scope_id();
@ -27,9 +26,6 @@ impl VirtualDom {
props: Some(props),
node_arena_1: BumpFrame::new(0),
node_arena_2: BumpFrame::new(0),
render_cnt: Default::default(),
hooks: Default::default(),
hook_idx: Default::default(),

View file

@ -301,7 +301,7 @@ pub fn provide_root_context<T: 'static + Clone>(value: T) -> Option<T> {
}
/// Suspends the current component
pub fn suspend() -> Option<Element<'static>> {
pub fn suspend() -> Option<Element> {
with_current_scope(|cx| {
cx.suspend();
});

View file

@ -1,16 +1,13 @@
use crate::{
any_props::AnyProps,
any_props::VProps,
bump_frame::BumpFrame,
innerlude::ErrorBoundary,
innerlude::{DynamicNode, EventHandler, VComponent, VNodeId, VText},
lazynodes::LazyNodes,
nodes::{IntoAttributeValue, IntoDynNode, RenderReturn},
runtime::Runtime,
scope_context::ScopeContext,
AnyValue, Attribute, AttributeValue, Element, Event, Properties, TaskId,
};
use bumpalo::{boxed::Box as BumpBox, Bump};
use std::{
any::Any,
cell::{Cell, Ref, RefCell, UnsafeCell},
@ -90,11 +87,11 @@ pub struct ScopeState {
pub(crate) hooks: RefCell<Vec<Box<UnsafeCell<dyn Any>>>>,
pub(crate) hook_idx: Cell<usize>,
pub(crate) borrowed_props: RefCell<Vec<*const VComponent<'static>>>,
pub(crate) borrowed_props: RefCell<Vec<*const VComponent>>,
pub(crate) element_refs_to_drop: RefCell<Vec<VNodeId>>,
pub(crate) attributes_to_drop_before_render: RefCell<Vec<*const Attribute<'static>>>,
pub(crate) attributes_to_drop_before_render: RefCell<Vec<*const Attribute>>,
pub(crate) props: Option<Box<dyn AnyProps<'static>>>,
pub(crate) props: Option<Box<dyn AnyProps>>,
}
impl Drop for ScopeState {
@ -108,22 +105,6 @@ impl<'src> ScopeState {
self.runtime.get_context(self.context_id).unwrap()
}
pub(crate) fn current_frame(&self) -> &BumpFrame {
match self.render_cnt.get() % 2 {
0 => &self.node_arena_1,
1 => &self.node_arena_2,
_ => unreachable!(),
}
}
pub(crate) fn previous_frame(&self) -> &BumpFrame {
match self.render_cnt.get() % 2 {
1 => &self.node_arena_1,
0 => &self.node_arena_2,
_ => unreachable!(),
}
}
/// Get the name of this component
pub fn name(&self) -> &str {
self.context().name
@ -136,17 +117,6 @@ impl<'src> ScopeState {
self.render_cnt.get()
}
/// Get a handle to the currently active bump arena for this Scope
///
/// This is a bump memory allocator. Be careful using this directly since the contents will be wiped on the next render.
/// It's easy to leak memory here since the drop implementation will not be called for any objects allocated in this arena.
///
/// If you need to allocate items that need to be dropped, use bumpalo's box.
pub fn bump(&self) -> &Bump {
// note that this is actually the previous frame since we use that as scratch space while the component is rendering
self.previous_frame().bump()
}
/// 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.
@ -343,7 +313,7 @@ impl<'src> ScopeState {
/// cx.render(lazy_tree)
/// }
///```
pub fn render(&'src self, rsx: LazyNodes<'src, '_>) -> Element<'src> {
pub fn render(&'src self, rsx: LazyNodes<'src, '_>) -> Element {
let element = rsx.call(self);
let mut listeners = self.attributes_to_drop_before_render.borrow_mut();
@ -383,25 +353,13 @@ impl<'src> ScopeState {
}
/// Create a dynamic text node using [`Arguments`] and the [`ScopeState`]'s internal [`Bump`] allocator
pub fn text_node(&'src self, args: Arguments) -> DynamicNode<'src> {
pub fn text_node(&'src self, args: Arguments) -> DynamicNode {
DynamicNode::Text(VText {
value: self.raw_text(args),
id: Default::default(),
})
}
/// Allocate some text inside the [`ScopeState`] from [`Arguments`]
///
/// Uses the currently active [`Bump`] allocator
pub fn raw_text(&'src self, args: Arguments) -> &'src str {
args.as_str().unwrap_or_else(|| {
use bumpalo::core_alloc::fmt::Write;
let mut str_buf = bumpalo::collections::String::new_in(self.bump());
str_buf.write_fmt(args).unwrap();
str_buf.into_bump_str()
})
}
/// Convert any item that implements [`IntoDynNode`] into a [`DynamicNode`] using the internal [`Bump`] allocator
pub fn make_node<'c, I>(&'src self, into: impl IntoDynNode<'src, I> + 'c) -> DynamicNode {
into.into_dyn_node(self)
@ -417,7 +375,7 @@ impl<'src> ScopeState {
value: impl IntoAttributeValue<'src>,
namespace: Option<&'static str>,
volatile: bool,
) -> Attribute<'src> {
) -> Attribute {
Attribute {
name,
namespace,
@ -443,10 +401,10 @@ impl<'src> ScopeState {
/// ```
pub fn component<'child, P>(
&'src self,
component: fn(Scope<'child, P>) -> Element<'child>,
component: fn(Scope<'child, P>) -> Element,
props: P,
fn_name: &'static str,
) -> DynamicNode<'src>
) -> DynamicNode
where
// The properties must be valid until the next bump frame
P: Properties + 'src,
@ -456,13 +414,12 @@ impl<'src> ScopeState {
let vcomp = VProps::new(component, P::memoize, props);
// cast off the lifetime of the render return
let as_dyn: Box<dyn AnyProps<'child> + '_> = Box::new(vcomp);
let extended: Box<dyn AnyProps<'src> + 'src> = unsafe { std::mem::transmute(as_dyn) };
let as_dyn: Box<dyn AnyProps + '_> = Box::new(vcomp);
let extended: Box<dyn AnyProps + 'src> = unsafe { std::mem::transmute(as_dyn) };
DynamicNode::Component(VComponent {
name: fn_name,
render_fn: component as *const (),
static_props: P::IS_STATIC,
props: RefCell::new(Some(extended)),
scope: Default::default(),
})
@ -470,9 +427,9 @@ impl<'src> ScopeState {
/// Create a new [`EventHandler`] from an [`FnMut`]
pub fn event_handler<T>(&'src self, f: impl FnMut(T) + 'src) -> EventHandler<'src, T> {
let handler: &mut dyn FnMut(T) = self.bump().alloc(f);
let caller = unsafe { BumpBox::from_raw(handler as *mut dyn FnMut(T)) };
let callback = RefCell::new(Some(caller));
let callback = RefCell::new(Some(Box::new(move |event: Event<T>| {
f(event.data);
})));
EventHandler {
callback,
origin: self.context().id,
@ -485,34 +442,22 @@ impl<'src> ScopeState {
pub fn listener<T: 'static>(
&'src self,
mut callback: impl FnMut(Event<T>) + 'src,
) -> AttributeValue<'src> {
// safety: there's no other way to create a dynamicly-dispatched bump box other than alloc + from-raw
// This is the suggested way to build a bumpbox
//
// In theory, we could just use regular boxes
let boxed: BumpBox<'src, dyn FnMut(_) + 'src> = unsafe {
BumpBox::from_raw(self.bump().alloc(move |event: Event<dyn Any>| {
) -> AttributeValue {
AttributeValue::Listener(RefCell::new(Some(Box::new(
move |event: Event<dyn Any>| {
if let Ok(data) = event.data.downcast::<T>() {
callback(Event {
propagates: event.propagates,
data,
});
}
}))
};
AttributeValue::Listener(RefCell::new(Some(boxed)))
},
))))
}
/// Create a new [`AttributeValue`] with a value that implements [`AnyValue`]
pub fn any_value<T: AnyValue>(&'src self, value: T) -> AttributeValue<'src> {
// safety: there's no other way to create a dynamicly-dispatched bump box other than alloc + from-raw
// This is the suggested way to build a bumpbox
//
// In theory, we could just use regular boxes
let boxed: BumpBox<'src, dyn AnyValue> =
unsafe { BumpBox::from_raw(self.bump().alloc(value)) };
AttributeValue::Any(RefCell::new(Some(boxed)))
pub fn any_value<T: AnyValue>(&'src self, value: T) -> AttributeValue {
AttributeValue::Any(RefCell::new(Some(Box::new(value))))
}
/// Mark this component as suspended and then return None

View file

@ -16,7 +16,9 @@ use crate::{
use futures_util::{pin_mut, StreamExt};
use rustc_hash::{FxHashMap, FxHashSet};
use slab::Slab;
use std::{any::Any, cell::Cell, collections::BTreeSet, future::Future, ptr::NonNull, rc::Rc, sync::Arc};
use std::{
any::Any, cell::Cell, collections::BTreeSet, future::Future, ptr::NonNull, rc::Rc, sync::Arc,
};
/// A virtual node system that progresses user events and diffs UI trees.
///
@ -186,7 +188,7 @@ pub struct VirtualDom {
pub(crate) templates: FxHashMap<TemplateId, FxHashMap<usize, Template<'static>>>,
// Every element is actually a dual reference - one to the template and the other to the dynamic node in that template
pub(crate) element_refs: Slab<Option<NonNull<VNode<'static>>>>,
pub(crate) element_refs: Slab<Option<NonNull<VNode>>>,
// The element ids that are used in the renderer
pub(crate) elements: Slab<Option<ElementRef>>,

View file

@ -9,7 +9,7 @@ macro_rules! impl_event {
$(
$( #[$attr] )*
#[inline]
pub fn $name<'a, E: crate::EventReturn<T>, T>(_cx: &'a ::dioxus_core::ScopeState, mut _f: impl FnMut(::dioxus_core::Event<$data>) -> E + 'a) -> ::dioxus_core::Attribute<'a> {
pub fn $name<'a, E: crate::EventReturn<T>, T>(_cx: &'a ::dioxus_core::ScopeState, mut _f: impl FnMut(::dioxus_core::Event<$data>) -> E + 'a) -> ::dioxus_core::Attribute {
::dioxus_core::Attribute::new(
stringify!($name),
_cx.listener(move |e: ::dioxus_core::Event<$data>| {

View file

@ -151,7 +151,7 @@ impl_event! {
pub fn ondoubleclick<'a, E: crate::EventReturn<T>, T>(
_cx: &'a ::dioxus_core::ScopeState,
mut _f: impl FnMut(::dioxus_core::Event<MouseData>) -> E + 'a,
) -> ::dioxus_core::Attribute<'a> {
) -> ::dioxus_core::Attribute {
::dioxus_core::Attribute::new(
"ondblclick",
_cx.listener(move |e: ::dioxus_core::Event<MouseData>| {