nodes.rs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672
  1. //! Virtual Node Support
  2. //! --------------------
  3. //! VNodes represent lazily-constructed VDom trees that support diffing and event handlers.
  4. //!
  5. //! These VNodes should be *very* cheap and *very* fast to construct - building a full tree should be insanely quick.
  6. use crate::{
  7. events::VirtualEvent,
  8. innerlude::{empty_cell, Context, DomTree, ElementId, Properties, Scope, ScopeId, FC},
  9. };
  10. use bumpalo::{boxed::Box as BumpBox, Bump};
  11. use std::{
  12. cell::{Cell, RefCell},
  13. fmt::{Arguments, Debug, Formatter},
  14. marker::PhantomData,
  15. mem::ManuallyDrop,
  16. rc::Rc,
  17. };
  18. pub struct VNode<'src> {
  19. pub kind: VNodeKind<'src>,
  20. pub(crate) key: Option<&'src str>,
  21. }
  22. impl<'src> VNode<'src> {
  23. pub fn key(&self) -> Option<&'src str> {
  24. self.key
  25. }
  26. pub fn direct_id(&self) -> ElementId {
  27. self.try_direct_id().unwrap()
  28. }
  29. pub fn try_direct_id(&self) -> Option<ElementId> {
  30. match &self.kind {
  31. VNodeKind::Text(el) => el.dom_id.get(),
  32. VNodeKind::Element(el) => el.dom_id.get(),
  33. VNodeKind::Anchor(el) => el.dom_id.get(),
  34. VNodeKind::Fragment(_) => None,
  35. VNodeKind::Component(_) => None,
  36. VNodeKind::Suspended(_) => None,
  37. }
  38. }
  39. }
  40. /// Tools for the base unit of the virtual dom - the VNode
  41. /// VNodes are intended to be quickly-allocated, lightweight enum values.
  42. ///
  43. /// Components will be generating a lot of these very quickly, so we want to
  44. /// limit the amount of heap allocations / overly large enum sizes.
  45. pub enum VNodeKind<'src> {
  46. Text(VText<'src>),
  47. Element(&'src VElement<'src>),
  48. Fragment(VFragment<'src>),
  49. Component(&'src VComponent<'src>),
  50. Suspended(VSuspended),
  51. Anchor(VAnchor),
  52. }
  53. pub struct VAnchor {
  54. pub dom_id: Cell<Option<ElementId>>,
  55. }
  56. pub struct VText<'src> {
  57. pub text: &'src str,
  58. pub dom_id: Cell<Option<ElementId>>,
  59. pub is_static: bool,
  60. }
  61. pub struct VFragment<'src> {
  62. pub children: &'src [VNode<'src>],
  63. pub is_static: bool,
  64. }
  65. pub trait DioxusElement {
  66. const TAG_NAME: &'static str;
  67. const NAME_SPACE: Option<&'static str>;
  68. #[inline]
  69. fn tag_name(&self) -> &'static str {
  70. Self::TAG_NAME
  71. }
  72. #[inline]
  73. fn namespace(&self) -> Option<&'static str> {
  74. Self::NAME_SPACE
  75. }
  76. }
  77. pub struct VElement<'a> {
  78. // tag is always static
  79. pub tag_name: &'static str,
  80. pub namespace: Option<&'static str>,
  81. pub dom_id: Cell<Option<ElementId>>,
  82. pub static_listeners: bool,
  83. pub listeners: &'a [Listener<'a>],
  84. pub static_attrs: bool,
  85. pub attributes: &'a [Attribute<'a>],
  86. pub static_children: bool,
  87. pub children: &'a [VNode<'a>],
  88. }
  89. /// An attribute on a DOM node, such as `id="my-thing"` or
  90. /// `href="https://example.com"`.
  91. #[derive(Clone, Debug)]
  92. pub struct Attribute<'a> {
  93. pub name: &'static str,
  94. pub value: &'a str,
  95. pub is_static: bool,
  96. pub is_volatile: bool,
  97. // Doesn't exist in the html spec, mostly used to denote "style" tags - could be for any type of group
  98. pub namespace: Option<&'static str>,
  99. }
  100. /// An event listener.
  101. /// IE onclick, onkeydown, etc
  102. pub struct Listener<'bump> {
  103. /// The type of event to listen for.
  104. pub(crate) event: &'static str,
  105. pub mounted_node: Cell<Option<ElementId>>,
  106. pub(crate) callback: RefCell<Option<BumpBox<'bump, dyn FnMut(VirtualEvent) + 'bump>>>,
  107. }
  108. impl Listener<'_> {
  109. // serialize the listener event stuff to a string
  110. pub fn serialize(&self) {
  111. //
  112. }
  113. pub fn deserialize() {
  114. //
  115. }
  116. }
  117. /// Virtual Components for custom user-defined components
  118. /// Only supports the functional syntax
  119. pub struct VComponent<'src> {
  120. pub ass_scope: Cell<Option<ScopeId>>,
  121. pub(crate) caller: Rc<dyn Fn(&Scope) -> DomTree>,
  122. pub(crate) children: &'src [VNode<'src>],
  123. pub(crate) comparator: Option<&'src dyn Fn(&VComponent) -> bool>,
  124. pub(crate) drop_props: Option<&'src dyn FnOnce()>,
  125. pub is_static: bool,
  126. // a pointer into the bump arena (given by the 'src lifetime)
  127. pub(crate) raw_props: *const (),
  128. // a pointer to the raw fn typ
  129. // pub(crate) user_fc: BumpB\,
  130. pub(crate) user_fc: *const (),
  131. }
  132. pub struct VSuspended {
  133. pub node: Rc<Cell<Option<ElementId>>>,
  134. }
  135. /// This struct provides an ergonomic API to quickly build VNodes.
  136. ///
  137. /// NodeFactory is used to build VNodes in the component's memory space.
  138. /// This struct adds metadata to the final VNode about listeners, attributes, and children
  139. #[derive(Copy, Clone)]
  140. pub struct NodeFactory<'a> {
  141. pub(crate) bump: &'a Bump,
  142. }
  143. impl<'a> NodeFactory<'a> {
  144. pub fn new(bump: &'a Bump) -> NodeFactory<'a> {
  145. NodeFactory { bump }
  146. }
  147. #[inline]
  148. pub fn bump(&self) -> &'a bumpalo::Bump {
  149. &self.bump
  150. }
  151. pub fn render_directly<F>(&self, lazy_nodes: LazyNodes<'a, F>) -> DomTree<'a>
  152. where
  153. F: FnOnce(NodeFactory<'a>) -> VNode<'a>,
  154. {
  155. Some(lazy_nodes.into_vnode(NodeFactory { bump: self.bump }))
  156. }
  157. pub fn unstable_place_holder() -> VNode<'static> {
  158. VNode {
  159. key: None,
  160. kind: VNodeKind::Text(VText {
  161. text: "",
  162. dom_id: empty_cell(),
  163. is_static: true,
  164. }),
  165. }
  166. }
  167. /// Used in a place or two to make it easier to build vnodes from dummy text
  168. pub fn static_text(&self, text: &'static str) -> VNode<'a> {
  169. VNode {
  170. key: None,
  171. kind: VNodeKind::Text(VText {
  172. dom_id: empty_cell(),
  173. text,
  174. is_static: true,
  175. }),
  176. }
  177. }
  178. /// Parses a lazy text Arguments and returns a string and a flag indicating if the text is 'static
  179. ///
  180. /// Text that's static may be pointer compared, making it cheaper to diff
  181. pub fn raw_text(&self, args: Arguments) -> (&'a str, bool) {
  182. match args.as_str() {
  183. Some(static_str) => (static_str, true),
  184. None => {
  185. use bumpalo::core_alloc::fmt::Write;
  186. let mut s = bumpalo::collections::String::new_in(self.bump());
  187. s.write_fmt(args).unwrap();
  188. (s.into_bump_str(), false)
  189. }
  190. }
  191. }
  192. /// Create some text that's allocated along with the other vnodes
  193. ///
  194. pub fn text(&self, args: Arguments) -> VNode<'a> {
  195. let (text, is_static) = self.raw_text(args);
  196. VNode {
  197. key: None,
  198. kind: VNodeKind::Text(VText {
  199. text,
  200. is_static,
  201. dom_id: empty_cell(),
  202. }),
  203. }
  204. }
  205. pub fn element<L, A, V>(
  206. &self,
  207. el: impl DioxusElement,
  208. listeners: L,
  209. attributes: A,
  210. children: V,
  211. key: Option<Arguments>,
  212. ) -> VNode<'a>
  213. where
  214. L: 'a + AsRef<[Listener<'a>]>,
  215. A: 'a + AsRef<[Attribute<'a>]>,
  216. V: 'a + AsRef<[VNode<'a>]>,
  217. {
  218. self.raw_element(
  219. el.tag_name(),
  220. el.namespace(),
  221. listeners,
  222. attributes,
  223. children,
  224. key,
  225. )
  226. }
  227. pub fn raw_element<L, A, V>(
  228. &self,
  229. tag: &'static str,
  230. namespace: Option<&'static str>,
  231. listeners: L,
  232. attributes: A,
  233. children: V,
  234. key: Option<Arguments>,
  235. ) -> VNode<'a>
  236. where
  237. L: 'a + AsRef<[Listener<'a>]>,
  238. A: 'a + AsRef<[Attribute<'a>]>,
  239. V: 'a + AsRef<[VNode<'a>]>,
  240. {
  241. let listeners: &'a L = self.bump().alloc(listeners);
  242. let listeners = listeners.as_ref();
  243. let attributes: &'a A = self.bump().alloc(attributes);
  244. let attributes = attributes.as_ref();
  245. let children: &'a V = self.bump().alloc(children);
  246. let children = children.as_ref();
  247. let key = key.map(|f| self.raw_text(f).0);
  248. VNode {
  249. key,
  250. kind: VNodeKind::Element(self.bump().alloc(VElement {
  251. tag_name: tag,
  252. namespace,
  253. listeners,
  254. attributes,
  255. children,
  256. dom_id: empty_cell(),
  257. // todo: wire up more constization
  258. static_listeners: false,
  259. static_attrs: false,
  260. static_children: false,
  261. })),
  262. }
  263. }
  264. pub fn suspended() -> VNode<'static> {
  265. VNode {
  266. key: None,
  267. kind: VNodeKind::Suspended(VSuspended {
  268. node: Rc::new(empty_cell()),
  269. }),
  270. }
  271. }
  272. pub fn attr(
  273. &self,
  274. name: &'static str,
  275. val: Arguments,
  276. namespace: Option<&'static str>,
  277. is_volatile: bool,
  278. ) -> Attribute<'a> {
  279. let (value, is_static) = self.raw_text(val);
  280. Attribute {
  281. name,
  282. value,
  283. is_static,
  284. namespace,
  285. is_volatile,
  286. }
  287. }
  288. pub fn attr_with_alloc_val(
  289. &self,
  290. name: &'static str,
  291. val: &'a str,
  292. namespace: Option<&'static str>,
  293. is_volatile: bool,
  294. ) -> Attribute<'a> {
  295. Attribute {
  296. name,
  297. value: val,
  298. is_static: false,
  299. namespace,
  300. is_volatile,
  301. }
  302. }
  303. pub fn component<P, V>(
  304. &self,
  305. component: FC<P>,
  306. props: P,
  307. key: Option<Arguments>,
  308. children: V,
  309. ) -> VNode<'a>
  310. where
  311. P: Properties + 'a,
  312. V: 'a + AsRef<[VNode<'a>]>,
  313. {
  314. // TODO
  315. // It's somewhat wrong to go about props like this
  316. // We don't want the fat part of the fat pointer
  317. // This function does static dispatch so we don't need any VTable stuff
  318. let children: &'a V = self.bump().alloc(children);
  319. let children = children.as_ref();
  320. let props = self.bump().alloc(props);
  321. let raw_props = props as *mut P as *mut ();
  322. let user_fc = component as *const ();
  323. let comparator: Option<&dyn Fn(&VComponent) -> bool> = Some(self.bump().alloc_with(|| {
  324. move |other: &VComponent| {
  325. if user_fc == other.user_fc {
  326. // Safety
  327. // - We guarantee that FC<P> is the same by function pointer
  328. // - Because FC<P> is the same, then P must be the same (even with generics)
  329. // - Non-static P are autoderived to memoize as false
  330. // - This comparator is only called on a corresponding set of bumpframes
  331. let props_memoized = unsafe {
  332. let real_other: &P = &*(other.raw_props as *const _ as *const P);
  333. props.memoize(&real_other)
  334. };
  335. // It's only okay to memoize if there are no children and the props can be memoized
  336. // Implementing memoize is unsafe and done automatically with the props trait
  337. match (props_memoized, children.len() == 0) {
  338. (true, true) => true,
  339. _ => false,
  340. }
  341. } else {
  342. false
  343. }
  344. }
  345. }));
  346. // create a closure to drop the props
  347. let drop_props: Option<&dyn FnOnce()> = Some(self.bump().alloc_with(|| {
  348. move || unsafe {
  349. let real_other = raw_props as *mut _ as *mut P;
  350. let b = BumpBox::from_raw(real_other);
  351. std::mem::drop(b);
  352. }
  353. }));
  354. let is_static = children.len() == 0 && P::IS_STATIC && key.is_none();
  355. let key = key.map(|f| self.raw_text(f).0);
  356. VNode {
  357. key,
  358. kind: VNodeKind::Component(self.bump().alloc_with(|| VComponent {
  359. user_fc,
  360. comparator,
  361. raw_props,
  362. children,
  363. caller: NodeFactory::create_component_caller(component, raw_props),
  364. is_static,
  365. drop_props,
  366. ass_scope: Cell::new(None),
  367. })),
  368. }
  369. }
  370. pub fn create_component_caller<'g, P: 'g>(
  371. component: FC<P>,
  372. raw_props: *const (),
  373. ) -> Rc<dyn for<'r> Fn(&'r Scope) -> DomTree<'r>> {
  374. type Captured<'a> = Rc<dyn for<'r> Fn(&'r Scope) -> DomTree<'r> + 'a>;
  375. let caller: Captured = Rc::new(move |scp: &Scope| -> DomTree {
  376. // cast back into the right lifetime
  377. let safe_props: &'_ P = unsafe { &*(raw_props as *const P) };
  378. let cx: Context<P> = Context {
  379. props: safe_props,
  380. scope: scp,
  381. };
  382. let res = component(cx);
  383. let g2 = unsafe { std::mem::transmute(res) };
  384. g2
  385. });
  386. unsafe { std::mem::transmute::<_, Captured<'static>>(caller) }
  387. }
  388. pub fn fragment_from_iter(self, node_iter: impl IntoVNodeList<'a>) -> VNode<'a> {
  389. let children = node_iter.into_vnode_list(self);
  390. VNode {
  391. key: None,
  392. kind: VNodeKind::Fragment(VFragment {
  393. children,
  394. is_static: false,
  395. }),
  396. }
  397. }
  398. }
  399. /// Trait implementations for use in the rsx! and html! macros.
  400. ///
  401. /// ## Details
  402. ///
  403. /// This section provides convenience methods and trait implementations for converting common structs into a format accepted
  404. /// by the macros.
  405. ///
  406. /// All dynamic content in the macros must flow in through `fragment_from_iter`. Everything else must be statically layed out.
  407. /// We pipe basically everything through `fragment_from_iter`, so we expect a very specific type:
  408. /// ```
  409. /// impl IntoIterator<Item = impl IntoVNode<'a>>
  410. /// ```
  411. ///
  412. /// As such, all node creation must go through the factory, which is only availble in the component context.
  413. /// These strict requirements make it possible to manage lifetimes and state.
  414. pub trait IntoVNode<'a> {
  415. fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a>;
  416. }
  417. pub trait IntoVNodeList<'a> {
  418. fn into_vnode_list(self, cx: NodeFactory<'a>) -> &'a [VNode<'a>];
  419. }
  420. impl<'a, T, V> IntoVNodeList<'a> for T
  421. where
  422. T: IntoIterator<Item = V>,
  423. V: IntoVNode<'a>,
  424. {
  425. fn into_vnode_list(self, cx: NodeFactory<'a>) -> &'a [VNode<'a>] {
  426. let mut nodes = bumpalo::collections::Vec::new_in(cx.bump());
  427. for node in self.into_iter() {
  428. nodes.push(node.into_vnode(cx));
  429. }
  430. if cfg!(debug_assertions) {
  431. if nodes.len() > 1 {
  432. if nodes.last().unwrap().key().is_none() {
  433. log::error!(
  434. r#"
  435. Warning: Each child in an array or iterator should have a unique "key" prop.
  436. Not providing a key will lead to poor performance with lists.
  437. See docs.rs/dioxus for more information.
  438. ---
  439. To help you identify where this error is coming from, we've generated a backtrace.
  440. "#,
  441. );
  442. }
  443. }
  444. }
  445. if nodes.len() == 0 {
  446. nodes.push(VNode {
  447. kind: VNodeKind::Anchor(VAnchor {
  448. dom_id: empty_cell(),
  449. }),
  450. key: None,
  451. });
  452. }
  453. nodes.into_bump_slice()
  454. }
  455. }
  456. pub struct ScopeChildren<'a>(pub &'a [VNode<'a>]);
  457. impl Copy for ScopeChildren<'_> {}
  458. impl<'a> Clone for ScopeChildren<'a> {
  459. fn clone(&self) -> Self {
  460. ScopeChildren(self.0)
  461. }
  462. }
  463. impl ScopeChildren<'_> {
  464. pub unsafe fn extend_lifetime(self) -> ScopeChildren<'static> {
  465. std::mem::transmute(self)
  466. }
  467. pub unsafe fn unextend_lfetime<'a>(self) -> ScopeChildren<'a> {
  468. std::mem::transmute(self)
  469. }
  470. }
  471. impl<'a> IntoVNodeList<'a> for ScopeChildren<'a> {
  472. fn into_vnode_list(self, _: NodeFactory<'a>) -> &'a [VNode<'a>] {
  473. self.0
  474. }
  475. }
  476. // For the case where a rendered VNode is passed into the rsx! macro through curly braces
  477. impl<'a> IntoIterator for VNode<'a> {
  478. type Item = VNode<'a>;
  479. type IntoIter = std::iter::Once<Self::Item>;
  480. fn into_iter(self) -> Self::IntoIter {
  481. std::iter::once(self)
  482. }
  483. }
  484. // For the case where a rendered VNode is passed into the rsx! macro through curly braces
  485. impl<'a> IntoVNode<'a> for VNode<'a> {
  486. fn into_vnode(self, _: NodeFactory<'a>) -> VNode<'a> {
  487. self
  488. }
  489. }
  490. /// A concrete type provider for closures that build VNode structures.
  491. ///
  492. /// This struct wraps lazy structs that build VNode trees Normally, we cannot perform a blanket implementation over
  493. /// closures, but if we wrap the closure in a concrete type, we can maintain separate implementations of IntoVNode.
  494. ///
  495. ///
  496. /// ```rust
  497. /// LazyNodes::new(|f| f.element("div", [], [], [] None))
  498. /// ```
  499. pub struct LazyNodes<'a, G>
  500. where
  501. G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
  502. {
  503. inner: G,
  504. _p: PhantomData<&'a ()>,
  505. }
  506. impl<'a, G> LazyNodes<'a, G>
  507. where
  508. G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
  509. {
  510. pub fn new(f: G) -> Self {
  511. Self {
  512. inner: f,
  513. _p: PhantomData {},
  514. }
  515. }
  516. }
  517. // Our blanket impl
  518. impl<'a, G> IntoVNode<'a> for LazyNodes<'a, G>
  519. where
  520. G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
  521. {
  522. fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
  523. (self.inner)(cx)
  524. }
  525. }
  526. // Our blanket impl
  527. impl<'a, G> IntoIterator for LazyNodes<'a, G>
  528. where
  529. G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
  530. {
  531. type Item = Self;
  532. type IntoIter = std::iter::Once<Self::Item>;
  533. fn into_iter(self) -> Self::IntoIter {
  534. std::iter::once(self)
  535. }
  536. }
  537. // Conveniently, we also support "null" (nothing) passed in
  538. impl IntoVNode<'_> for () {
  539. fn into_vnode<'a>(self, cx: NodeFactory<'a>) -> VNode<'a> {
  540. cx.fragment_from_iter(None as Option<VNode>)
  541. }
  542. }
  543. // Conveniently, we also support "None"
  544. impl IntoVNode<'_> for Option<()> {
  545. fn into_vnode<'a>(self, cx: NodeFactory<'a>) -> VNode<'a> {
  546. cx.fragment_from_iter(None as Option<VNode>)
  547. }
  548. }
  549. impl<'a> IntoVNode<'a> for Option<VNode<'a>> {
  550. fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
  551. match self {
  552. Some(n) => n,
  553. None => cx.fragment_from_iter(None as Option<VNode>),
  554. }
  555. }
  556. }
  557. impl IntoVNode<'_> for &'static str {
  558. fn into_vnode<'a>(self, cx: NodeFactory<'a>) -> VNode<'a> {
  559. cx.static_text(self)
  560. }
  561. }
  562. impl IntoVNode<'_> for Arguments<'_> {
  563. fn into_vnode<'a>(self, cx: NodeFactory<'a>) -> VNode<'a> {
  564. cx.text(self)
  565. }
  566. }
  567. impl Debug for NodeFactory<'_> {
  568. fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  569. Ok(())
  570. }
  571. }
  572. impl Debug for VNode<'_> {
  573. fn fmt(&self, s: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
  574. match &self.kind {
  575. VNodeKind::Element(el) => write!(s, "VElement {{ name: {} }}", el.tag_name),
  576. VNodeKind::Text(t) => write!(s, "VText {{ text: {} }}", t.text),
  577. VNodeKind::Anchor(a) => write!(s, "VAnchor"),
  578. VNodeKind::Fragment(_) => write!(s, "fragment"),
  579. VNodeKind::Suspended { .. } => write!(s, "suspended"),
  580. VNodeKind::Component(_) => write!(s, "component"),
  581. }
  582. }
  583. }