diff --git a/packages/native-core-macro/src/lib.rs b/packages/native-core-macro/src/lib.rs index 42c76cb04..4343743d1 100644 --- a/packages/native-core-macro/src/lib.rs +++ b/packages/native-core-macro/src/lib.rs @@ -3,126 +3,119 @@ extern crate proc_macro; use std::collections::HashSet; use proc_macro::TokenStream; -use quote::quote; -use syn::{ItemImpl, Type}; +use quote::{format_ident, quote}; +use syn::{parse_macro_input, ItemImpl, Type, TypeTuple, TypePath}; /// A helper attribute for deriving `State` for a struct. #[proc_macro_attribute] -pub fn partial_derive_pass(_: TokenStream, input: TokenStream) -> TokenStream { - let impl_block: syn::ItemImpl = syn::parse(input).unwrap(); +pub fn partial_derive_state(_: TokenStream, input: TokenStream) -> TokenStream { + let impl_block: syn::ItemImpl = parse_macro_input!(input as syn::ItemImpl); let parent_dependencies = impl_block .items .iter() .find_map(|item| { - if let syn::ImplItem::Type(syn::ImplItemType { - attrs, ident, ty, .. - }) = item - { + if let syn::ImplItem::Type(syn::ImplItemType { ident, ty, .. }) = item { (ident == "ParentDependencies").then_some(ty) } else { None } }) - .unwrap(); + .expect("ParentDependencies must be defined"); let child_dependencies = impl_block .items .iter() .find_map(|item| { - if let syn::ImplItem::Type(syn::ImplItemType { - attrs, ident, ty, .. - }) = item - { + if let syn::ImplItem::Type(syn::ImplItemType { ident, ty, .. }) = item { (ident == "ChildDependencies").then_some(ty) } else { None } }) - .unwrap(); + .expect("ChildDependencies must be defined"); let node_dependencies = impl_block .items .iter() .find_map(|item| { - if let syn::ImplItem::Type(syn::ImplItemType { - attrs, ident, ty, .. - }) = item - { + if let syn::ImplItem::Type(syn::ImplItemType { ident, ty, .. }) = item { (ident == "NodeDependencies").then_some(ty) } else { None } }) - .unwrap(); + .expect("NodeDependencies must be defined"); let this_type = &impl_block.self_ty; + let this_type = extract_type_path(this_type).unwrap_or_else(|| panic!("Self must be a type path, found {}", quote!(#this_type))); let mut combined_dependencies = HashSet::new(); - let parent_dependencies = match parent_dependencies { - Type::Tuple(tuple) => { + let parent_dependencies = match extract_tuple(parent_dependencies) { + Some(tuple) => { let mut parent_dependencies = Vec::new(); for type_ in &tuple.elems { + let type_ = extract_type_path(type_).unwrap_or_else(|| panic!("ParentDependencies must be a tuple of type paths, found {}", quote!(#type_))); combined_dependencies.insert(type_.clone()); - parent_dependencies.push(type_.clone()); + parent_dependencies.push(type_); } parent_dependencies } - _ => panic!("ParentDependencies must be a tuple"), + _ => panic!("ParentDependencies must be a tuple, found {}", quote!(#parent_dependencies)), }; - let child_dependencies = match child_dependencies { - Type::Tuple(tuple) => { + let child_dependencies = match extract_tuple(child_dependencies) { + Some(tuple) => { let mut child_dependencies = Vec::new(); for type_ in &tuple.elems { + let type_ = extract_type_path(type_).unwrap_or_else(|| panic!("ChildDependencies must be a tuple of type paths, found {}", quote!(#type_))); combined_dependencies.insert(type_.clone()); - child_dependencies.push(type_.clone()); + child_dependencies.push(type_); } child_dependencies } - _ => panic!("ChildDependencies must be a tuple"), + _ => panic!("ChildDependencies must be a tuple, found {}", quote!(#child_dependencies)), }; - let node_dependencies = match node_dependencies { - Type::Tuple(tuple) => { + let node_dependencies = match extract_tuple(node_dependencies) { + Some(tuple) => { let mut node_dependencies = Vec::new(); for type_ in &tuple.elems { + let type_ = extract_type_path(type_).unwrap_or_else(|| panic!("NodeDependencies must be a tuple of type paths, found {}", quote!(#type_))); combined_dependencies.insert(type_.clone()); - node_dependencies.push(type_.clone()); + node_dependencies.push(type_); } node_dependencies } - _ => panic!("NodeDependencies must be a tuple"), + _ => panic!("NodeDependencies must be a tuple, found {}", quote!(#node_dependencies)), }; - combined_dependencies.insert(*this_type.clone()); + combined_dependencies.insert(this_type.clone()); let combined_dependencies: Vec<_> = combined_dependencies.into_iter().collect(); - let parent_dependancies_idxes: Vec<_> = combined_dependencies + let parent_dependancies_idxes: Vec<_> = parent_dependencies .iter() - .enumerate() - .filter_map(|(i, ident)| parent_dependencies.contains(ident).then_some(i)) + .filter_map(|ident| combined_dependencies.iter().position(|i| i == ident)) .collect(); - let child_dependencies_idxes: Vec<_> = combined_dependencies + let child_dependencies_idxes: Vec<_> = child_dependencies .iter() - .enumerate() - .filter_map(|(i, ident)| child_dependencies.contains(ident).then_some(i)) + .filter_map(|ident| combined_dependencies.iter().position(|i| i == ident)) .collect(); - let node_dependencies_idxes: Vec<_> = combined_dependencies + let node_dependencies_idxes: Vec<_> = node_dependencies .iter() - .enumerate() - .filter_map(|(i, ident)| node_dependencies.contains(ident).then_some(i)) + .filter_map(|ident| combined_dependencies.iter().position(|i| i == ident)) .collect(); - let this_type_idx = node_dependencies + let this_type_idx = combined_dependencies .iter() .enumerate() - .find_map(|(i, ident)| (&**this_type == ident).then_some(i)) + .find_map(|(i, ident)| (this_type == *ident).then_some(i)) .unwrap(); + let this_view = format_ident!("__data{}", this_type_idx); - let combined_dependencies = combined_dependencies.iter().map(|ident| { - if ident == &**this_type { + let combined_dependencies_quote = combined_dependencies.iter().map(|ident| { + if ident == &this_type { quote! {shipyard::ViewMut<#ident>} } else { quote! {shipyard::View<#ident>} } }); - let combined_dependencies = quote!((#(#combined_dependencies),*)); + let combined_dependencies_quote = quote!((#(#combined_dependencies_quote,)*)); let ItemImpl { attrs, @@ -138,78 +131,150 @@ pub fn partial_derive_pass(_: TokenStream, input: TokenStream) -> TokenStream { let for_ = trait_.as_ref().map(|t| t.2); let trait_ = trait_.map(|t| t.1); - // if the mutable borrow on the current type overlaps with the child or parent dependancies we need to use apply + let split_views: Vec<_> = (0..combined_dependencies.len()) + .map(|i| { + let ident = format_ident!("__data{}", i); + if i == this_type_idx { + quote!{mut #ident} + } else { + quote!{#ident} + } + }) + .collect(); + + let node_view = node_dependencies_idxes + .iter() + .map(|i| format_ident!("__data{}", i)) + .collect::>(); + let get_node_view = { + if node_dependencies.is_empty() { + quote! { + let raw_node = (); + } + } else { + quote! { + let raw_node: (#(*const #node_dependencies,)*) = (#(&#node_view,)*).get(id).unwrap(); + } + } + }; + let parent_view = parent_dependancies_idxes + .iter() + .map(|i| format_ident!("__data{}", i)) + .collect::>(); + let get_parent_view = { + if parent_dependencies.is_empty() { + quote! { + let raw_parent = tree.parent_id(id).map(|_| ()); + } + } else { + let temps = (0..parent_dependencies.len()) + .map(|i| format_ident!("__temp{}", i)) + .collect::>(); + quote! { + let raw_parent = tree.parent_id(id).and_then(|parent_id| { + let raw_parent: Option<(#(*const #parent_dependencies,)*)> = (#(&#parent_view,)*).get(parent_id).ok().map(|c| { + let (#(#temps,)*) = c; + (#(#temps as *const _,)*) + }); + raw_parent + }); + } + } + }; + let child_view = child_dependencies_idxes + .iter() + .map(|i| format_ident!("__data{}", i)) + .collect::>(); +let get_child_view = { + if child_dependencies.is_empty() { + quote! { + let raw_children: Vec<_> = tree.children_ids(id).into_iter().map(|_| ()).collect(); + } + } else { + let temps = (0..child_dependencies.len()) + .map(|i| format_ident!("__temp{}", i)) + .collect::>(); + quote! { + let raw_children: Vec<_> = tree.children_ids(id).into_iter().filter_map(|id| { + let raw_children: Option<(#(*const #child_dependencies,)*)> = (#(&#child_view,)*).get(id).ok().map(|c| { + let (#(#temps,)*) = c; + (#(#temps as *const _,)*) + }); + raw_children + }).collect(); + } + } + }; + + let trait_generics = trait_ + .as_ref() + .unwrap() + .segments + .last() + .unwrap() + .arguments + .clone(); quote!( #(#attrs)* - #defaultness #unsafety #impl_token #impl_token #generics #trait_ #for_ #self_ty { + #defaultness #unsafety #impl_token #generics #trait_ #for_ #self_ty { #(#items)* - fn workload_system(type_id: TypeId, dependants: FxHashSet, pass_direction: PassDirection) -> WorkloadSystem { - use shipyard::IntoWorkloadSystem; + fn workload_system(type_id: std::any::TypeId, dependants: dioxus_native_core::exports::FxHashSet, pass_direction: dioxus_native_core::PassDirection) -> dioxus_native_core::exports::shipyard::WorkloadSystem { + use dioxus_native_core::exports::shipyard::{IntoWorkloadSystem, Get, AddComponent}; + use dioxus_native_core::tree::TreeRef; - move |data: #combined_dependencies, run_view: RunPassView| { - shipyard::run(type_id, dependants, pass_direction, run_view, |id: NodeId, ctx: &SendAnyMap| { + (move |data: #combined_dependencies_quote, run_view: dioxus_native_core::RunPassView #trait_generics| { + let (#(#split_views,)*) = data; + let (tree, types, _, _, _) = &run_view; + let tree = tree.clone(); + let node_mask = Self::NODE_MASK.build(); + let node_types = types.clone(); + dioxus_native_core::run_pass(type_id, dependants.clone(), pass_direction, run_view, |id, context| { + let node_data: &NodeType<_> = node_types.get(id).unwrap(); // get all of the states from the tree view // Safety: No node has itself as a parent or child. - let myself: SlabEntry<'static, Self> = unsafe { - std::mem::transmute(tree.get_slab_mut::().unwrap().entry(node_id)) - }; - let node_data = tree.get_single::>(node_id).unwrap(); - let node = tree.get::(node_id).unwrap(); - let children = tree.children::(node_id); - let parent = tree.parent::(node_id); + let raw_myself: Option<*mut Self> = (&mut #this_view).get(id).ok().map(|c| c as *mut _); + #get_node_view + #get_parent_view + #get_child_view - let view = NodeView::new(node_id, node_data, &node_mask); - if myself.value.is_none() { - *myself.value = Some(Self::create(view, node, parent, children, context)); - true - } else { + let myself: Option<&mut Self> = unsafe { raw_myself.map(|val| std::mem::transmute(val)) }; + let node = unsafe { std::mem::transmute(raw_node) }; + let parent = unsafe { raw_parent.map(|val| std::mem::transmute(val)) }; + let children = unsafe { std::mem::transmute(raw_children) }; + + let view = NodeView::new(id, node_data, &node_mask); + if let Some(myself) = myself { myself - .value - .as_mut() - .unwrap() .update(view, node, parent, children, context) } + else { + (&mut #this_view).add_component_unchecked( + id, + Self::create(view, node, parent, children, context)); + true + } }) - }.into_workload_system() + }).into_workload_system().unwrap() } } ) .into() } -// pub trait State: Any + Send + Sync { -// /// This is a tuple of (T: Pass, ..) of states read from the parent required to run this pass -// type ParentDependencies: Dependancy; -// /// This is a tuple of (T: Pass, ..) of states read from the children required to run this pass -// type ChildDependencies: Dependancy; -// /// This is a tuple of (T: Pass, ..) of states read from the node required to run this pass -// type NodeDependencies: Dependancy; -// /// A tuple of all the dependencies combined -// type CombinedDependencies: Dependancy; -// /// This is a mask of what aspects of the node are required to run this pass -// const NODE_MASK: NodeMaskBuilder<'static>; -// -// /// Update this state in a node, returns if the state was updated -// fn update<'a>( -// &mut self, -// node_view: NodeView, -// node: ::ElementBorrowed<'a>, -// parent: Option<::ElementBorrowed<'a>>, -// children: Option::ElementBorrowed<'a>>>, -// context: &SendAnyMap, -// ) -> bool; -// -// /// Create a new instance of this state -// fn create<'a>( -// node_view: NodeView, -// node: ::ElementBorrowed<'a>, -// parent: Option<::ElementBorrowed<'a>>, -// children: Option::ElementBorrowed<'a>>>, -// context: &SendAnyMap, -// ) -> Self; -// -// /// Create a workload system for this state -// fn workload_system(dependants: FxHashSet) -> WorkloadSystem; -// } +fn extract_tuple(ty: &Type) -> Option { + match ty { + Type::Tuple(tuple) => Some(tuple.clone()), + Type::Group(group) => extract_tuple(&group.elem), + _ => None, + } +} + +fn extract_type_path(ty: &Type) -> Option { + match ty { + Type::Path(path) => Some(path.clone()), + Type::Group(group) => extract_type_path(&group.elem), + _ => None, + } +} \ No newline at end of file diff --git a/packages/native-core/src/lib.rs b/packages/native-core/src/lib.rs index 777d61760..34a2332e9 100644 --- a/packages/native-core/src/lib.rs +++ b/packages/native-core/src/lib.rs @@ -3,7 +3,7 @@ use std::hash::BuildHasherDefault; pub use node_ref::NodeMask; pub use passes::AnyMapLike; -pub use passes::{Dependancy, State, TypeErasedPass}; +pub use passes::{run_pass, Dependancy, PassDirection, RunPassView, State, TypeErasedPass}; pub use real_dom::{NodeMut, NodeRef, RealDom}; use rustc_hash::FxHasher; @@ -17,13 +17,19 @@ mod passes; pub mod real_dom; pub mod tree; pub mod utils; -pub use passes::run_pass; pub use shipyard::EntityId as NodeId; +// exported for the macro +pub mod exports { + #[doc(hidden)] + pub use rustc_hash::FxHashSet; + pub use shipyard; +} + pub mod prelude { pub use crate::node::{ElementNode, FromAnyValue, NodeType, OwnedAttributeView, TextNode}; pub use crate::node_ref::{AttributeMaskBuilder, NodeMaskBuilder, NodeView}; - pub use crate::passes::{Dependancy, State}; + pub use crate::passes::{AnyState, Dependancy, State}; pub use crate::real_dom::{NodeImmutable, NodeMut, NodeRef, RealDom}; pub use crate::NodeId; pub use crate::SendAnyMap;