scopes.rs 31 KB

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