virtual_dom.rs 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110
  1. //! # VirtualDOM Implementation for Rust
  2. //! This module provides the primary mechanics to create a hook-based, concurrent VDOM for Rust.
  3. //!
  4. //! In this file, multiple items are defined. This file is big, but should be documented well to
  5. //! navigate the innerworkings of the Dom. We try to keep these main mechanics in this file to limit
  6. //! the possible exposed API surface (keep fields private). This particular implementation of VDOM
  7. //! is extremely efficient, but relies on some unsafety under the hood to do things like manage
  8. //! micro-heaps for components. We are currently working on refactoring the safety out into safe(r)
  9. //! abstractions, but current tests (MIRI and otherwise) show no issues with the current implementation.
  10. //!
  11. //! Included is:
  12. //! - The [`VirtualDom`] itself
  13. //! - The [`Scope`] object for mangning component lifecycle
  14. //! - The [`ActiveFrame`] object for managing the Scope`s microheap
  15. //! - The [`Context`] object for exposing VirtualDOM API to components
  16. //! - The [`NodeCtx`] object for lazyily exposing the `Context` API to the nodebuilder API
  17. //! - The [`Hook`] object for exposing state management in components.
  18. //!
  19. //! This module includes just the barebones for a complete VirtualDOM API.
  20. //! Additional functionality is defined in the respective files.
  21. use crate::{arena::ScopeArena, innerlude::*};
  22. use bumpalo::Bump;
  23. use generational_arena::Arena;
  24. use std::{
  25. any::{Any, TypeId},
  26. cell::RefCell,
  27. collections::{HashMap, HashSet, VecDeque},
  28. fmt::Debug,
  29. future::Future,
  30. ops::Deref,
  31. pin::Pin,
  32. rc::{Rc, Weak},
  33. };
  34. /// An integrated virtual node system that progresses events and diffs UI trees.
  35. /// Differences are converted into patches which a renderer can use to draw the UI.
  36. pub struct VirtualDom {
  37. /// All mounted components are arena allocated to make additions, removals, and references easy to work with
  38. /// A generational arena is used to re-use slots of deleted scopes without having to resize the underlying arena.
  39. ///
  40. /// This is wrapped in an UnsafeCell because we will need to get mutable access to unique values in unique bump arenas
  41. /// and rusts's guartnees cannot prove that this is safe. We will need to maintain the safety guarantees manually.
  42. pub components: ScopeArena,
  43. /// The index of the root component
  44. /// Should always be the first (gen=0, id=0)
  45. pub base_scope: ScopeIdx,
  46. /// All components dump their updates into a queue to be processed
  47. pub(crate) event_queue: EventQueue,
  48. /// a strong allocation to the "caller" for the original component and its props
  49. #[doc(hidden)]
  50. _root_caller: Rc<OpaqueComponent<'static>>,
  51. /// Type of the original ctx. This is stored as TypeId so VirtualDom does not need to be generic.
  52. ///
  53. /// Whenver props need to be updated, an Error will be thrown if the new props do not
  54. /// match the props used to create the VirtualDom.
  55. #[doc(hidden)]
  56. _root_prop_type: std::any::TypeId,
  57. }
  58. // ======================================
  59. // Public Methods for the VirtualDom
  60. // ======================================
  61. impl VirtualDom {
  62. /// Create a new instance of the Dioxus Virtual Dom with no properties for the root component.
  63. ///
  64. /// This means that the root component must either consumes its own context, or statics are used to generate the page.
  65. /// The root component can access things like routing in its context.
  66. ///
  67. /// As an end-user, you'll want to use the Renderer's "new" method instead of this method.
  68. /// Directly creating the VirtualDOM is only useful when implementing a new renderer.
  69. ///
  70. ///
  71. /// ```ignore
  72. /// // Directly from a closure
  73. ///
  74. /// let dom = VirtualDom::new(|ctx| ctx.render(rsx!{ div {"hello world"} }));
  75. ///
  76. /// // or pass in...
  77. ///
  78. /// let root = |ctx| {
  79. /// ctx.render(rsx!{
  80. /// div {"hello world"}
  81. /// })
  82. /// }
  83. /// let dom = VirtualDom::new(root);
  84. ///
  85. /// // or directly from a fn
  86. ///
  87. /// fn Example(ctx: Context<()>) -> VNode {
  88. /// ctx.render(rsx!{ div{"hello world"} })
  89. /// }
  90. ///
  91. /// let dom = VirtualDom::new(Example);
  92. /// ```
  93. pub fn new(root: impl Fn(Context<()>) -> VNode + 'static) -> Self {
  94. Self::new_with_props(root, ())
  95. }
  96. /// Start a new VirtualDom instance with a dependent ctx.
  97. /// Later, the props can be updated by calling "update" with a new set of props, causing a set of re-renders.
  98. ///
  99. /// This is useful when a component tree can be driven by external state (IE SSR) but it would be too expensive
  100. /// to toss out the entire tree.
  101. ///
  102. /// ```ignore
  103. /// // Directly from a closure
  104. ///
  105. /// let dom = VirtualDom::new(|ctx| ctx.render(rsx!{ div {"hello world"} }));
  106. ///
  107. /// // or pass in...
  108. ///
  109. /// let root = |ctx| {
  110. /// ctx.render(rsx!{
  111. /// div {"hello world"}
  112. /// })
  113. /// }
  114. /// let dom = VirtualDom::new(root);
  115. ///
  116. /// // or directly from a fn
  117. ///
  118. /// fn Example(ctx: Context, props: &SomeProps) -> VNode {
  119. /// ctx.render(rsx!{ div{"hello world"} })
  120. /// }
  121. ///
  122. /// let dom = VirtualDom::new(Example);
  123. /// ```
  124. pub fn new_with_props<P: Properties + 'static>(
  125. root: impl Fn(Context<P>) -> VNode + 'static,
  126. root_props: P,
  127. ) -> Self {
  128. let components = ScopeArena::new(Arena::new());
  129. // Normally, a component would be passed as a child in the RSX macro which automatically produces OpaqueComponents
  130. // Here, we need to make it manually, using an RC to force the Weak reference to stick around for the main scope.
  131. let _root_caller: Rc<OpaqueComponent<'static>> = Rc::new(move |scope| {
  132. // the lifetime of this closure is just as long as the lifetime on the scope reference
  133. // this closure moves root props (which is static) into this closure
  134. let props = unsafe { &*(&root_props as *const _) };
  135. root(Context { props, scope })
  136. });
  137. // Create a weak reference to the OpaqueComponent for the root scope to use as its render function
  138. let caller_ref = Rc::downgrade(&_root_caller);
  139. // Build a funnel for hooks to send their updates into. The `use_hook` method will call into the update funnel.
  140. let event_queue = EventQueue::default();
  141. let _event_queue = event_queue.clone();
  142. // Make the first scope
  143. // We don't run the component though, so renderers will need to call "rebuild" when they initialize their DOM
  144. let link = components.clone();
  145. let event_channel = Rc::new(move || {});
  146. let base_scope = components
  147. .with(|arena| {
  148. arena.insert_with(move |myidx| {
  149. Scope::new(caller_ref, myidx, None, 0, event_channel, link, &[])
  150. })
  151. })
  152. .unwrap();
  153. Self {
  154. _root_caller,
  155. base_scope,
  156. event_queue,
  157. components,
  158. _root_prop_type: TypeId::of::<P>(),
  159. }
  160. }
  161. /// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom rom scratch
  162. /// Currently this doesn't do what we want it to do
  163. pub fn rebuild<'s>(&'s mut self) -> Result<EditList<'s>> {
  164. let mut diff_machine = DiffMachine::new();
  165. // Schedule an update and then immediately call it on the root component
  166. // This is akin to a hook being called from a listener and requring a re-render
  167. // Instead, this is done on top-level component
  168. let base = self.components.try_get(self.base_scope)?;
  169. let update = &base.event_channel;
  170. update();
  171. self.progress_completely(&mut diff_machine)?;
  172. Ok(diff_machine.consume())
  173. }
  174. pub fn base_scope(&self) -> &Scope {
  175. todo!()
  176. }
  177. }
  178. // ======================================
  179. // Private Methods for the VirtualDom
  180. // ======================================
  181. impl VirtualDom {
  182. /// This method is the most sophisticated way of updating the virtual dom after an external event has been triggered.
  183. ///
  184. /// Given a synthetic event, the component that triggered the event, and the index of the callback, this runs the virtual
  185. /// dom to completion, tagging components that need updates, compressing events together, and finally emitting a single
  186. /// change list.
  187. ///
  188. /// If implementing an external renderer, this is the perfect method to combine with an async event loop that waits on
  189. /// listeners, something like this:
  190. ///
  191. /// ```ignore
  192. /// while let Ok(event) = receiver.recv().await {
  193. /// let edits = self.internal_dom.progress_with_event(event)?;
  194. /// for edit in &edits {
  195. /// patch_machine.handle_edit(edit);
  196. /// }
  197. /// }
  198. /// ```
  199. ///
  200. /// Note: this method is not async and does not provide suspense-like functionality. It is up to the renderer to provide the
  201. /// executor and handlers for suspense as show in the example.
  202. ///
  203. /// ```ignore
  204. /// let (sender, receiver) = channel::new();
  205. /// sender.send(EventTrigger::start());
  206. ///
  207. /// let mut dom = VirtualDom::new();
  208. /// dom.suspense_handler(|event| sender.send(event));
  209. ///
  210. /// while let Ok(diffs) = dom.progress_with_event(receiver.recv().await) {
  211. /// render(diffs);
  212. /// }
  213. ///
  214. /// ```
  215. //
  216. // Developer notes:
  217. // ----
  218. // This method has some pretty complex safety guarantees to uphold.
  219. // We interact with bump arenas, raw pointers, and use UnsafeCell to get a partial borrow of the arena.
  220. // The final EditList has edits that pull directly from the Bump Arenas which add significant complexity
  221. // in crafting a 100% safe solution with traditional lifetimes. Consider this method to be internally unsafe
  222. // but the guarantees provide a safe, fast, and efficient abstraction for the VirtualDOM updating framework.
  223. //
  224. // A good project would be to remove all unsafe from this crate and move the unsafety into safer abstractions.
  225. pub fn progress_with_event(&mut self, event: EventTrigger) -> Result<EditList> {
  226. let id = event.component_id.clone();
  227. self.components.try_get_mut(id)?.call_listener(event)?;
  228. let mut diff_machine = DiffMachine::new();
  229. self.progress_completely(&mut diff_machine)?;
  230. Ok(diff_machine.consume())
  231. }
  232. /// Consume the event queue, descending depth-first.
  233. /// Only ever run each component once.
  234. ///
  235. /// The DiffMachine logs its progress as it goes which might be useful for certain types of renderers.
  236. pub(crate) fn progress_completely<'s>(
  237. &'s mut self,
  238. diff_machine: &'_ mut DiffMachine<'s>,
  239. ) -> Result<()> {
  240. // Add this component to the list of components that need to be difed
  241. #[allow(unused_assignments)]
  242. let mut cur_height: u32 = 0;
  243. // Now, there are events in the queue
  244. let mut seen_nodes = HashSet::<ScopeIdx>::new();
  245. let mut updates = self.event_queue.0.as_ref().borrow_mut();
  246. // Order the nodes by their height, we want the biggest nodes on the top
  247. // This prevents us from running the same component multiple times
  248. updates.sort_unstable();
  249. // Iterate through the triggered nodes (sorted by height) and begin to diff them
  250. for update in updates.drain(..) {
  251. // Make sure this isn't a node we've already seen, we don't want to double-render anything
  252. // If we double-renderer something, this would cause memory safety issues
  253. if seen_nodes.contains(&update.idx) {
  254. continue;
  255. }
  256. // Now, all the "seen nodes" are nodes that got notified by running this listener
  257. seen_nodes.insert(update.idx.clone());
  258. // Start a new mutable borrow to components
  259. // We are guaranteeed that this scope is unique because we are tracking which nodes have modified
  260. let cur_component = self.components.try_get_mut(update.idx).unwrap();
  261. cur_component.run_scope()?;
  262. diff_machine.diff_node(cur_component.old_frame(), cur_component.next_frame());
  263. cur_height = cur_component.height;
  264. log::debug!(
  265. "Processing update: {:#?} with height {}",
  266. &update.idx,
  267. cur_height
  268. );
  269. // Now, the entire subtree has been invalidated. We need to descend depth-first and process
  270. // any updates that the diff machine has proprogated into the component lifecycle queue
  271. while let Some(event) = diff_machine.lifecycle_events.pop_front() {
  272. match event {
  273. // A new component has been computed from the diffing algorithm
  274. // create a new component in the arena, run it, move the diffing machine to this new spot, and then diff it
  275. // this will flood the lifecycle queue with new updates to build up the subtree
  276. LifeCycleEvent::Mount {
  277. caller,
  278. root_id: id,
  279. stable_scope_addr,
  280. } => {
  281. log::debug!("Mounting a new component");
  282. // We're modifying the component arena while holding onto references into the assoiated bump arenas of its children
  283. // those references are stable, even if the component arena moves around in memory, thanks to the bump arenas.
  284. // However, there is no way to convey this to rust, so we need to use unsafe to pierce through the lifetime.
  285. // Insert a new scope into our component list
  286. let idx = self.components.with(|components| {
  287. components.insert_with(|new_idx| {
  288. let height = cur_height + 1;
  289. Scope::new(
  290. caller,
  291. new_idx,
  292. Some(cur_component.arena_idx),
  293. height,
  294. self.event_queue.new_channel(height, new_idx),
  295. self.components.clone(),
  296. &[],
  297. )
  298. })
  299. })?;
  300. {
  301. let cur_component = self.components.try_get_mut(update.idx).unwrap();
  302. let mut ch = cur_component.descendents.borrow_mut();
  303. ch.insert(idx);
  304. std::mem::drop(ch);
  305. }
  306. // Grab out that component
  307. let new_component = self.components.try_get_mut(idx).unwrap();
  308. // Actually initialize the caller's slot with the right address
  309. *stable_scope_addr.upgrade().unwrap().as_ref().borrow_mut() = Some(idx);
  310. // Run the scope for one iteration to initialize it
  311. new_component.run_scope()?;
  312. // Navigate the diff machine to the right point in the output dom
  313. diff_machine.change_list.load_known_root(id);
  314. // And then run the diff algorithm
  315. diff_machine
  316. .diff_node(new_component.old_frame(), new_component.next_frame());
  317. // Finally, insert this node as a seen node.
  318. seen_nodes.insert(idx);
  319. }
  320. // A component has remained in the same location but its properties have changed
  321. // We need to process this component and then dump the output lifecycle events into the queue
  322. LifeCycleEvent::PropsChanged {
  323. caller,
  324. root_id,
  325. stable_scope_addr,
  326. } => {
  327. log::debug!("Updating a component after its props have changed");
  328. // Get the stable index to the target component
  329. // This *should* exist due to guarantees in the diff algorithm
  330. let idx = stable_scope_addr
  331. .upgrade()
  332. .unwrap()
  333. .as_ref()
  334. .borrow()
  335. .unwrap();
  336. // Grab out that component
  337. let component = self.components.try_get_mut(idx).unwrap();
  338. // We have to move the caller over or running the scope will fail
  339. component.update_caller(caller);
  340. // Run the scope
  341. component.run_scope()?;
  342. // Navigate the diff machine to the right point in the output dom
  343. diff_machine.change_list.load_known_root(root_id);
  344. // And then run the diff algorithm
  345. diff_machine.diff_node(component.old_frame(), component.next_frame());
  346. // Finally, insert this node as a seen node.
  347. seen_nodes.insert(idx);
  348. }
  349. // A component's parent has updated, but its properties did not change.
  350. // This means the caller ptr is invalidated and needs to be updated, but the component itself does not need to be re-ran
  351. LifeCycleEvent::SameProps {
  352. caller,
  353. stable_scope_addr,
  354. ..
  355. } => {
  356. // In this case, the parent made a new VNode that resulted in the same props for us
  357. // However, since our caller is located in a Bump frame, we need to update the caller pointer (which is now invalid)
  358. log::debug!("Received the same props");
  359. // Get the stable index to the target component
  360. // This *should* exist due to guarantees in the diff algorithm
  361. let idx = stable_scope_addr
  362. .upgrade()
  363. .unwrap()
  364. .as_ref()
  365. .borrow()
  366. .unwrap();
  367. // Grab out that component
  368. let component = self.components.try_get_mut(idx).unwrap();
  369. // We have to move the caller over or running the scope will fail
  370. component.update_caller(caller);
  371. // This time, we will not add it to our seen nodes since we did not actually run it
  372. }
  373. LifeCycleEvent::Remove {
  374. root_id,
  375. stable_scope_addr,
  376. } => {
  377. let id = stable_scope_addr
  378. .upgrade()
  379. .unwrap()
  380. .as_ref()
  381. .borrow()
  382. .unwrap();
  383. log::warn!("Removing node {:#?}", id);
  384. // This would normally be recursive but makes sense to do linear to
  385. let mut children_to_remove = VecDeque::new();
  386. children_to_remove.push_back(id);
  387. // Accumulate all the child components that need to be removed
  388. while let Some(child_id) = children_to_remove.pop_back() {
  389. let comp = self.components.try_get(child_id).unwrap();
  390. let children = comp.descendents.borrow();
  391. for child in children.iter() {
  392. children_to_remove.push_front(*child);
  393. }
  394. log::debug!("Removing component: {:#?}", child_id);
  395. self.components
  396. .with(|components| components.remove(child_id).unwrap())
  397. .unwrap();
  398. }
  399. }
  400. LifeCycleEvent::Replace {
  401. caller,
  402. root_id: id,
  403. ..
  404. } => {
  405. unimplemented!("This feature (Replace) is unimplemented")
  406. }
  407. }
  408. }
  409. }
  410. Ok(())
  411. }
  412. }
  413. // TODO!
  414. // These impls are actually wrong. The DOM needs to have a mutex implemented.
  415. unsafe impl Sync for VirtualDom {}
  416. unsafe impl Send for VirtualDom {}
  417. /// Every component in Dioxus is represented by a `Scope`.
  418. ///
  419. /// Scopes contain the state for hooks, the component's props, and other lifecycle information.
  420. ///
  421. /// Scopes are allocated in a generational arena. As components are mounted/unmounted, they will replace slots of dead components.
  422. /// The actual contents of the hooks, though, will be allocated with the standard allocator. These should not allocate as frequently.
  423. pub struct Scope {
  424. // The parent's scope ID
  425. pub parent: Option<ScopeIdx>,
  426. // IDs of children that this scope has created
  427. // This enables us to drop the children and their children when this scope is destroyed
  428. descendents: RefCell<HashSet<ScopeIdx>>,
  429. child_nodes: &'static [VNode<'static>],
  430. // A reference to the list of components.
  431. // This lets us traverse the component list whenever we need to access our parent or children.
  432. arena_link: ScopeArena,
  433. pub shared_contexts: RefCell<HashMap<TypeId, Rc<dyn Any>>>,
  434. // Our own ID accessible from the component map
  435. pub arena_idx: ScopeIdx,
  436. pub height: u32,
  437. pub event_channel: Rc<dyn Fn() + 'static>,
  438. // pub event_queue: EventQueue,
  439. pub caller: Weak<OpaqueComponent<'static>>,
  440. pub hookidx: RefCell<usize>,
  441. // ==========================
  442. // slightly unsafe stuff
  443. // ==========================
  444. // an internal, highly efficient storage of vnodes
  445. pub frames: ActiveFrame,
  446. // These hooks are actually references into the hook arena
  447. // These two could be combined with "OwningRef" to remove unsafe usage
  448. // or we could dedicate a tiny bump arena just for them
  449. // could also use ourborous
  450. hooks: RefCell<Vec<Hook>>,
  451. // Unsafety:
  452. // - is self-refenrential and therefore needs to point into the bump
  453. // Stores references into the listeners attached to the vnodes
  454. // NEEDS TO BE PRIVATE
  455. pub(crate) listeners: RefCell<Vec<*const dyn Fn(VirtualEvent)>>,
  456. }
  457. // We need to pin the hook so it doesn't move as we initialize the list of hooks
  458. type Hook = Pin<Box<dyn std::any::Any>>;
  459. type EventChannel = Rc<dyn Fn()>;
  460. impl Scope {
  461. // we are being created in the scope of an existing component (where the creator_node lifetime comes into play)
  462. // we are going to break this lifetime by force in order to save it on ourselves.
  463. // To make sure that the lifetime isn't truly broken, we receive a Weak RC so we can't keep it around after the parent dies.
  464. // This should never happen, but is a good check to keep around
  465. //
  466. // Scopes cannot be made anywhere else except for this file
  467. // Therefore, their lifetimes are connected exclusively to the virtual dom
  468. fn new<'creator_node>(
  469. caller: Weak<OpaqueComponent<'creator_node>>,
  470. arena_idx: ScopeIdx,
  471. parent: Option<ScopeIdx>,
  472. height: u32,
  473. event_channel: EventChannel,
  474. arena_link: ScopeArena,
  475. child_nodes: &'creator_node [VNode<'creator_node>],
  476. ) -> Self {
  477. log::debug!(
  478. "New scope created, height is {}, idx is {:?}",
  479. height,
  480. arena_idx
  481. );
  482. // The function to run this scope is actually located in the parent's bump arena.
  483. // Every time the parent is updated, that function is invalidated via double-buffering wiping the old frame.
  484. // If children try to run this invalid caller, it *will* result in UB.
  485. //
  486. // During the lifecycle progression process, this caller will need to be updated. Right now,
  487. // until formal safety abstractions are implemented, we will just use unsafe to "detach" the caller
  488. // lifetime from the bump arena, exposing ourselves to this potential for invalidation. Truthfully,
  489. // this is a bit of a hack, but will remain this way until we've figured out a cleaner solution.
  490. //
  491. // Not the best solution, so TODO on removing this in favor of a dedicated resource abstraction.
  492. let caller = unsafe {
  493. std::mem::transmute::<
  494. Weak<OpaqueComponent<'creator_node>>,
  495. Weak<OpaqueComponent<'static>>,
  496. >(caller)
  497. };
  498. Self {
  499. child_nodes: &[],
  500. caller,
  501. parent,
  502. arena_idx,
  503. height,
  504. event_channel,
  505. arena_link,
  506. frames: ActiveFrame::new(),
  507. hooks: Default::default(),
  508. shared_contexts: Default::default(),
  509. listeners: Default::default(),
  510. hookidx: Default::default(),
  511. descendents: Default::default(),
  512. }
  513. }
  514. pub fn update_caller<'creator_node>(&mut self, caller: Weak<OpaqueComponent<'creator_node>>) {
  515. let broken_caller = unsafe {
  516. std::mem::transmute::<
  517. Weak<OpaqueComponent<'creator_node>>,
  518. Weak<OpaqueComponent<'static>>,
  519. >(caller)
  520. };
  521. self.caller = broken_caller;
  522. }
  523. /// Create a new context and run the component with references from the Virtual Dom
  524. /// This function downcasts the function pointer based on the stored props_type
  525. ///
  526. /// Props is ?Sized because we borrow the props and don't need to know the size. P (sized) is used as a marker (unsized)
  527. pub fn run_scope<'sel>(&'sel mut self) -> Result<()> {
  528. // Cycle to the next frame and then reset it
  529. // This breaks any latent references, invalidating every pointer referencing into it.
  530. self.frames.next().bump.reset();
  531. // Remove all the outdated listeners
  532. //
  533. self.listeners
  534. .try_borrow_mut()
  535. .ok()
  536. .ok_or(Error::FatalInternal("Borrowing listener failed"))?
  537. .drain(..);
  538. *self.hookidx.borrow_mut() = 0;
  539. let caller = self
  540. .caller
  541. .upgrade()
  542. .ok_or(Error::FatalInternal("Failed to get caller"))?;
  543. // Cast the caller ptr from static to one with our own reference
  544. let c2: &OpaqueComponent<'static> = caller.as_ref();
  545. let c3: &OpaqueComponent<'sel> = unsafe { std::mem::transmute(c2) };
  546. let unsafe_head = unsafe { self.own_vnodes(c3) };
  547. self.frames.cur_frame_mut().head_node = unsafe_head;
  548. Ok(())
  549. }
  550. // this is its own function so we can preciesly control how lifetimes flow
  551. unsafe fn own_vnodes<'a>(&'a self, f: &OpaqueComponent<'a>) -> VNode<'static> {
  552. let new_head: VNode<'a> = f(self);
  553. let out: VNode<'static> = std::mem::transmute(new_head);
  554. out
  555. }
  556. // A safe wrapper around calling listeners
  557. // calling listeners will invalidate the list of listeners
  558. // The listener list will be completely drained because the next frame will write over previous listeners
  559. pub fn call_listener(&mut self, trigger: EventTrigger) -> Result<()> {
  560. let EventTrigger {
  561. listener_id, event, ..
  562. } = trigger;
  563. //
  564. unsafe {
  565. // Convert the raw ptr into an actual object
  566. // This operation is assumed to be safe
  567. let listener_fn = self
  568. .listeners
  569. .try_borrow()
  570. .ok()
  571. .ok_or(Error::FatalInternal("Borrowing listener failed"))?
  572. .get(listener_id as usize)
  573. .ok_or(Error::FatalInternal("Event should exist if triggered"))?
  574. .as_ref()
  575. .ok_or(Error::FatalInternal("Raw event ptr is invalid"))?;
  576. // Run the callback with the user event
  577. listener_fn(event);
  578. }
  579. Ok(())
  580. }
  581. pub fn next_frame<'bump>(&'bump self) -> &'bump VNode<'bump> {
  582. self.frames.current_head_node()
  583. }
  584. pub fn old_frame<'bump>(&'bump self) -> &'bump VNode<'bump> {
  585. self.frames.prev_head_node()
  586. }
  587. pub fn cur_frame(&self) -> &BumpFrame {
  588. self.frames.cur_frame()
  589. }
  590. }
  591. /// Components in Dioxus use the "Context" object to interact with their lifecycle.
  592. /// This lets components schedule updates, integrate hooks, and expose their context via the context api.
  593. ///
  594. /// Properties passed down from the parent component are also directly accessible via the exposed "props" field.
  595. ///
  596. /// ```ignore
  597. /// #[derive(Properties)]
  598. /// struct Props {
  599. /// name: String
  600. ///
  601. /// }
  602. ///
  603. /// fn example(ctx: Context, props: &Props -> VNode {
  604. /// html! {
  605. /// <div> "Hello, {ctx.ctx.name}" </div>
  606. /// }
  607. /// }
  608. /// ```
  609. // todo: force lifetime of source into T as a valid lifetime too
  610. // it's definitely possible, just needs some more messing around
  611. pub struct Context<'src, T> {
  612. pub props: &'src T,
  613. pub scope: &'src Scope,
  614. }
  615. impl<'src, T> Copy for Context<'src, T> {}
  616. impl<'src, T> Clone for Context<'src, T> {
  617. fn clone(&self) -> Self {
  618. Self {
  619. props: self.props,
  620. scope: self.scope,
  621. }
  622. }
  623. }
  624. impl<'a, T> Deref for Context<'a, T> {
  625. type Target = &'a T;
  626. fn deref(&self) -> &Self::Target {
  627. &self.props
  628. }
  629. }
  630. impl<'src, T> Scoped<'src> for Context<'src, T> {
  631. fn get_scope(&self) -> &'src Scope {
  632. self.scope
  633. }
  634. }
  635. pub trait Scoped<'src>: Sized {
  636. fn get_scope(&self) -> &'src Scope;
  637. /// Access the children elements passed into the component
  638. fn children(&self) -> &'src [VNode<'src>] {
  639. // We're re-casting the nodes back out
  640. // They don't really have a static lifetime
  641. unsafe {
  642. let scope = self.get_scope();
  643. let nodes = scope.child_nodes;
  644. nodes
  645. }
  646. }
  647. /// Create a subscription that schedules a future render for the reference component
  648. fn schedule_update(&self) -> Rc<dyn Fn() + 'static> {
  649. self.get_scope().event_channel.clone()
  650. }
  651. /// Take a lazy VNode structure and actually build it with the context of the VDom's efficient VNode allocator.
  652. ///
  653. /// This function consumes the context and absorb the lifetime, so these VNodes *must* be returned.
  654. ///
  655. /// ## Example
  656. ///
  657. /// ```ignore
  658. /// fn Component(ctx: Context<()>) -> VNode {
  659. /// // Lazy assemble the VNode tree
  660. /// let lazy_tree = html! {<div> "Hello World" </div>};
  661. ///
  662. /// // Actually build the tree and allocate it
  663. /// ctx.render(lazy_tree)
  664. /// }
  665. ///```
  666. fn render<'a, F: for<'b> FnOnce(&'b NodeCtx<'src>) -> VNode<'src> + 'src + 'a>(
  667. self,
  668. lazy_nodes: LazyNodes<'src, F>,
  669. ) -> VNode<'src> {
  670. lazy_nodes.into_vnode(&NodeCtx {
  671. scope_ref: self.get_scope(),
  672. listener_id: 0.into(),
  673. })
  674. }
  675. // impl<'scope> Context<'scope> {
  676. /// Store a value between renders
  677. ///
  678. /// - Initializer: closure used to create the initial hook state
  679. /// - Runner: closure used to output a value every time the hook is used
  680. /// - Cleanup: closure used to teardown the hook once the dom is cleaned up
  681. ///
  682. /// ```ignore
  683. /// // use_ref is the simplest way of storing a value between renders
  684. /// pub fn use_ref<T: 'static>(initial_value: impl FnOnce() -> T + 'static) -> Rc<RefCell<T>> {
  685. /// use_hook(
  686. /// || Rc::new(RefCell::new(initial_value())),
  687. /// |state| state.clone(),
  688. /// |_| {},
  689. /// )
  690. /// }
  691. /// ```
  692. fn use_hook<InternalHookState: 'static, Output: 'src>(
  693. &self,
  694. // The closure that builds the hook state
  695. initializer: impl FnOnce() -> InternalHookState,
  696. // The closure that takes the hookstate and returns some value
  697. runner: impl FnOnce(&'src mut InternalHookState) -> Output,
  698. // The closure that cleans up whatever mess is left when the component gets torn down
  699. // TODO: add this to the "clean up" group for when the component is dropped
  700. _cleanup: impl FnOnce(InternalHookState),
  701. ) -> Output {
  702. let scope = self.get_scope();
  703. let idx = *scope.hookidx.borrow();
  704. // Grab out the hook list
  705. let mut hooks = scope.hooks.borrow_mut();
  706. // If the idx is the same as the hook length, then we need to add the current hook
  707. if idx >= hooks.len() {
  708. let new_state = initializer();
  709. hooks.push(Box::pin(new_state));
  710. }
  711. *scope.hookidx.borrow_mut() += 1;
  712. let stable_ref = hooks
  713. .get_mut(idx)
  714. .expect("Should not fail, idx is validated")
  715. .as_mut();
  716. let pinned_state = unsafe { Pin::get_unchecked_mut(stable_ref) };
  717. let internal_state = pinned_state.downcast_mut::<InternalHookState>().expect(
  718. r###"
  719. Unable to retrive the hook that was initialized in this index.
  720. Consult the `rules of hooks` to understand how to use hooks properly.
  721. You likely used the hook in a conditional. Hooks rely on consistent ordering between renders.
  722. Any function prefixed with "use" should not be called conditionally.
  723. "###,
  724. );
  725. // We extend the lifetime of the internal state
  726. runner(unsafe { &mut *(internal_state as *mut _) })
  727. }
  728. /// This hook enables the ability to expose state to children further down the VirtualDOM Tree.
  729. ///
  730. /// This is a hook, so it may not be called conditionally!
  731. ///
  732. /// The init method is ran *only* on first use, otherwise it is ignored. However, it uses hooks (ie `use`)
  733. /// so don't put it in a conditional.
  734. ///
  735. /// When the component is dropped, so is the context. Be aware of this behavior when consuming
  736. /// the context via Rc/Weak.
  737. ///
  738. ///
  739. ///
  740. fn use_create_context<T: 'static>(&self, init: impl Fn() -> T) {
  741. let scope = self.get_scope();
  742. let mut ctxs = scope.shared_contexts.borrow_mut();
  743. let ty = TypeId::of::<T>();
  744. let is_initialized = self.use_hook(
  745. || false,
  746. |s| {
  747. let i = s.clone();
  748. *s = true;
  749. i
  750. },
  751. |_| {},
  752. );
  753. match (is_initialized, ctxs.contains_key(&ty)) {
  754. // Do nothing, already initialized and already exists
  755. (true, true) => {}
  756. // Needs to be initialized
  757. (false, false) => {
  758. log::debug!("Initializing context...");
  759. ctxs.insert(ty, Rc::new(init()));
  760. }
  761. _ => debug_assert!(false, "Cannot initialize two contexts of the same type"),
  762. }
  763. }
  764. /// There are hooks going on here!
  765. fn use_context<T: 'static>(&self) -> &'src Rc<T> {
  766. self.try_use_context().unwrap()
  767. }
  768. /// Uses a context, storing the cached value around
  769. fn try_use_context<T: 'static>(&self) -> Result<&'src Rc<T>> {
  770. struct UseContextHook<C> {
  771. par: Option<Rc<C>>,
  772. we: Option<Weak<C>>,
  773. }
  774. self.use_hook(
  775. move || UseContextHook {
  776. par: None as Option<Rc<T>>,
  777. we: None as Option<Weak<T>>,
  778. },
  779. move |hook| {
  780. let scope = self.get_scope();
  781. let mut scope = Some(scope);
  782. if let Some(we) = &hook.we {
  783. if let Some(re) = we.upgrade() {
  784. hook.par = Some(re);
  785. return Ok(hook.par.as_ref().unwrap());
  786. }
  787. }
  788. let ty = TypeId::of::<T>();
  789. while let Some(inner) = scope {
  790. log::debug!("Searching {:#?} for valid shared_context", inner.arena_idx);
  791. let shared_contexts = inner.shared_contexts.borrow();
  792. if let Some(shared_ctx) = shared_contexts.get(&ty) {
  793. log::debug!("found matching ctx");
  794. let rc = shared_ctx
  795. .clone()
  796. .downcast::<T>()
  797. .expect("Should not fail, already validated the type from the hashmap");
  798. hook.we = Some(Rc::downgrade(&rc));
  799. hook.par = Some(rc);
  800. return Ok(hook.par.as_ref().unwrap());
  801. } else {
  802. match inner.parent {
  803. Some(parent_id) => {
  804. let parent = inner
  805. .arena_link
  806. .try_get(parent_id)
  807. .map_err(|_| Error::FatalInternal("Failed to find parent"))?;
  808. scope = Some(parent);
  809. }
  810. None => return Err(Error::MissingSharedContext),
  811. }
  812. }
  813. }
  814. Err(Error::MissingSharedContext)
  815. },
  816. |_| {},
  817. )
  818. }
  819. }
  820. // ==================================================================================
  821. // Supporting structs for the above abstractions
  822. // ==================================================================================
  823. // We actually allocate the properties for components in their parent's properties
  824. // We then expose a handle to use those props for render in the form of "OpaqueComponent"
  825. pub(crate) type OpaqueComponent<'e> = dyn for<'b> Fn(&'b Scope) -> VNode<'b> + 'e;
  826. #[derive(PartialEq, Debug, Clone, Default)]
  827. pub(crate) struct EventQueue(pub Rc<RefCell<Vec<HeightMarker>>>);
  828. impl EventQueue {
  829. pub fn new_channel(&self, height: u32, idx: ScopeIdx) -> Rc<dyn Fn()> {
  830. let inner = self.clone();
  831. let marker = HeightMarker { height, idx };
  832. Rc::new(move || inner.0.as_ref().borrow_mut().push(marker))
  833. }
  834. }
  835. /// A helper type that lets scopes be ordered by their height
  836. #[derive(Debug, Clone, Copy, PartialEq, Eq)]
  837. pub(crate) struct HeightMarker {
  838. pub idx: ScopeIdx,
  839. pub height: u32,
  840. }
  841. impl Ord for HeightMarker {
  842. fn cmp(&self, other: &Self) -> std::cmp::Ordering {
  843. self.height.cmp(&other.height)
  844. }
  845. }
  846. impl PartialOrd for HeightMarker {
  847. fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
  848. Some(self.cmp(other))
  849. }
  850. }
  851. // NodeCtx is used to build VNodes in the component's memory space.
  852. // This struct adds metadata to the final VNode about listeners, attributes, and children
  853. #[derive(Clone)]
  854. pub struct NodeCtx<'a> {
  855. pub scope_ref: &'a Scope,
  856. pub listener_id: RefCell<usize>,
  857. }
  858. impl<'a> NodeCtx<'a> {
  859. pub fn bump(&self) -> &'a Bump {
  860. &self.scope_ref.cur_frame().bump
  861. }
  862. }
  863. impl Debug for NodeCtx<'_> {
  864. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  865. Ok(())
  866. }
  867. }
  868. #[derive(Debug, PartialEq, Hash)]
  869. pub struct ContextId {
  870. // Which component is the scope in
  871. original: ScopeIdx,
  872. // What's the height of the scope
  873. height: u32,
  874. // Which scope is it (in order)
  875. id: u32,
  876. }
  877. pub struct ActiveFrame {
  878. // We use a "generation" for users of contents in the bump frames to ensure their data isn't broken
  879. pub generation: RefCell<usize>,
  880. // The double-buffering situation that we will use
  881. pub frames: [BumpFrame; 2],
  882. }
  883. pub struct BumpFrame {
  884. pub bump: Bump,
  885. pub head_node: VNode<'static>,
  886. }
  887. impl ActiveFrame {
  888. pub fn new() -> Self {
  889. Self::from_frames(
  890. BumpFrame {
  891. bump: Bump::new(),
  892. head_node: VNode::text(""),
  893. },
  894. BumpFrame {
  895. bump: Bump::new(),
  896. head_node: VNode::text(""),
  897. },
  898. )
  899. }
  900. fn from_frames(a: BumpFrame, b: BumpFrame) -> Self {
  901. Self {
  902. generation: 0.into(),
  903. frames: [a, b],
  904. }
  905. }
  906. fn cur_frame(&self) -> &BumpFrame {
  907. match *self.generation.borrow() & 1 == 0 {
  908. true => &self.frames[0],
  909. false => &self.frames[1],
  910. }
  911. }
  912. fn cur_frame_mut(&mut self) -> &mut BumpFrame {
  913. match *self.generation.borrow() & 1 == 0 {
  914. true => &mut self.frames[0],
  915. false => &mut self.frames[1],
  916. }
  917. }
  918. pub fn current_head_node<'b>(&'b self) -> &'b VNode<'b> {
  919. let raw_node = match *self.generation.borrow() & 1 == 0 {
  920. true => &self.frames[0],
  921. false => &self.frames[1],
  922. };
  923. // Give out our self-referential item with our own borrowed lifetime
  924. unsafe {
  925. let unsafe_head = &raw_node.head_node;
  926. let safe_node = std::mem::transmute::<&VNode<'static>, &VNode<'b>>(unsafe_head);
  927. safe_node
  928. }
  929. }
  930. pub fn prev_head_node<'b>(&'b self) -> &'b VNode<'b> {
  931. let raw_node = match *self.generation.borrow() & 1 != 0 {
  932. true => &self.frames[0],
  933. false => &self.frames[1],
  934. };
  935. // Give out our self-referential item with our own borrowed lifetime
  936. unsafe {
  937. let unsafe_head = &raw_node.head_node;
  938. let safe_node = std::mem::transmute::<&VNode<'static>, &VNode<'b>>(unsafe_head);
  939. safe_node
  940. }
  941. }
  942. fn next(&mut self) -> &mut BumpFrame {
  943. *self.generation.borrow_mut() += 1;
  944. if *self.generation.borrow() % 2 == 0 {
  945. &mut self.frames[0]
  946. } else {
  947. &mut self.frames[1]
  948. }
  949. }
  950. }