Browse Source

use BumpBox<Any> and FromAnyValue instead of sync bounds

Evan Almloff 2 years ago
parent
commit
51dba50c04

+ 1 - 3
packages/core/Cargo.toml

@@ -44,6 +44,4 @@ pretty_assertions = "1.3.0"
 
 
 [features]
 [features]
 default = []
 default = []
-serialize = ["serde"]
-# Adds a Sync + Send bound on any values in attributes
-sync_attributes = []
+serialize = ["serde"]

+ 3 - 3
packages/core/src/arena.rs

@@ -158,10 +158,10 @@ impl VirtualDom {
             let listener = unsafe { &*listener };
             let listener = unsafe { &*listener };
             match &listener.value {
             match &listener.value {
                 AttributeValue::Listener(l) => {
                 AttributeValue::Listener(l) => {
-                    _ = l.0.take();
+                    _ = l.take();
                 }
                 }
-                AttributeValue::Any(a) => {
-                    _ = a.take();
+                AttributeValue::Any(_) => {
+                    todo!()
                 }
                 }
                 _ => (),
                 _ => (),
             }
             }

+ 4 - 3
packages/core/src/create.rs

@@ -1,5 +1,5 @@
 use crate::any_props::AnyProps;
 use crate::any_props::AnyProps;
-use crate::innerlude::{VComponent, VPlaceholder, VText};
+use crate::innerlude::{BorrowedAttributeValue, VComponent, VPlaceholder, 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;
@@ -285,7 +285,7 @@ impl<'b> VirtualDom {
         }
         }
     }
     }
 
 
