scopes.rs 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008
  1. use crate::{innerlude::*, unsafe_utils::extend_vnode};
  2. use bumpalo::Bump;
  3. use futures_channel::mpsc::UnboundedSender;
  4. use fxhash::FxHashMap;
  5. use slab::Slab;
  6. use std::{
  7. any::{Any, TypeId},
  8. borrow::Borrow,
  9. cell::{Cell, RefCell},
  10. collections::{HashMap, HashSet},
  11. future::Future,
  12. pin::Pin,
  13. rc::Rc,
  14. sync::Arc,
  15. };
  16. /// for traceability, we use the raw fn pointer to identify the function
  17. /// we also get the component name, but that's not necessarily unique in the app
  18. pub(crate) type ComponentPtr = *mut std::os::raw::c_void;
  19. pub(crate) struct Heuristic {
  20. hook_arena_size: usize,
  21. node_arena_size: usize,
  22. }
  23. // a slab-like arena with stable references even when new scopes are allocated
  24. // uses a bump arena as a backing
  25. //
  26. // has an internal heuristics engine to pre-allocate arenas to the right size
  27. pub(crate) struct ScopeArena {
  28. pub scope_gen: Cell<usize>,
  29. pub bump: Bump,
  30. pub scopes: RefCell<FxHashMap<ScopeId, *mut ScopeState>>,
  31. pub heuristics: RefCell<FxHashMap<ComponentPtr, Heuristic>>,
  32. pub free_scopes: RefCell<Vec<*mut ScopeState>>,
  33. pub nodes: RefCell<Slab<*const VNode<'static>>>,
  34. pub tasks: Rc<TaskQueue>,
  35. pub template_cache: RefCell<HashSet<Template<'static>>>,
  36. }
  37. impl ScopeArena {
  38. pub(crate) fn new(sender: UnboundedSender<SchedulerMsg>) -> Self {
  39. let bump = Bump::new();
  40. // allocate a container for the root element
  41. // this will *never* show up in the diffing process
  42. // todo: figure out why this is necessary. i forgot. whoops.
  43. let el = bump.alloc(VElement {
  44. tag: "root",
  45. namespace: None,
  46. key: None,
  47. id: Cell::new(Some(ElementId(0))),
  48. parent: Default::default(),
  49. listeners: &[],
  50. attributes: &[],
  51. children: &[],
  52. });
  53. let node = bump.alloc(VNode::Element(el));
  54. let mut nodes = Slab::new();
  55. let root_id = nodes.insert(unsafe { std::mem::transmute(node as *const _) });
  56. debug_assert_eq!(root_id, 0);
  57. Self {
  58. scope_gen: Cell::new(0),
  59. bump,
  60. scopes: RefCell::new(FxHashMap::default()),
  61. heuristics: RefCell::new(FxHashMap::default()),
  62. free_scopes: RefCell::new(Vec::new()),
  63. nodes: RefCell::new(nodes),
  64. tasks: Rc::new(TaskQueue {
  65. tasks: RefCell::new(FxHashMap::default()),
  66. task_map: RefCell::new(FxHashMap::default()),
  67. gen: Cell::new(0),
  68. sender,
  69. }),
  70. template_cache: RefCell::new(HashSet::new()),
  71. }
  72. }
  73. /// Safety:
  74. /// - Obtaining a mutable reference to any Scope is unsafe
  75. /// - Scopes use interior mutability when sharing data into components
  76. pub(crate) fn get_scope(&self, id: ScopeId) -> Option<&ScopeState> {
  77. unsafe { self.scopes.borrow().get(&id).map(|f| &**f) }
  78. }
  79. pub(crate) fn get_scope_raw(&self, id: ScopeId) -> Option<*mut ScopeState> {
  80. self.scopes.borrow().get(&id).copied()
  81. }
  82. pub(crate) fn new_with_key(
  83. &self,
  84. fc_ptr: ComponentPtr,
  85. vcomp: Box<dyn AnyProps>,
  86. parent_scope: Option<ScopeId>,
  87. container: ElementId,
  88. subtree: u32,
  89. ) -> ScopeId {
  90. // Increment the ScopeId system. ScopeIDs are never reused
  91. let new_scope_id = ScopeId(self.scope_gen.get());
  92. self.scope_gen.set(self.scope_gen.get() + 1);
  93. // Get the height of the scope
  94. let height = parent_scope
  95. .and_then(|id| self.get_scope(id).map(|scope| scope.height + 1))
  96. .unwrap_or_default();
  97. let parent_scope = parent_scope.and_then(|f| self.get_scope_raw(f));
  98. /*
  99. This scopearena aggressively reuses old scopes when possible.
  100. We try to minimize the new allocations for props/arenas.
  101. However, this will probably lead to some sort of fragmentation.
  102. I'm not exactly sure how to improve this today.
  103. */
  104. if let Some(old_scope) = self.free_scopes.borrow_mut().pop() {
  105. // reuse the old scope
  106. let scope = unsafe { &mut *old_scope };
  107. scope.container = container;
  108. scope.our_arena_idx = new_scope_id;
  109. scope.parent_scope = parent_scope;
  110. scope.height = height;
  111. scope.fnptr = fc_ptr;
  112. scope.props.get_mut().replace(vcomp);
  113. scope.subtree.set(subtree);
  114. scope.frames[0].reset();
  115. scope.frames[1].reset();
  116. scope.shared_contexts.get_mut().clear();
  117. scope.items.get_mut().listeners.clear();
  118. scope.items.get_mut().borrowed_props.clear();
  119. scope.hook_idx.set(0);
  120. scope.hook_vals.get_mut().clear();
  121. let any_item = self.scopes.borrow_mut().insert(new_scope_id, scope);
  122. debug_assert!(any_item.is_none());
  123. } else {
  124. // else create a new scope
  125. let (node_capacity, hook_capacity) = self
  126. .heuristics
  127. .borrow()
  128. .get(&fc_ptr)
  129. .map(|h| (h.node_arena_size, h.hook_arena_size))
  130. .unwrap_or_default();
  131. self.scopes.borrow_mut().insert(
  132. new_scope_id,
  133. self.bump.alloc(ScopeState {
  134. container,
  135. our_arena_idx: new_scope_id,
  136. parent_scope,
  137. height,
  138. fnptr: fc_ptr,
  139. props: RefCell::new(Some(vcomp)),
  140. frames: [BumpFrame::new(node_capacity), BumpFrame::new(node_capacity)],
  141. // todo: subtrees
  142. subtree: Cell::new(0),
  143. is_subtree_root: Cell::default(),
  144. generation: 0.into(),
  145. tasks: self.tasks.clone(),
  146. shared_contexts: RefCell::default(),
  147. items: RefCell::new(SelfReferentialItems {
  148. listeners: Vec::default(),
  149. borrowed_props: Vec::default(),
  150. }),
  151. hook_arena: Bump::new(),
  152. hook_vals: RefCell::new(Vec::with_capacity(hook_capacity)),
  153. hook_idx: Cell::default(),
  154. }),
  155. );
  156. }
  157. new_scope_id
  158. }
  159. // Removes a scope and its descendents from the arena
  160. pub fn try_remove(&self, id: ScopeId) {
  161. self.ensure_drop_safety(id);
  162. // Dispose of any ongoing tasks
  163. let mut tasks = self.tasks.tasks.borrow_mut();
  164. let mut task_map = self.tasks.task_map.borrow_mut();
  165. if let Some(cur_tasks) = task_map.remove(&id) {
  166. for task in cur_tasks {
  167. tasks.remove(&task);
  168. }
  169. }
  170. // Safety:
  171. // - ensure_drop_safety ensures that no references to this scope are in use
  172. // - this raw pointer is removed from the map
  173. let scope = unsafe { &mut *self.scopes.borrow_mut().remove(&id).unwrap() };
  174. scope.reset();
  175. self.free_scopes.borrow_mut().push(scope);
  176. }
  177. pub fn reserve_node<'a>(&self, node: &'a VNode<'a>) -> ElementId {
  178. let mut els = self.nodes.borrow_mut();
  179. let entry = els.vacant_entry();
  180. let key = entry.key();
  181. let id = ElementId(key);
  182. let node = unsafe { extend_vnode(node) };
  183. entry.insert(node as *const _);
  184. id
  185. }
  186. pub fn update_node<'a>(&self, node: &'a VNode<'a>, id: ElementId) {
  187. let node = unsafe { extend_vnode(node) };
  188. *self.nodes.borrow_mut().get_mut(id.0).unwrap() = node;
  189. }
  190. pub fn collect_garbage(&self, id: ElementId) {
  191. self.nodes.borrow_mut().remove(id.0);
  192. }
  193. /// This method cleans up any references to data held within our hook list. This prevents mutable aliasing from
  194. /// causing UB in our tree.
  195. ///
  196. /// This works by cleaning up our references from the bottom of the tree to the top. The directed graph of components
  197. /// essentially forms a dependency tree that we can traverse from the bottom to the top. As we traverse, we remove
  198. /// any possible references to the data in the hook list.
  199. ///
  200. /// References to hook data can only be stored in listeners and component props. During diffing, we make sure to log
  201. /// all listeners and borrowed props so we can clear them here.
  202. ///
  203. /// This also makes sure that drop order is consistent and predictable. All resources that rely on being dropped will
  204. /// be dropped.
  205. pub(crate) fn ensure_drop_safety(&self, scope_id: ScopeId) {
  206. if let Some(scope) = self.get_scope(scope_id) {
  207. let mut items = scope.items.borrow_mut();
  208. // make sure we drop all borrowed props manually to guarantee that their drop implementation is called before we
  209. // run the hooks (which hold an &mut Reference)
  210. // recursively call ensure_drop_safety on all children
  211. items.borrowed_props.drain(..).for_each(|comp| {
  212. if let Some(scope_id) = comp.scope.get() {
  213. self.ensure_drop_safety(scope_id);
  214. }
  215. drop(comp.props.take());
  216. });
  217. // Now that all the references are gone, we can safely drop our own references in our listeners.
  218. items
  219. .listeners
  220. .drain(..)
  221. .for_each(|listener| drop(listener.callback.borrow_mut().take()));
  222. }
  223. }
  224. pub(crate) fn run_scope(&self, id: ScopeId) {
  225. // Cycle to the next frame and then reset it
  226. // This breaks any latent references, invalidating every pointer referencing into it.
  227. // Remove all the outdated listeners
  228. self.ensure_drop_safety(id);
  229. // todo: we *know* that this is aliased by the contents of the scope itself
  230. let scope = unsafe { &mut *self.get_scope_raw(id).expect("could not find scope") };
  231. log::trace!("running scope {:?} symbol: {:?}", id, scope.fnptr);
  232. // Safety:
  233. // - We dropped the listeners, so no more &mut T can be used while these are held
  234. // - All children nodes that rely on &mut T are replaced with a new reference
  235. scope.hook_idx.set(0);
  236. {
  237. // Safety:
  238. // - We've dropped all references to the wip bump frame with "ensure_drop_safety"
  239. unsafe { scope.reset_wip_frame() };
  240. let items = scope.items.borrow();
  241. // guarantee that we haven't screwed up - there should be no latent references anywhere
  242. debug_assert!(items.listeners.is_empty());
  243. debug_assert!(items.borrowed_props.is_empty());
  244. }
  245. /*
  246. If the component returns None, then we fill in a placeholder node. This will wipe what was there.
  247. An alternate approach is to leave the Real Dom the same, but that can lead to safety issues and a lot more checks.
  248. Instead, we just treat the `None` as a shortcut to placeholder.
  249. If the developer wants to prevent a scope from updating, they should control its memoization instead.
  250. Also, the way we implement hooks allows us to cut rendering short before the next hook is recalled.
  251. I'm not sure if React lets you abort the component early, but we let you do that.
  252. */
  253. let props = scope.props.borrow();
  254. let render = props.as_ref().unwrap();
  255. if let Some(node) = render.render(scope) {
  256. let frame = scope.wip_frame();
  257. let node = frame.bump.alloc(node);
  258. frame.node.set(unsafe { extend_vnode(node) });
  259. } else {
  260. let frame = scope.wip_frame();
  261. let node = frame.bump.alloc(VNode::Text(frame.bump.alloc(VText {
  262. id: Cell::default(),
  263. text: "asd",
  264. })));
  265. frame.node.set(unsafe { extend_vnode(node) });
  266. }
  267. // make the "wip frame" contents the "finished frame"
  268. // any future dipping into completed nodes after "render" will go through "fin head"
  269. scope.cycle_frame();
  270. }
  271. pub fn call_listener_with_bubbling(&self, event: &UserEvent, element: ElementId) {
  272. let nodes = self.nodes.borrow();
  273. let mut cur_el = Some(element);
  274. let state = Rc::new(BubbleState::new());
  275. while let Some(id) = cur_el.take() {
  276. if let Some(el) = nodes.get(id.0) {
  277. let real_el = unsafe { &**el };
  278. if let VNode::Element(real_el) = real_el {
  279. for listener in real_el.listeners.borrow().iter() {
  280. if listener.event == event.name {
  281. if state.canceled.get() {
  282. // stop bubbling if canceled
  283. return;
  284. }
  285. let mut cb = listener.callback.borrow_mut();
  286. if let Some(cb) = cb.as_mut() {
  287. // todo: arcs are pretty heavy to clone
  288. // we really want to convert arc to rc
  289. // unfortunately, the SchedulerMsg must be send/sync to be sent across threads
  290. // we could convert arc to rc internally or something
  291. (cb)(AnyEvent {
  292. bubble_state: state.clone(),
  293. data: event.data.clone(),
  294. });
  295. }
  296. if !event.bubbles {
  297. return;
  298. }
  299. }
  300. }
  301. cur_el = real_el.parent.get();
  302. }
  303. }
  304. }
  305. }
  306. // The head of the bumpframe is the first linked NodeLink
  307. pub fn wip_head(&self, id: ScopeId) -> &VNode {
  308. let scope = self.get_scope(id).unwrap();
  309. let frame = scope.wip_frame();
  310. let node = unsafe { &*frame.node.get() };
  311. unsafe { extend_vnode(node) }
  312. }
  313. // The head of the bumpframe is the first linked NodeLink
  314. pub fn fin_head(&self, id: ScopeId) -> &VNode {
  315. let scope = self.get_scope(id).unwrap();
  316. let frame = scope.fin_frame();
  317. let node = unsafe { &*frame.node.get() };
  318. unsafe { extend_vnode(node) }
  319. }
  320. pub fn root_node(&self, id: ScopeId) -> &VNode {
  321. self.fin_head(id)
  322. }
  323. // this is totally okay since all our nodes are always in a valid state
  324. pub fn get_element(&self, id: ElementId) -> Option<&VNode> {
  325. self.nodes
  326. .borrow()
  327. .get(id.0)
  328. .copied()
  329. .map(|ptr| unsafe { extend_vnode(&*ptr) })
  330. }
  331. }
  332. /// Components in Dioxus use the "Context" object to interact with their lifecycle.
  333. ///
  334. /// This lets components access props, schedule updates, integrate hooks, and expose shared state.
  335. ///
  336. /// For the most part, the only method you should be using regularly is `render`.
  337. ///
  338. /// ## Example
  339. ///
  340. /// ```ignore
  341. /// #[derive(Props)]
  342. /// struct ExampleProps {
  343. /// name: String
  344. /// }
  345. ///
  346. /// fn Example(cx: Scope<ExampleProps>) -> Element {
  347. /// cx.render(rsx!{ div {"Hello, {cx.props.name}"} })
  348. /// }
  349. /// ```
  350. pub struct Scope<'a, P = ()> {
  351. /// The internal ScopeState for this component
  352. pub scope: &'a ScopeState,
  353. /// The props for this component
  354. pub props: &'a P,
  355. }
  356. impl<P> Copy for Scope<'_, P> {}
  357. impl<P> Clone for Scope<'_, P> {
  358. fn clone(&self) -> Self {
  359. Self {
  360. scope: self.scope,
  361. props: self.props,
  362. }
  363. }
  364. }
  365. impl<'a, P> std::ops::Deref for Scope<'a, P> {
  366. type Target = &'a ScopeState;
  367. fn deref(&self) -> &Self::Target {
  368. &self.scope
  369. }
  370. }
  371. /// A component's unique identifier.
  372. ///
  373. /// `ScopeId` is a `usize` that is unique across the entire [`VirtualDom`] and across time. [`ScopeID`]s will never be reused
  374. /// once a component has been unmounted.
  375. #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
  376. #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
  377. pub struct ScopeId(pub usize);
  378. /// A task's unique identifier.
  379. ///
  380. /// `TaskId` is a `usize` that is unique across the entire [`VirtualDom`] and across time. [`TaskID`]s will never be reused
  381. /// once a Task has been completed.
  382. #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
  383. #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
  384. pub struct TaskId {
  385. /// The global ID of the task
  386. pub id: usize,
  387. /// The original scope that this task was scheduled in
  388. pub scope: ScopeId,
  389. }
  390. /// Every component in Dioxus is represented by a `ScopeState`.
  391. ///
  392. /// Scopes contain the state for hooks, the component's props, and other lifecycle information.
  393. ///
  394. /// Scopes are allocated in a generational arena. As components are mounted/unmounted, they will replace slots of dead components.
  395. /// The actual contents of the hooks, though, will be allocated with the standard allocator. These should not allocate as frequently.
  396. ///
  397. /// We expose the `Scope` type so downstream users can traverse the Dioxus [`VirtualDom`] for whatever
  398. /// use case they might have.
  399. pub struct ScopeState {
  400. pub(crate) parent_scope: Option<*mut ScopeState>,
  401. pub(crate) container: ElementId,
  402. pub(crate) our_arena_idx: ScopeId,
  403. pub(crate) height: u32,
  404. pub(crate) fnptr: ComponentPtr,
  405. // todo: subtrees
  406. pub(crate) is_subtree_root: Cell<bool>,
  407. pub(crate) subtree: Cell<u32>,
  408. pub(crate) props: RefCell<Option<Box<dyn AnyProps>>>,
  409. // nodes, items
  410. pub(crate) frames: [BumpFrame; 2],
  411. pub(crate) generation: Cell<u32>,
  412. pub(crate) items: RefCell<SelfReferentialItems<'static>>,
  413. // hooks
  414. pub(crate) hook_arena: Bump,
  415. pub(crate) hook_vals: RefCell<Vec<*mut dyn Any>>,
  416. pub(crate) hook_idx: Cell<usize>,
  417. // shared state -> todo: move this out of scopestate
  418. pub(crate) shared_contexts: RefCell<HashMap<TypeId, Box<dyn Any>>>,
  419. pub(crate) tasks: Rc<TaskQueue>,
  420. }
  421. pub struct SelfReferentialItems<'a> {
  422. pub(crate) listeners: Vec<&'a Listener<'a>>,
  423. pub(crate) borrowed_props: Vec<&'a VComponent<'a>>,
  424. }
  425. // Public methods exposed to libraries and components
  426. impl ScopeState {
  427. /// Get the subtree ID that this scope belongs to.
  428. ///
  429. /// Each component has its own subtree ID - the root subtree has an ID of 0. This ID is used by the renderer to route
  430. /// the mutations to the correct window/portal/subtree.
  431. ///
  432. ///
  433. /// # Example
  434. ///
  435. /// ```rust, ignore
  436. /// let mut dom = VirtualDom::new(|cx| cx.render(rsx!{ div {} }));
  437. /// dom.rebuild();
  438. ///
  439. /// let base = dom.base_scope();
  440. ///
  441. /// assert_eq!(base.subtree(), 0);
  442. /// ```
  443. ///
  444. /// todo: enable
  445. pub(crate) fn _subtree(&self) -> u32 {
  446. self.subtree.get()
  447. }
  448. /// Create a new subtree with this scope as the root of the subtree.
  449. ///
  450. /// Each component has its own subtree ID - the root subtree has an ID of 0. This ID is used by the renderer to route
  451. /// the mutations to the correct window/portal/subtree.
  452. ///
  453. /// This method
  454. ///
  455. /// # Example
  456. ///
  457. /// ```rust, ignore
  458. /// fn App(cx: Scope) -> Element {
  459. /// render!(div { "Subtree {id}"})
  460. /// };
  461. /// ```
  462. ///
  463. /// todo: enable subtree
  464. pub(crate) fn _create_subtree(&self) -> Option<u32> {
  465. if self.is_subtree_root.get() {
  466. None
  467. } else {
  468. todo!()
  469. }
  470. }
  471. /// Get the height of this Scope - IE the number of scopes above it.
  472. ///
  473. /// A Scope with a height of `0` is the root scope - there are no other scopes above it.
  474. ///
  475. /// # Example
  476. ///
  477. /// ```rust, ignore
  478. /// let mut dom = VirtualDom::new(|cx| cx.render(rsx!{ div {} }));
  479. /// dom.rebuild();
  480. ///
  481. /// let base = dom.base_scope();
  482. ///
  483. /// assert_eq!(base.height(), 0);
  484. /// ```
  485. pub fn height(&self) -> u32 {
  486. self.height
  487. }
  488. /// Get the Parent of this [`Scope`] within this Dioxus [`VirtualDom`].
  489. ///
  490. /// This ID is not unique across Dioxus [`VirtualDom`]s or across time. IDs will be reused when components are unmounted.
  491. ///
  492. /// The base component will not have a parent, and will return `None`.
  493. ///
  494. /// # Example
  495. ///
  496. /// ```rust, ignore
  497. /// let mut dom = VirtualDom::new(|cx| cx.render(rsx!{ div {} }));
  498. /// dom.rebuild();
  499. ///
  500. /// let base = dom.base_scope();
  501. ///
  502. /// assert_eq!(base.parent(), None);
  503. /// ```
  504. pub fn parent(&self) -> Option<ScopeId> {
  505. // safety: the pointer to our parent is *always* valid thanks to the bump arena
  506. self.parent_scope.map(|p| unsafe { &*p }.our_arena_idx)
  507. }
  508. /// Get the ID of this Scope within this Dioxus [`VirtualDom`].
  509. ///
  510. /// This ID is not unique across Dioxus [`VirtualDom`]s or across time. IDs will be reused when components are unmounted.
  511. ///
  512. /// # Example
  513. ///
  514. /// ```rust, ignore
  515. /// let mut dom = VirtualDom::new(|cx| cx.render(rsx!{ div {} }));
  516. /// dom.rebuild();
  517. /// let base = dom.base_scope();
  518. ///
  519. /// assert_eq!(base.scope_id(), 0);
  520. /// ```
  521. pub fn scope_id(&self) -> ScopeId {
  522. self.our_arena_idx
  523. }
  524. /// Get a handle to the raw update scheduler channel
  525. pub fn scheduler_channel(&self) -> UnboundedSender<SchedulerMsg> {
  526. self.tasks.sender.clone()
  527. }
  528. /// Create a subscription that schedules a future render for the reference component
  529. ///
  530. /// ## Notice: you should prefer using [`schedule_update_any`] and [`scope_id`]
  531. pub fn schedule_update(&self) -> Arc<dyn Fn() + Send + Sync + 'static> {
  532. let (chan, id) = (self.tasks.sender.clone(), self.scope_id());
  533. Arc::new(move || drop(chan.unbounded_send(SchedulerMsg::Immediate(id))))
  534. }
  535. /// Schedule an update for any component given its [`ScopeId`].
  536. ///
  537. /// A component's [`ScopeId`] can be obtained from `use_hook` or the [`ScopeState::scope_id`] method.
  538. ///
  539. /// This method should be used when you want to schedule an update for a component
  540. pub fn schedule_update_any(&self) -> Arc<dyn Fn(ScopeId) + Send + Sync> {
  541. let chan = self.tasks.sender.clone();
  542. Arc::new(move |id| drop(chan.unbounded_send(SchedulerMsg::Immediate(id))))
  543. }
  544. pub fn needs_update(&self) {
  545. self.needs_update_any(self.scope_id());
  546. }
  547. /// Get the [`ScopeId`] of a mounted component.
  548. ///
  549. /// `ScopeId` is not unique for the lifetime of the [`VirtualDom`] - a [`ScopeId`] will be reused if a component is unmounted.
  550. pub fn needs_update_any(&self, id: ScopeId) {
  551. self.tasks
  552. .sender
  553. .unbounded_send(SchedulerMsg::Immediate(id))
  554. .expect("Scheduler to exist if scope exists");
  555. }
  556. /// Get the Root Node of this scope
  557. pub fn root_node(&self) -> &VNode {
  558. let node = unsafe { &*self.fin_frame().node.get() };
  559. unsafe { std::mem::transmute(node) }
  560. }
  561. /// This method enables the ability to expose state to children further down the [`VirtualDom`] Tree.
  562. ///
  563. /// This is a "fundamental" operation and should only be called during initialization of a hook.
  564. ///
  565. /// For a hook that provides the same functionality, use `use_provide_context` and `use_consume_context` instead.
  566. ///
  567. /// When the component is dropped, so is the context. Be aware of this behavior when consuming
  568. /// the context via Rc/Weak.
  569. ///
  570. /// # Example
  571. ///
  572. /// ```rust, ignore
  573. /// struct SharedState(&'static str);
  574. ///
  575. /// static App: Component = |cx| {
  576. /// cx.use_hook(|| cx.provide_context(SharedState("world")));
  577. /// render!(Child {})
  578. /// }
  579. ///
  580. /// static Child: Component = |cx| {
  581. /// let state = cx.consume_state::<SharedState>();
  582. /// render!(div { "hello {state.0}" })
  583. /// }
  584. /// ```
  585. pub fn provide_context<T: 'static + Clone>(&self, value: T) -> T {
  586. self.shared_contexts
  587. .borrow_mut()
  588. .insert(TypeId::of::<T>(), Box::new(value.clone()))
  589. .and_then(|f| f.downcast::<T>().ok());
  590. value
  591. }
  592. /// Provide a context for the root component from anywhere in your app.
  593. ///
  594. ///
  595. /// # Example
  596. ///
  597. /// ```rust, ignore
  598. /// struct SharedState(&'static str);
  599. ///
  600. /// static App: Component = |cx| {
  601. /// cx.use_hook(|| cx.provide_root_context(SharedState("world")));
  602. /// render!(Child {})
  603. /// }
  604. ///
  605. /// static Child: Component = |cx| {
  606. /// let state = cx.consume_state::<SharedState>();
  607. /// render!(div { "hello {state.0}" })
  608. /// }
  609. /// ```
  610. pub fn provide_root_context<T: 'static + Clone>(&self, value: T) -> T {
  611. // if we *are* the root component, then we can just provide the context directly
  612. if self.scope_id() == ScopeId(0) {
  613. self.shared_contexts
  614. .borrow_mut()
  615. .insert(TypeId::of::<T>(), Box::new(value.clone()))
  616. .and_then(|f| f.downcast::<T>().ok());
  617. return value;
  618. }
  619. let mut search_parent = self.parent_scope;
  620. while let Some(parent) = search_parent.take() {
  621. let parent = unsafe { &*parent };
  622. if parent.scope_id() == ScopeId(0) {
  623. let exists = parent
  624. .shared_contexts
  625. .borrow_mut()
  626. .insert(TypeId::of::<T>(), Box::new(value.clone()));
  627. if exists.is_some() {
  628. log::warn!("Context already provided to parent scope - replacing it");
  629. }
  630. return value;
  631. }
  632. search_parent = parent.parent_scope;
  633. }
  634. unreachable!("all apps have a root scope")
  635. }
  636. /// Try to retrieve a shared state with type T from the any parent Scope.
  637. pub fn consume_context<T: 'static + Clone>(&self) -> Option<T> {
  638. if let Some(shared) = self.shared_contexts.borrow().get(&TypeId::of::<T>()) {
  639. Some(
  640. (*shared
  641. .downcast_ref::<T>()
  642. .expect("Context of type T should exist"))
  643. .clone(),
  644. )
  645. } else {
  646. let mut search_parent = self.parent_scope;
  647. while let Some(parent_ptr) = search_parent {
  648. // safety: all parent pointers are valid thanks to the bump arena
  649. let parent = unsafe { &*parent_ptr };
  650. if let Some(shared) = parent.shared_contexts.borrow().get(&TypeId::of::<T>()) {
  651. return Some(
  652. shared
  653. .downcast_ref::<T>()
  654. .expect("Context of type T should exist")
  655. .clone(),
  656. );
  657. }
  658. search_parent = parent.parent_scope;
  659. }
  660. None
  661. }
  662. }
  663. /// Pushes the future onto the poll queue to be polled after the component renders.
  664. pub fn push_future(&self, fut: impl Future<Output = ()> + 'static) -> TaskId {
  665. // wake up the scheduler if it is sleeping
  666. self.tasks
  667. .sender
  668. .unbounded_send(SchedulerMsg::NewTask(self.our_arena_idx))
  669. .expect("Scheduler should exist");
  670. self.tasks.spawn(self.our_arena_idx, fut)
  671. }
  672. /// Spawns the future but does not return the [`TaskId`]
  673. pub fn spawn(&self, fut: impl Future<Output = ()> + 'static) {
  674. self.push_future(fut);
  675. }
  676. /// Spawn a future that Dioxus will never clean up
  677. ///
  678. /// This is good for tasks that need to be run after the component has been dropped.
  679. pub fn spawn_forever(&self, fut: impl Future<Output = ()> + 'static) -> TaskId {
  680. // wake up the scheduler if it is sleeping
  681. self.tasks
  682. .sender
  683. .unbounded_send(SchedulerMsg::NewTask(self.our_arena_idx))
  684. .expect("Scheduler should exist");
  685. // The root scope will never be unmounted so we can just add the task at the top of the app
  686. self.tasks.spawn(ScopeId(0), fut)
  687. }
  688. /// Informs the scheduler that this task is no longer needed and should be removed
  689. /// on next poll.
  690. pub fn remove_future(&self, id: TaskId) {
  691. self.tasks.remove(id);
  692. }
  693. /// Take a lazy [`VNode`] structure and actually build it with the context of the Vdoms efficient [`VNode`] allocator.
  694. ///
  695. /// ## Example
  696. ///
  697. /// ```ignore
  698. /// fn Component(cx: Scope<Props>) -> Element {
  699. /// // Lazy assemble the VNode tree
  700. /// let lazy_nodes = rsx!("hello world");
  701. ///
  702. /// // Actually build the tree and allocate it
  703. /// cx.render(lazy_tree)
  704. /// }
  705. ///```
  706. pub fn render<'src>(&'src self, rsx: LazyNodes<'src, '_>) -> Option<VNode<'src>> {
  707. Some(rsx.call(NodeFactory {
  708. scope: self,
  709. bump: &self.wip_frame().bump,
  710. }))
  711. }
  712. /// Store a value between renders. The foundational hook for all other hooks.
  713. ///
  714. /// Accepts an `initializer` closure, which is run on the first use of the hook (typically the initial render). The return value of this closure is stored for the lifetime of the component, and a mutable reference to it is provided on every render as the return value of `use_hook`.
  715. ///
  716. /// When the component is unmounted (removed from the UI), the value is dropped. This means you can return a custom type and provide cleanup code by implementing the [`Drop`] trait
  717. ///
  718. /// # Example
  719. ///
  720. /// ```
  721. /// use dioxus_core::ScopeState;
  722. ///
  723. /// // prints a greeting on the initial render
  724. /// pub fn use_hello_world(cx: &ScopeState) {
  725. /// cx.use_hook(|| println!("Hello, world!"));
  726. /// }
  727. /// ```
  728. #[allow(clippy::mut_from_ref)]
  729. pub fn use_hook<State: 'static>(&self, initializer: impl FnOnce() -> State) -> &mut State {
  730. let mut vals = self.hook_vals.borrow_mut();
  731. let hook_len = vals.len();
  732. let cur_idx = self.hook_idx.get();
  733. if cur_idx >= hook_len {
  734. vals.push(self.hook_arena.alloc(initializer()));
  735. }
  736. vals
  737. .get(cur_idx)
  738. .and_then(|inn| {
  739. self.hook_idx.set(cur_idx + 1);
  740. let raw_box = unsafe { &mut **inn };
  741. raw_box.downcast_mut::<State>()
  742. })
  743. .expect(
  744. r###"
  745. Unable to retrieve the hook that was initialized at this index.
  746. Consult the `rules of hooks` to understand how to use hooks properly.
  747. You likely used the hook in a conditional. Hooks rely on consistent ordering between renders.
  748. Functions prefixed with "use" should never be called conditionally.
  749. "###,
  750. )
  751. }
  752. /// The "work in progress frame" represents the frame that is currently being worked on.
  753. pub(crate) fn wip_frame(&self) -> &BumpFrame {
  754. match self.generation.get() & 1 {
  755. 0 => &self.frames[0],
  756. _ => &self.frames[1],
  757. }
  758. }
  759. /// Mutable access to the "work in progress frame" - used to clear it
  760. pub(crate) fn wip_frame_mut(&mut self) -> &mut BumpFrame {
  761. match self.generation.get() & 1 {
  762. 0 => &mut self.frames[0],
  763. _ => &mut self.frames[1],
  764. }
  765. }
  766. /// Access to the frame where finalized nodes existed
  767. pub(crate) fn fin_frame(&self) -> &BumpFrame {
  768. match self.generation.get() & 1 {
  769. 1 => &self.frames[0],
  770. _ => &self.frames[1],
  771. }
  772. }
  773. /// Reset this component's frame
  774. ///
  775. /// # Safety:
  776. ///
  777. /// This method breaks every reference of every [`VNode`] in the current frame.
  778. ///
  779. /// Calling reset itself is not usually a big deal, but we consider it important
  780. /// due to the complex safety guarantees we need to uphold.
  781. pub(crate) unsafe fn reset_wip_frame(&mut self) {
  782. self.wip_frame_mut().bump.reset();
  783. }
  784. /// Cycle to the next generation
  785. pub(crate) fn cycle_frame(&self) {
  786. self.generation.set(self.generation.get() + 1);
  787. }
  788. // todo: disable bookkeeping on drop (unncessary)
  789. pub(crate) fn reset(&mut self) {
  790. // first: book keaping
  791. self.hook_idx.set(0);
  792. self.parent_scope = None;
  793. self.generation.set(0);
  794. self.is_subtree_root.set(false);
  795. self.subtree.set(0);
  796. // next: shared context data
  797. self.shared_contexts.get_mut().clear();
  798. // next: reset the node data
  799. let SelfReferentialItems {
  800. borrowed_props,
  801. listeners,
  802. } = self.items.get_mut();
  803. borrowed_props.clear();
  804. listeners.clear();
  805. self.frames[0].reset();
  806. self.frames[1].reset();
  807. // Free up the hook values
  808. self.hook_vals.get_mut().drain(..).for_each(|state| {
  809. let as_mut = unsafe { &mut *state };
  810. let boxed = unsafe { bumpalo::boxed::Box::from_raw(as_mut) };
  811. drop(boxed);
  812. });
  813. // Finally, clear the hook arena
  814. self.hook_arena.reset();
  815. }
  816. }
  817. pub(crate) struct BumpFrame {
  818. pub bump: Bump,
  819. pub node: Cell<*const VNode<'static>>,
  820. }
  821. impl BumpFrame {
  822. pub(crate) fn new(capacity: usize) -> Self {
  823. let bump = Bump::with_capacity(capacity);
  824. let node = bump.alloc(VText {
  825. text: "placeholdertext",
  826. id: Cell::default(),
  827. });
  828. let node = bump.alloc(VNode::Text(unsafe {
  829. &*(node as *mut VText as *const VText)
  830. }));
  831. let nodes = Cell::new(node as *const _);
  832. Self { bump, node: nodes }
  833. }
  834. pub(crate) fn reset(&mut self) {
  835. self.bump.reset();
  836. let node = self.bump.alloc(VText {
  837. text: "placeholdertext",
  838. id: Cell::default(),
  839. });
  840. let node = self.bump.alloc(VNode::Text(unsafe {
  841. &*(node as *mut VText as *const VText)
  842. }));
  843. self.node.set(node as *const _);
  844. }
  845. }
  846. pub(crate) struct TaskQueue {
  847. pub(crate) tasks: RefCell<FxHashMap<TaskId, InnerTask>>,
  848. pub(crate) task_map: RefCell<FxHashMap<ScopeId, HashSet<TaskId>>>,
  849. gen: Cell<usize>,
  850. sender: UnboundedSender<SchedulerMsg>,
  851. }
  852. pub(crate) type InnerTask = Pin<Box<dyn Future<Output = ()>>>;
  853. impl TaskQueue {
  854. fn spawn(&self, scope: ScopeId, task: impl Future<Output = ()> + 'static) -> TaskId {
  855. let pinned = Box::pin(task);
  856. let id = self.gen.get();
  857. self.gen.set(id + 1);
  858. let tid = TaskId { id, scope };
  859. self.tasks.borrow_mut().insert(tid, pinned);
  860. // also add to the task map
  861. // when the component is unmounted we know to remove it from the map
  862. self.task_map
  863. .borrow_mut()
  864. .entry(scope)
  865. .or_default()
  866. .insert(tid);
  867. tid
  868. }
  869. fn remove(&self, id: TaskId) {
  870. if let Ok(mut tasks) = self.tasks.try_borrow_mut() {
  871. tasks.remove(&id);
  872. if let Some(task_map) = self.task_map.borrow_mut().get_mut(&id.scope) {
  873. task_map.remove(&id);
  874. }
  875. }
  876. // the task map is still around, but it'll be removed when the scope is unmounted
  877. }
  878. pub(crate) fn has_tasks(&self) -> bool {
  879. !self.tasks.borrow().is_empty()
  880. }
  881. }
  882. #[test]
  883. fn sizeof() {
  884. dbg!(std::mem::size_of::<ScopeState>());
  885. }