-    fn write_attribute(&mut self, attribute: &crate::Attribute, id: ElementId) {
+    fn write_attribute(&mut self, attribute: &'b crate::Attribute<'b>, id: ElementId) {
         // Make sure we set the attribute's associated id
         // Make sure we set the attribute's associated id
         attribute.mounted_element.set(id);
         attribute.mounted_element.set(id);
 
 
@@ -302,7 +302,8 @@ impl<'b> VirtualDom {
             }
             }
             _ => {
             _ => {
                 // Safety: we promise not to re-alias this text later on after committing it to the mutation
                 // Safety: we promise not to re-alias this text later on after committing it to the mutation
-                let unbounded_value = unsafe { std::mem::transmute(attribute.value.clone()) };
+                let value: BorrowedAttributeValue<'b> = (&attribute.value).into();
+                let unbounded_value = unsafe { std::mem::transmute(value) };
 
 
                 self.mutations.push(SetAttribute {
                 self.mutations.push(SetAttribute {
                     name: unbounded_name,
                     name: unbounded_name,

+ 4 - 4
packages/core/src/diff.rs

@@ -1,7 +1,7 @@
 use crate::{
 use crate::{
     any_props::AnyProps,
     any_props::AnyProps,
     arena::ElementId,
     arena::ElementId,
-    innerlude::{DirtyScope, VComponent, VPlaceholder, VText},
+    innerlude::{BorrowedAttributeValue, DirtyScope, VComponent, VPlaceholder, VText},
     mutations::Mutation,
     mutations::Mutation,
     nodes::RenderReturn,
     nodes::RenderReturn,
     nodes::{DynamicNode, VNode},
     nodes::{DynamicNode, VNode},
@@ -150,10 +150,10 @@ impl<'b> VirtualDom {
         };
         };
     }
     }
 
 
-    fn update_attribute(&mut self, right_attr: &Attribute, left_attr: &Attribute) {
-        // todo: add more types of attribute values
+    fn update_attribute(&mut self, right_attr: &'b Attribute<'b>, left_attr: &'b Attribute) {
         let name = unsafe { std::mem::transmute(left_attr.name) };
         let name = unsafe { std::mem::transmute(left_attr.name) };
-        let value = unsafe { std::mem::transmute(right_attr.value.clone()) };
+        let value: BorrowedAttributeValue<'b> = (&right_attr.value).into();
+        let value = unsafe { std::mem::transmute(value) };
         self.mutations.push(Mutation::SetAttribute {
         self.mutations.push(Mutation::SetAttribute {
             id: left_attr.mounted_element.get(),
             id: left_attr.mounted_element.get(),
             ns: right_attr.namespace,
             ns: right_attr.namespace,

+ 4 - 4
packages/core/src/lib.rs

@@ -70,10 +70,10 @@ pub(crate) mod innerlude {
 }
 }
 
 
 pub use crate::innerlude::{
 pub use crate::innerlude::{
-    fc_to_builder, AnyValueContainer, 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,
+    fc_to_builder, AnyValue, Attribute, AttributeValue, BorrowedAttributeValue, 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
 /// The purpose of this module is to alleviate imports of many common types

+ 2 - 2
packages/core/src/mutations.rs

@@ -1,6 +1,6 @@
 use rustc_hash::FxHashSet;
 use rustc_hash::FxHashSet;
 
 
-use crate::{arena::ElementId, AttributeValue, ScopeId, Template};
+use crate::{arena::ElementId, innerlude::BorrowedAttributeValue, ScopeId, Template};
 
 
 /// A container for all the relevant steps to modify the Real DOM
 /// A container for all the relevant steps to modify the Real DOM
 ///
 ///
@@ -208,7 +208,7 @@ pub enum Mutation<'a> {
         name: &'a str,
         name: &'a str,
 
 
         /// The value of the attribute.
         /// The value of the attribute.
-        value: AttributeValue<'a>,
+        value: BorrowedAttributeValue<'a>,
 
 
         /// The ID of the node to set the attribute of.
         /// The ID of the node to set the attribute of.
         id: ElementId,
         id: ElementId,

+ 78 - 113
packages/core/src/nodes.rs

@@ -6,9 +6,8 @@ use bumpalo::Bump;
 use std::{
 use std::{
     any::{Any, TypeId},
     any::{Any, TypeId},
     cell::{Cell, RefCell, UnsafeCell},
     cell::{Cell, RefCell, UnsafeCell},
-    fmt::Arguments,
+    fmt::{Arguments, Debug},
     future::Future,
     future::Future,
-    ops::Deref,
 };
 };
 
 
 pub type TemplateId = &'static str;
 pub type TemplateId = &'static str;
@@ -200,7 +199,7 @@ impl<'a> VNode<'a> {
     pub(crate) fn clear_listeners(&self) {
     pub(crate) fn clear_listeners(&self) {
         for attr in self.dynamic_attrs {
         for attr in self.dynamic_attrs {
             if let AttributeValue::Listener(l) = &attr.value {
             if let AttributeValue::Listener(l) = &attr.value {
-                l.0.borrow_mut().take();
+                l.borrow_mut().take();
             }
             }
         }
         }
     }
     }
@@ -507,9 +506,6 @@ pub struct Attribute<'a> {
 ///
 ///
 /// These are built-in to be faster during the diffing process. To use a custom value, use the [`AttributeValue::Any`]
 /// These are built-in to be faster during the diffing process. To use a custom value, use the [`AttributeValue::Any`]
 /// variant.
 /// variant.
-#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
-#[cfg_attr(feature = "serialize", serde(untagged))]
-#[derive(Clone)]
 pub enum AttributeValue<'a> {
 pub enum AttributeValue<'a> {
     /// Text attribute
     /// Text attribute
     Text(&'a str),
     Text(&'a str),
@@ -524,107 +520,105 @@ pub enum AttributeValue<'a> {
     Bool(bool),
     Bool(bool),
 
 
     /// A listener, like "onclick"
     /// A listener, like "onclick"
-    Listener(ListenerCb<'a>),
+    Listener(RefCell<Option<ListenerCb<'a>>>),
 
 
     /// An arbitrary value that implements PartialEq and is static
     /// An arbitrary value that implements PartialEq and is static
-    Any(RefCell<Option<AnyValueContainer>>),
+    Any(BumpBox<'a, dyn AnyValue>),
 
 
     /// A "none" value, resulting in the removal of an attribute from the dom
     /// A "none" value, resulting in the removal of an attribute from the dom
     None,
     None,
 }
 }
 
 
-pub type ListenerCbInner<'a> = RefCell<Option<BumpBox<'a, dyn FnMut(Event<dyn Any>) + 'a>>>;
-pub struct ListenerCb<'a>(pub ListenerCbInner<'a>);
+pub type ListenerCb<'a> = BumpBox<'a, dyn FnMut(Event<dyn Any>) + 'a>;
 
 
-impl Clone for ListenerCb<'_> {
-    fn clone(&self) -> Self {
-        panic!("ListenerCb cannot be cloned")
-    }
-}
+/// Any of the built-in values that the Dioxus VirtualDom supports as dynamic attributes on elements that are borrowed
+///
+/// These varients are used to communicate what the value of an attribute is that needs to be updated
+#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serialize", serde(untagged))]
+pub enum BorrowedAttributeValue<'a> {
+    /// Text attribute
+    Text(&'a str),
 
 
-#[cfg(feature = "serialize")]
-impl<'a> serde::Serialize for ListenerCb<'a> {
-    fn serialize<S>(&self, _: S) -> Result<S::Ok, S::Error>
-    where
-        S: serde::Serializer,
-    {
-        panic!("ListenerCb cannot be serialized")
-    }
-}
+    /// A float
+    Float(f64),
 
 
-#[cfg(feature = "serialize")]
-impl<'de, 'a> serde::Deserialize<'de> for ListenerCb<'a> {
-    fn deserialize<D>(_: D) -> Result<Self, D::Error>
-    where
-        D: serde::Deserializer<'de>,
-    {
-        panic!("ListenerCb cannot be deserialized")
-    }
-}
+    /// Signed integer
+    Int(i64),
 
 
-#[derive(Clone)]
-#[cfg(not(feature = "sync_attributes"))]
-/// A boxed value that implements PartialEq, and Any
-pub struct AnyValueContainer(pub std::rc::Rc<dyn AnyValue>);
+    /// Boolean
+    Bool(bool),
 
 
-#[derive(Clone)]
-#[cfg(feature = "sync_attributes")]
-/// A boxed value that implements PartialEq, Any, Sync, and Send
-pub struct AnyValueContainer(pub std::sync::Arc<dyn AnyValue>);
+    /// An arbitrary value that implements PartialEq and is static
+    #[cfg_attr(
+        feature = "serialize",
+        serde(
+            deserialize_with = "deserialize_any_value",
+            serialize_with = "serialize_any_value"
+        )
+    )]
+    Any(&'a dyn AnyValue),
 
 
-impl PartialEq for AnyValueContainer {
-    fn eq(&self, other: &Self) -> bool {
-        self.0.any_cmp(other.0.as_ref())
-    }
+    /// A "none" value, resulting in the removal of an attribute from the dom
+    None,
 }
 }
 
 
-impl AnyValueContainer {
-    /// Create a new AnyValueContainer containing the specified data.
-    pub fn new<T: AnyValue + 'static>(value: T) -> Self {
-        #[cfg(feature = "sync_attributes")]
-        return Self(std::sync::Arc::new(value));
-        #[cfg(not(feature = "sync_attributes"))]
-        return Self(std::rc::Rc::new(value));
-    }
-
-    /// Returns a reference to the inner value.
-    pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
-        self.0.deref().as_any().downcast_ref()
+impl<'a> From<&'a AttributeValue<'a>> for BorrowedAttributeValue<'a> {
+    fn from(value: &'a AttributeValue<'a>) -> Self {
+        match value {
+            AttributeValue::Text(value) => BorrowedAttributeValue::Text(value),
+            AttributeValue::Float(value) => BorrowedAttributeValue::Float(*value),
+            AttributeValue::Int(value) => BorrowedAttributeValue::Int(*value),
+            AttributeValue::Bool(value) => BorrowedAttributeValue::Bool(*value),
+            AttributeValue::Listener(_) => {
+                panic!("A listener cannot be turned into a borrowed value")
+            }
+            AttributeValue::Any(value) => BorrowedAttributeValue::Any(&**value),
+            AttributeValue::None => BorrowedAttributeValue::None,
+        }
     }
     }
+}
 
 
-    /// Checks if the type of the inner value is 'T'.
-    pub fn is<T: Any>(&self) -> bool {
-        self.0.deref().as_any().is::<T>()
+impl Debug for BorrowedAttributeValue<'_> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Self::Text(arg0) => f.debug_tuple("Text").field(arg0).finish(),
+            Self::Float(arg0) => f.debug_tuple("Float").field(arg0).finish(),
+            Self::Int(arg0) => f.debug_tuple("Int").field(arg0).finish(),
+            Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(),
+            Self::Any(_) => f.debug_tuple("Any").field(&"...").finish(),
+            Self::None => write!(f, "None"),
+        }
     }
     }
 }
 }
 
 
-#[test]
-fn test_any_value_rc() {
-    let a = AnyValueContainer::new(1i32);
-    assert_eq!(a.downcast_ref::<i32>(), Some(&1i32));
-    assert_eq!(a.downcast_ref::<i64>(), None);
-    assert!(a.is::<i32>());
-    assert!(!a.is::<i64>());
+impl PartialEq for BorrowedAttributeValue<'_> {
+    fn eq(&self, other: &Self) -> bool {
+        match (self, other) {
+            (Self::Text(l0), Self::Text(r0)) => l0 == r0,
+            (Self::Float(l0), Self::Float(r0)) => l0 == r0,
+            (Self::Int(l0), Self::Int(r0)) => l0 == r0,
+            (Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
+            (Self::Any(l0), Self::Any(r0)) => l0.any_cmp(&**r0),
+            _ => core::mem::discriminant(self) == core::mem::discriminant(other),
+        }
+    }
 }
 }
 
 
 #[cfg(feature = "serialize")]
 #[cfg(feature = "serialize")]
-impl serde::Serialize for AnyValueContainer {
-    fn serialize<S>(&self, _: S) -> Result<S::Ok, S::Error>
-    where
-        S: serde::Serializer,
-    {
-        panic!("AnyValueBox cannot be serialized")
-    }
+fn serialize_any_value<S>(_: &&dyn AnyValue, _: S) -> Result<S::Ok, S::Error>
+where
+    S: serde::Serializer,
+{
+    panic!("Any cannot be serialized")
 }
 }
 
 
 #[cfg(feature = "serialize")]
 #[cfg(feature = "serialize")]
-impl<'de> serde::Deserialize<'de> for AnyValueContainer {
-    fn deserialize<D>(_: D) -> Result<Self, D::Error>
-    where
-        D: serde::Deserializer<'de>,
-    {
-        panic!("AnyValueBox cannot be deserialized")
-    }
+fn deserialize_any_value<'de, 'a, D>(_: D) -> Result<&'a dyn AnyValue, D::Error>
+where
+    D: serde::Deserializer<'de>,
+{
+    panic!("Any cannot be deserialized")
 }
 }
 
 
 impl<'a> std::fmt::Debug for AttributeValue<'a> {
 impl<'a> std::fmt::Debug for AttributeValue<'a> {
@@ -649,41 +643,13 @@ impl<'a> PartialEq for AttributeValue<'a> {
             (Self::Int(l0), Self::Int(r0)) => l0 == r0,
             (Self::Int(l0), Self::Int(r0)) => l0 == r0,
             (Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
             (Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
             (Self::Listener(_), Self::Listener(_)) => true,
             (Self::Listener(_), Self::Listener(_)) => true,
-            (Self::Any(l0), Self::Any(r0)) => l0 == r0,
+            (Self::Any(l0), Self::Any(r0)) => l0.any_cmp(&**r0),
             _ => false,
             _ => false,
         }
         }
     }
     }
 }
 }
 
 
-// Some renderers need attributes to be sync and send. The rest of the attributes are already sync and send, but there is no way to force Any values to be sync and send on the renderer side.
-// The sync_attributes flag restricts any valuse to be sync and send.
-#[doc(hidden)]
-#[cfg(feature = "sync_attributes")]
-pub trait AnyValue: Sync + Send + 'static {
-    fn any_cmp(&self, other: &dyn AnyValue) -> bool;
-    fn as_any(&self) -> &dyn Any;
-    fn type_id(&self) -> TypeId {
-        self.as_any().type_id()
-    }
-}
-
-#[cfg(feature = "sync_attributes")]
-impl<T: Any + PartialEq + Send + Sync + 'static> AnyValue for T {
-    fn any_cmp(&self, other: &dyn AnyValue) -> bool {
-        if let Some(other) = other.as_any().downcast_ref() {
-            self == other
-        } else {
-            false
-        }
-    }
-
-    fn as_any(&self) -> &dyn Any {
-        self
-    }
-}
-
 #[doc(hidden)]
 #[doc(hidden)]
-#[cfg(not(feature = "sync_attributes"))]
 pub trait AnyValue: 'static {
 pub trait AnyValue: 'static {
     fn any_cmp(&self, other: &dyn AnyValue) -> bool;
     fn any_cmp(&self, other: &dyn AnyValue) -> bool;
     fn as_any(&self) -> &dyn Any;
     fn as_any(&self) -> &dyn Any;
@@ -692,7 +658,6 @@ pub trait AnyValue: 'static {
     }
     }
 }
 }
 
 
-#[cfg(not(feature = "sync_attributes"))]
 impl<T: Any + PartialEq + 'static> AnyValue for T {
 impl<T: Any + PartialEq + 'static> AnyValue for T {
     fn any_cmp(&self, other: &dyn AnyValue) -> bool {
     fn any_cmp(&self, other: &dyn AnyValue) -> bool {
         if let Some(other) = other.as_any().downcast_ref() {
         if let Some(other) = other.as_any().downcast_ref() {
@@ -903,8 +868,8 @@ impl<'a> IntoAttributeValue<'a> for Arguments<'_> {
     }
     }
 }
 }
 
 
-impl<'a> IntoAttributeValue<'a> for AnyValueContainer {
+impl<'a> IntoAttributeValue<'a> for BumpBox<'a, dyn AnyValue> {
     fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
     fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
-        AttributeValue::Any(RefCell::new(Some(self)))
+        AttributeValue::Any(self)
     }
     }
 }
 }

+ 2 - 2
packages/core/src/scopes.rs

@@ -4,7 +4,7 @@ use crate::{
     arena::ElementId,
     arena::ElementId,
     bump_frame::BumpFrame,
     bump_frame::BumpFrame,
     innerlude::{DynamicNode, EventHandler, VComponent, VText},
     innerlude::{DynamicNode, EventHandler, VComponent, VText},
-    innerlude::{ErrorBoundary, ListenerCb, Scheduler, SchedulerMsg},
+    innerlude::{ErrorBoundary, Scheduler, SchedulerMsg},
     lazynodes::LazyNodes,
     lazynodes::LazyNodes,
     nodes::{ComponentReturn, IntoAttributeValue, IntoDynNode, RenderReturn},
     nodes::{ComponentReturn, IntoAttributeValue, IntoDynNode, RenderReturn},
     Attribute, AttributeValue, Element, Event, Properties, TaskId,
     Attribute, AttributeValue, Element, Event, Properties, TaskId,
@@ -510,7 +510,7 @@ impl<'src> ScopeState {
             }))
             }))
         };
         };
 
 
-        AttributeValue::Listener(ListenerCb(RefCell::new(Some(boxed))))
+        AttributeValue::Listener(RefCell::new(Some(boxed)))
     }
     }
 
 
     /// Inject an error into the nearest error boundary and quit rendering
     /// Inject an error into the nearest error boundary and quit rendering

+ 1 - 1
packages/core/src/virtual_dom.rs

@@ -384,7 +384,7 @@ impl VirtualDom {
             // We check the bubble state between each call to see if the event has been stopped from bubbling
             // We check the bubble state between each call to see if the event has been stopped from bubbling
             for listener in listeners.drain(..).rev() {
             for listener in listeners.drain(..).rev() {
                 if let AttributeValue::Listener(listener) = listener {
                 if let AttributeValue::Listener(listener) = listener {
-                    if let Some(cb) = listener.0.borrow_mut().as_deref_mut() {
+                    if let Some(cb) = listener.borrow_mut().as_deref_mut() {
                         cb(uievent.clone());
                         cb(uievent.clone());
                     }
                     }
 
 

+ 10 - 5
packages/core/tests/attr_cleanup.rs

@@ -5,7 +5,7 @@
 use bumpalo::Bump;
 use bumpalo::Bump;
 use dioxus::core::{ElementId, Mutation::*};
 use dioxus::core::{ElementId, Mutation::*};
 use dioxus::prelude::*;
 use dioxus::prelude::*;
-use dioxus_core::AttributeValue;
+use dioxus_core::{AttributeValue, BorrowedAttributeValue};
 
 
 #[test]
 #[test]
 fn attrs_cycle() {
 fn attrs_cycle() {
@@ -42,11 +42,16 @@ fn attrs_cycle() {
             AssignId { path: &[0,], id: ElementId(3,) },
             AssignId { path: &[0,], id: ElementId(3,) },
             SetAttribute {
             SetAttribute {
                 name: "class",
                 name: "class",
-                value: "1".into_value(&bump),
+                value: (&*bump.alloc("1".into_value(&bump))).into(),
+                id: ElementId(3,),
+                ns: None
+            },
+            SetAttribute {
+                name: "id",
+                value: (&*bump.alloc("1".into_value(&bump))).into(),
                 id: ElementId(3,),
                 id: ElementId(3,),
                 ns: None
                 ns: None
             },
             },
-            SetAttribute { name: "id", value: "1".into_value(&bump), id: ElementId(3,), ns: None },
             ReplaceWith { id: ElementId(1,), m: 1 },
             ReplaceWith { id: ElementId(1,), m: 1 },
         ]
         ]
     );
     );
@@ -68,13 +73,13 @@ fn attrs_cycle() {
             AssignId { path: &[0], id: ElementId(3) },
             AssignId { path: &[0], id: ElementId(3) },
             SetAttribute {
             SetAttribute {
                 name: "class",
                 name: "class",
-                value: AttributeValue::Text("3"),
+                value: BorrowedAttributeValue::Text("3"),
                 id: ElementId(3),
                 id: ElementId(3),
                 ns: None
                 ns: None
             },
             },
             SetAttribute {
             SetAttribute {
                 name: "id",
                 name: "id",
-                value: AttributeValue::Text("3"),
+                value: BorrowedAttributeValue::Text("3"),
                 id: ElementId(3),
                 id: ElementId(3),
                 ns: None
                 ns: None
             },
             },

+ 2 - 1
packages/core/tests/boolattrs.rs

@@ -6,13 +6,14 @@ use dioxus::prelude::*;
 fn bool_test() {
 fn bool_test() {
     let mut app = VirtualDom::new(|cx| cx.render(rsx!(div { hidden: false })));
     let mut app = VirtualDom::new(|cx| cx.render(rsx!(div { hidden: false })));
     let bump = Bump::new();
     let bump = Bump::new();
+
     assert_eq!(
     assert_eq!(
         app.rebuild().santize().edits,
         app.rebuild().santize().edits,
         [
         [
             LoadTemplate { name: "template", index: 0, id: ElementId(1) },
             LoadTemplate { name: "template", index: 0, id: ElementId(1) },
             SetAttribute {
             SetAttribute {
                 name: "hidden",
                 name: "hidden",
-                value: false.into_value(&bump),
+                value: (&*bump.alloc(false.into_value(&bump))).into(),
                 id: ElementId(1,),
                 id: ElementId(1,),
                 ns: None
                 ns: None
             },
             },

+ 1 - 1
packages/core/tests/kitchen_sink.rs

@@ -36,7 +36,7 @@ fn dual_stream() {
             LoadTemplate { name: "template", index: 0, id: ElementId(1) },
             LoadTemplate { name: "template", index: 0, id: ElementId(1) },
             SetAttribute {
             SetAttribute {
                 name: "class",
                 name: "class",
-                value: "123".into_value(&bump),
+                value: (&*bump.alloc("123".into_value(&bump))).into(),
                 id: ElementId(1),
                 id: ElementId(1),
                 ns: None,
                 ns: None,
             },
             },

+ 0 - 1
packages/native-core-macro/tests/update_state.rs

@@ -43,7 +43,6 @@ impl ParentDepState for ParentDepCallCounter {
     const NODE_MASK: NodeMask = NodeMask::ALL;
     const NODE_MASK: NodeMask = NodeMask::ALL;
     fn reduce(&mut self, _node: NodeView, _parent: Option<(&Self,)>, _ctx: &Self::Ctx) -> bool {
     fn reduce(&mut self, _node: NodeView, _parent: Option<(&Self,)>, _ctx: &Self::Ctx) -> bool {
         self.0 += 1;
         self.0 += 1;
-        println!("ParentDepCallCounter::reduce on {:?}\n{}", _node, self.0);
         true
         true
     }
     }
 }
 }

+ 0 - 5
packages/native-core/Cargo.toml

@@ -27,8 +27,3 @@ dashmap = "5.4.0"
 [dev-dependencies]
 [dev-dependencies]
 rand = "0.8.5"
 rand = "0.8.5"
 dioxus = { path = "../dioxus", version = "^0.2.1" }
 dioxus = { path = "../dioxus", version = "^0.2.1" }
-
-[features]
-default = ["parallel"]
-# This makes core only accept sync and send any values for attributes to allow for parallel resolution of passes.
-parallel = ["dioxus-core/sync_attributes"]

+ 39 - 25
packages/native-core/src/node.rs

@@ -1,44 +1,44 @@
 use crate::{state::State, tree::NodeId};
 use crate::{state::State, tree::NodeId};
-use dioxus_core::{AnyValueContainer, AttributeValue, ElementId};
+use dioxus_core::{AnyValue, BorrowedAttributeValue, ElementId};
 use rustc_hash::{FxHashMap, FxHashSet};
 use rustc_hash::{FxHashMap, FxHashSet};
 use std::fmt::Debug;
 use std::fmt::Debug;
 
 
 /// The node is stored client side and stores only basic data about the node.
 /// The node is stored client side and stores only basic data about the node.
 #[derive(Debug, Clone)]
 #[derive(Debug, Clone)]
-pub struct Node<S: State> {
+pub struct Node<S: State, V: FromAnyValue = ()> {
     /// The transformed state of the node.
     /// The transformed state of the node.
     pub state: S,
     pub state: S,
     /// The raw data for the node
     /// The raw data for the node
-    pub node_data: NodeData,
+    pub node_data: NodeData<V>,
 }
 }
 
 
 #[derive(Debug, Clone)]
 #[derive(Debug, Clone)]
-pub struct NodeData {
+pub struct NodeData<V: FromAnyValue = ()> {
     /// The id of the node
     /// The id of the node
     pub node_id: NodeId,
     pub node_id: NodeId,
     /// The id of the node in the vdom.
     /// The id of the node in the vdom.
     pub element_id: Option<ElementId>,
     pub element_id: Option<ElementId>,
     /// Additional inforation specific to the node type
     /// Additional inforation specific to the node type
-    pub node_type: NodeType,
+    pub node_type: NodeType<V>,
 }
 }
 
 
 /// A type of node with data specific to the node type. The types are a subset of the [VNode] types.
 /// A type of node with data specific to the node type. The types are a subset of the [VNode] types.
 #[derive(Debug, Clone)]
 #[derive(Debug, Clone)]
-pub enum NodeType {
+pub enum NodeType<V: FromAnyValue = ()> {
     Text {
     Text {
         text: String,
         text: String,
     },
     },
     Element {
     Element {
         tag: String,
         tag: String,
         namespace: Option<String>,
         namespace: Option<String>,
-        attributes: FxHashMap<OwnedAttributeDiscription, OwnedAttributeValue>,
+        attributes: FxHashMap<OwnedAttributeDiscription, OwnedAttributeValue<V>>,
         listeners: FxHashSet<String>,
         listeners: FxHashSet<String>,
     },
     },
     Placeholder,
     Placeholder,
 }
 }
 
 
-impl<S: State> Node<S> {
-    pub(crate) fn new(node_type: NodeType) -> Self {
+impl<S: State, V: FromAnyValue> Node<S, V> {
+    pub(crate) fn new(node_type: NodeType<V>) -> Self {
         Node {
         Node {
             state: S::default(),
             state: S::default(),
             node_data: NodeData {
             node_data: NodeData {
@@ -65,52 +65,59 @@ pub struct OwnedAttributeDiscription {
 /// An attribute on a DOM node, such as `id="my-thing"` or
 /// An attribute on a DOM node, such as `id="my-thing"` or
 /// `href="https://example.com"`.
 /// `href="https://example.com"`.
 #[derive(Clone, Copy, Debug)]
 #[derive(Clone, Copy, Debug)]
-pub struct OwnedAttributeView<'a> {
+pub struct OwnedAttributeView<'a, V: FromAnyValue = ()> {
     /// The discription of the attribute.
     /// The discription of the attribute.
     pub attribute: &'a OwnedAttributeDiscription,
     pub attribute: &'a OwnedAttributeDiscription,
 
 
     /// The value of the attribute.
     /// The value of the attribute.
-    pub value: &'a OwnedAttributeValue,
+    pub value: &'a OwnedAttributeValue<V>,
 }
 }
 
 
 #[derive(Clone)]
 #[derive(Clone)]
-pub enum OwnedAttributeValue {
+pub enum OwnedAttributeValue<V: FromAnyValue = ()> {
     Text(String),
     Text(String),
     Float(f64),
     Float(f64),
     Int(i64),
     Int(i64),
     Bool(bool),
     Bool(bool),
-    Any(AnyValueContainer),
+    Custom(V),
     None,
     None,
 }
 }
 
 
-impl Debug for OwnedAttributeValue {
+pub trait FromAnyValue {
+    fn from_any_value(value: &dyn AnyValue) -> Self;
+}
+
+impl FromAnyValue for () {
+    fn from_any_value(_: &dyn AnyValue) -> Self {}
+}
+
+impl<V: FromAnyValue> Debug for OwnedAttributeValue<V> {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         match self {
         match self {
             Self::Text(arg0) => f.debug_tuple("Text").field(arg0).finish(),
             Self::Text(arg0) => f.debug_tuple("Text").field(arg0).finish(),
             Self::Float(arg0) => f.debug_tuple("Float").field(arg0).finish(),
             Self::Float(arg0) => f.debug_tuple("Float").field(arg0).finish(),
             Self::Int(arg0) => f.debug_tuple("Int").field(arg0).finish(),
             Self::Int(arg0) => f.debug_tuple("Int").field(arg0).finish(),
             Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(),
             Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(),
-            Self::Any(_) => f.debug_tuple("Any").finish(),
+            Self::Custom(_) => f.debug_tuple("Any").finish(),
             Self::None => write!(f, "None"),
             Self::None => write!(f, "None"),
         }
         }
     }
     }
 }
 }
 
 
-impl From<AttributeValue<'_>> for OwnedAttributeValue {
-    fn from(value: AttributeValue<'_>) -> Self {
+impl<V: FromAnyValue> From<BorrowedAttributeValue<'_>> for OwnedAttributeValue<V> {
+    fn from(value: BorrowedAttributeValue<'_>) -> Self {
         match value {
         match value {
-            AttributeValue::Text(text) => Self::Text(text.to_string()),
-            AttributeValue::Float(float) => Self::Float(float),
-            AttributeValue::Int(int) => Self::Int(int),
-            AttributeValue::Bool(bool) => Self::Bool(bool),
-            AttributeValue::Any(any) => Self::Any(any.borrow().as_ref().unwrap().clone()),
-            AttributeValue::None => Self::None,
-            _ => Self::None,
+            BorrowedAttributeValue::Text(text) => Self::Text(text.to_string()),
+            BorrowedAttributeValue::Float(float) => Self::Float(float),
+            BorrowedAttributeValue::Int(int) => Self::Int(int),
+            BorrowedAttributeValue::Bool(bool) => Self::Bool(bool),
+            BorrowedAttributeValue::Any(any) => Self::Custom(V::from_any_value(any)),
+            BorrowedAttributeValue::None => Self::None,
         }
         }
     }
     }
 }
 }
 
 
-impl OwnedAttributeValue {
+impl<V: FromAnyValue> OwnedAttributeValue<V> {
     pub fn as_text(&self) -> Option<&str> {
     pub fn as_text(&self) -> Option<&str> {
         match self {
         match self {
             OwnedAttributeValue::Text(text) => Some(text),
             OwnedAttributeValue::Text(text) => Some(text),
@@ -145,4 +152,11 @@ impl OwnedAttributeValue {
             _ => None,
             _ => None,
         }
         }
     }
     }
+
+    pub fn as_custom(&self) -> Option<&V> {
+        match self {
+            OwnedAttributeValue::Custom(custom) => Some(custom),
+            _ => None,
+        }
+    }
 }
 }

+ 8 - 6
packages/native-core/src/node_ref.rs

@@ -1,21 +1,21 @@
 use dioxus_core::ElementId;
 use dioxus_core::ElementId;
 
 
 use crate::{
 use crate::{
-    node::{NodeData, NodeType, OwnedAttributeView},
+    node::{FromAnyValue, NodeData, NodeType, OwnedAttributeView},
     state::union_ordered_iter,
     state::union_ordered_iter,
     RealNodeId,
     RealNodeId,
 };
 };
 
 
 /// A view into a [VNode] with limited access.
 /// A view into a [VNode] with limited access.
 #[derive(Debug)]
 #[derive(Debug)]
-pub struct NodeView<'a> {
-    inner: &'a NodeData,
+pub struct NodeView<'a, V: FromAnyValue = ()> {
+    inner: &'a NodeData<V>,
     mask: NodeMask,
     mask: NodeMask,
 }
 }
 
 
-impl<'a> NodeView<'a> {
+impl<'a, V: FromAnyValue> NodeView<'a, V> {
     /// Create a new NodeView from a VNode, and mask.
     /// Create a new NodeView from a VNode, and mask.
-    pub fn new(node: &'a NodeData, view: NodeMask) -> Self {
+    pub fn new(node: &'a NodeData<V>, view: NodeMask) -> Self {
         Self {
         Self {
             inner: node,
             inner: node,
             mask: view,
             mask: view,
@@ -55,7 +55,9 @@ impl<'a> NodeView<'a> {
     }
     }
 
 
     /// Get any attributes that are enabled in the mask
     /// Get any attributes that are enabled in the mask
-    pub fn attributes<'b>(&'b self) -> Option<impl Iterator<Item = OwnedAttributeView<'a>> + 'b> {
+    pub fn attributes<'b>(
+        &'b self,
+    ) -> Option<impl Iterator<Item = OwnedAttributeView<'a, V>> + 'b> {
         match &self.inner.node_type {
         match &self.inner.node_type {
             NodeType::Element { attributes, .. } => Some(
             NodeType::Element { attributes, .. } => Some(
                 attributes
                 attributes

+ 7 - 7
packages/native-core/src/state.rs

@@ -1,6 +1,6 @@
 use std::cmp::Ordering;
 use std::cmp::Ordering;
 
 
-use crate::node::Node;
+use crate::node::{FromAnyValue, Node};
 use crate::node_ref::{NodeMask, NodeView};
 use crate::node_ref::{NodeMask, NodeView};
 use crate::passes::{resolve_passes, resolve_passes_single_threaded, AnyPass, DirtyNodeStates};
 use crate::passes::{resolve_passes, resolve_passes_single_threaded, AnyPass, DirtyNodeStates};
 use crate::tree::TreeView;
 use crate::tree::TreeView;
@@ -76,7 +76,7 @@ pub(crate) fn union_ordered_iter<'a>(
 ///     }
 ///     }
 /// }
 /// }
 /// ```
 /// ```
-pub trait ChildDepState {
+pub trait ChildDepState<V: FromAnyValue = ()> {
     /// The context is passed to the [ChildDepState::reduce] when it is resolved.
     /// The context is passed to the [ChildDepState::reduce] when it is resolved.
     type Ctx;
     type Ctx;
     /// A state from each child node that this node depends on. Typically this is Self, but it could be any state that is within the state tree.
     /// A state from each child node that this node depends on. Typically this is Self, but it could be any state that is within the state tree.
@@ -87,7 +87,7 @@ pub trait ChildDepState {
     /// Resolve the state current node's state from the state of the children, the state of the node, and some external context.
     /// Resolve the state current node's state from the state of the children, the state of the node, and some external context.
     fn reduce<'a>(
     fn reduce<'a>(
         &mut self,
         &mut self,
-        node: NodeView<'a>,
+        node: NodeView<'a, V>,
         children: impl Iterator<Item = <Self::DepState as ElementBorrowable>::ElementBorrowed<'a>>,
         children: impl Iterator<Item = <Self::DepState as ElementBorrowable>::ElementBorrowed<'a>>,
         ctx: &Self::Ctx,
         ctx: &Self::Ctx,
     ) -> bool
     ) -> bool
@@ -138,7 +138,7 @@ pub trait ChildDepState {
 ///     }
 ///     }
 /// }
 /// }
 /// ```
 /// ```
-pub trait ParentDepState {
+pub trait ParentDepState<V: FromAnyValue = ()> {
     /// The context is passed to the [ParentDepState::reduce] when it is resolved.
     /// The context is passed to the [ParentDepState::reduce] when it is resolved.
     type Ctx;
     type Ctx;
     /// A state from from the parent node that this node depends on. Typically this is Self, but it could be any state that is within the state tree.
     /// A state from from the parent node that this node depends on. Typically this is Self, but it could be any state that is within the state tree.
@@ -149,7 +149,7 @@ pub trait ParentDepState {
     /// Resolve the state current node's state from the state of the parent node, the state of the node, and some external context.
     /// Resolve the state current node's state from the state of the parent node, the state of the node, and some external context.
     fn reduce<'a>(
     fn reduce<'a>(
         &mut self,
         &mut self,
-        node: NodeView<'a>,
+        node: NodeView<'a, V>,
         parent: Option<<Self::DepState as ElementBorrowable>::ElementBorrowed<'a>>,
         parent: Option<<Self::DepState as ElementBorrowable>::ElementBorrowed<'a>>,
         ctx: &Self::Ctx,
         ctx: &Self::Ctx,
     ) -> bool;
     ) -> bool;
@@ -193,7 +193,7 @@ pub trait ParentDepState {
 ///     }
 ///     }
 /// }
 /// }
 /// ```
 /// ```
-pub trait NodeDepState {
+pub trait NodeDepState<V: FromAnyValue = ()> {
     /// Depstate must be a tuple containing any number of borrowed elements that are either [ChildDepState], [ParentDepState] or [NodeDepState].
     /// Depstate must be a tuple containing any number of borrowed elements that are either [ChildDepState], [ParentDepState] or [NodeDepState].
     type DepState: ElementBorrowable;
     type DepState: ElementBorrowable;
     /// The state passed to [NodeDepState::reduce] when it is resolved.
     /// The state passed to [NodeDepState::reduce] when it is resolved.
@@ -203,7 +203,7 @@ pub trait NodeDepState {
     /// Resolve the state current node's state from the state of the sibling states, the state of the node, and some external context.
     /// Resolve the state current node's state from the state of the sibling states, the state of the node, and some external context.
     fn reduce<'a>(
     fn reduce<'a>(
         &mut self,
         &mut self,
-        node: NodeView<'a>,
+        node: NodeView<'a, V>,
         node_state: <Self::DepState as ElementBorrowable>::ElementBorrowed<'a>,
         node_state: <Self::DepState as ElementBorrowable>::ElementBorrowed<'a>,
         ctx: &Self::Ctx,
         ctx: &Self::Ctx,
     ) -> bool;
     ) -> bool;

+ 0 - 1
packages/native-core/src/tree.rs

@@ -305,7 +305,6 @@ impl<T> TreeView<T> for Tree<T> {
     fn parent_child_mut(&mut self, id: NodeId) -> Option<(&mut T, Self::IteratorMut<'_>)> {
     fn parent_child_mut(&mut self, id: NodeId) -> Option<(&mut T, Self::IteratorMut<'_>)> {
         // Safety: No node will appear as a child twice
         // Safety: No node will appear as a child twice
         if let Some(children_ids) = self.children_ids(id) {
         if let Some(children_ids) = self.children_ids(id) {
-            println!("parent_child_mut: {:?}\n{:?}", id, children_ids);
             debug_assert!(!children_ids.iter().any(|child_id| *child_id == id));
             debug_assert!(!children_ids.iter().any(|child_id| *child_id == id));
             let mut borrowed = unsafe {
             let mut borrowed = unsafe {
                 let as_vec = children_ids.to_vec();
                 let as_vec = children_ids.to_vec();

+ 1 - 1
packages/native-core/src/utils/persistant_iterator.rs

@@ -229,7 +229,7 @@ impl PersistantElementIter {
 #[derive(Default, Clone, Debug)]
 #[derive(Default, Clone, Debug)]
 struct Empty {}
 struct Empty {}
 impl State for Empty {
 impl State for Empty {
-    const PASSES: &'static [crate::AnyPass<crate::node::Node<Self>>] = &[];
+    const PASSES: &'static [crate::AnyPass<crate::node::Node<Self, ()>>] = &[];
 
 
     const MASKS: &'static [crate::NodeMask] = &[];
     const MASKS: &'static [crate::NodeMask] = &[];
 }
 }

+ 10 - 5
packages/web/src/dom.rs

@@ -7,7 +7,9 @@
 //! - tests to ensure dyn_into works for various event types.
 //! - tests to ensure dyn_into works for various event types.
 //! - Partial delegation?>
 //! - Partial delegation?>
 
 
-use dioxus_core::{ElementId, Mutation, Template, TemplateAttribute, TemplateNode};
+use dioxus_core::{
+    BorrowedAttributeValue, ElementId, Mutation, Template, TemplateAttribute, TemplateNode,
+};
 use dioxus_html::{event_bubbles, CompositionData, FormData};
 use dioxus_html::{event_bubbles, CompositionData, FormData};
 use dioxus_interpreter_js::{save_template, Channel};
 use dioxus_interpreter_js::{save_template, Channel};
 use futures_channel::mpsc;
 use futures_channel::mpsc;
@@ -176,21 +178,24 @@ impl WebsysDom {
                     id,
                     id,
                     ns,
                     ns,
                 } => match value {
                 } => match value {
-                    dioxus_core::AttributeValue::Text(txt) => {
+                    BorrowedAttributeValue::Text(txt) => {
                         i.set_attribute(id.0 as u32, name, txt, ns.unwrap_or_default())
                         i.set_attribute(id.0 as u32, name, txt, ns.unwrap_or_default())
                     }
                     }
-                    dioxus_core::AttributeValue::Float(f) => {
+                    BorrowedAttributeValue::Float(f) => {
                         i.set_attribute(id.0 as u32, name, &f.to_string(), ns.unwrap_or_default())
                         i.set_attribute(id.0 as u32, name, &f.to_string(), ns.unwrap_or_default())
                     }
                     }
-                    dioxus_core::AttributeValue::Int(n) => {
+                    BorrowedAttributeValue::Int(n) => {
                         i.set_attribute(id.0 as u32, name, &n.to_string(), ns.unwrap_or_default())
                         i.set_attribute(id.0 as u32, name, &n.to_string(), ns.unwrap_or_default())
                     }
                     }
-                    dioxus_core::AttributeValue::Bool(b) => i.set_attribute(
+                    BorrowedAttributeValue::Bool(b) => i.set_attribute(
                         id.0 as u32,
                         id.0 as u32,
                         name,
                         name,
                         if *b { "true" } else { "false" },
                         if *b { "true" } else { "false" },
                         ns.unwrap_or_default(),
                         ns.unwrap_or_default(),
                     ),
                     ),
+                    BorrowedAttributeValue::None => {
+                        i.remove_attribute(id.0 as u32, name, ns.unwrap_or_default())
+                    }
                     _ => unreachable!(),
                     _ => unreachable!(),
                 },
                 },
                 SetText { value, id } => i.set_text(id.0 as u32, value),
                 SetText { value, id } => i.set_text(id.0 as u32, value),