lazynodes.rs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. //! Support for storing lazy-nodes on the stack
  2. //!
  3. //! This module provides support for a type called `LazyNodes` which is a micro-heap located on the stack to make calls
  4. //! to `rsx!` more efficient.
  5. //!
  6. //! To support returning rsx! from branches in match statements, we need to use dynamic dispatch on [`NodeFactory`] closures.
  7. //!
  8. //! This can be done either through boxing directly, or by using dynamic-sized-types and a custom allocator. In our case,
  9. //! we build a tiny alloactor in the stack and allocate the closure into that.
  10. //!
  11. //! The logic for this was borrowed from <https://docs.rs/stack_dst/0.6.1/stack_dst/>. Unfortunately, this crate does not
  12. //! support non-static closures, so we've implemented the core logic of `ValueA` in this module.
  13. use crate::{innerlude::VNode, ScopeState};
  14. use std::mem;
  15. /// A concrete type provider for closures that build [`VNode`] structures.
  16. ///
  17. /// This struct wraps lazy structs that build [`VNode`] trees Normally, we cannot perform a blanket implementation over
  18. /// closures, but if we wrap the closure in a concrete type, we can maintain separate implementations of [`IntoVNode`].
  19. ///
  20. ///
  21. /// ```rust, ignore
  22. /// LazyNodes::new(|f| f.element("div", [], [], [] None))
  23. /// ```
  24. pub struct LazyNodes<'a, 'b> {
  25. inner: StackNodeStorage<'a, 'b>,
  26. }
  27. pub type NodeFactory<'a> = &'a ScopeState;
  28. type StackHeapSize = [usize; 16];
  29. enum StackNodeStorage<'a, 'b> {
  30. Stack(LazyStack),
  31. Heap(Box<dyn FnMut(Option<NodeFactory<'a>>) -> Option<VNode<'a>> + 'b>),
  32. }
  33. impl<'a, 'b> LazyNodes<'a, 'b> {
  34. /// Create a new [`LazyNodes`] closure, optimistically placing it onto the stack.
  35. ///
  36. /// If the closure cannot fit into the stack allocation (16 bytes), then it
  37. /// is placed on the heap. Most closures will fit into the stack, and is
  38. /// the most optimal way to use the creation function.
  39. pub fn new(val: impl FnOnce(NodeFactory<'a>) -> VNode<'a> + 'b) -> Self {
  40. // there's no way to call FnOnce without a box, so we need to store it in a slot and use static dispatch
  41. let mut slot = Some(val);
  42. let val = move |fac: Option<NodeFactory<'a>>| {
  43. fac.map(
  44. slot.take()
  45. .expect("LazyNodes closure to be called only once"),
  46. )
  47. };
  48. // miri does not know how to work with mucking directly into bytes
  49. // just use a heap allocated type when miri is running
  50. if cfg!(miri) {
  51. Self {
  52. inner: StackNodeStorage::Heap(Box::new(val)),
  53. }
  54. } else {
  55. unsafe { LazyNodes::new_inner(val) }
  56. }
  57. }
  58. /// Create a new [`LazyNodes`] closure, but force it onto the heap.
  59. pub fn new_boxed<F>(inner: F) -> Self
  60. where
  61. F: FnOnce(NodeFactory<'a>) -> VNode<'a> + 'b,
  62. {
  63. // there's no way to call FnOnce without a box, so we need to store it in a slot and use static dispatch
  64. let mut slot = Some(inner);
  65. Self {
  66. inner: StackNodeStorage::Heap(Box::new(move |fac: Option<NodeFactory<'a>>| {
  67. fac.map(
  68. slot.take()
  69. .expect("LazyNodes closure to be called only once"),
  70. )
  71. })),
  72. }
  73. }
  74. unsafe fn new_inner<F>(val: F) -> Self
  75. where
  76. F: FnMut(Option<NodeFactory<'a>>) -> Option<VNode<'a>> + 'b,
  77. {
  78. let mut ptr: *const _ = &val as &dyn FnMut(Option<NodeFactory<'a>>) -> Option<VNode<'a>>;
  79. assert_eq!(
  80. ptr as *const u8, &val as *const _ as *const u8,
  81. "MISUSE: Closure returned different pointer"
  82. );
  83. assert_eq!(
  84. std::mem::size_of_val(&*ptr),
  85. std::mem::size_of::<F>(),
  86. "MISUSE: Closure returned a subset pointer"
  87. );
  88. let words = ptr_as_slice(&mut ptr);
  89. assert!(
  90. words[0] == &val as *const _ as usize,
  91. "BUG: Pointer layout is not (data_ptr, info...)"
  92. );
  93. // - Ensure that Self is aligned same as data requires
  94. assert!(
  95. std::mem::align_of::<F>() <= std::mem::align_of::<Self>(),
  96. "TODO: Enforce alignment >{} (requires {})",
  97. std::mem::align_of::<Self>(),
  98. std::mem::align_of::<F>()
  99. );
  100. let info = &words[1..];
  101. let data = words[0] as *mut ();
  102. let size = mem::size_of::<F>();
  103. let stored_size = info.len() * mem::size_of::<usize>() + size;
  104. let max_size = mem::size_of::<StackHeapSize>();
  105. if stored_size > max_size {
  106. Self {
  107. inner: StackNodeStorage::Heap(Box::new(val)),
  108. }
  109. } else {
  110. let mut buf: StackHeapSize = StackHeapSize::default();
  111. assert!(info.len() + round_to_words(size) <= buf.as_ref().len());
  112. // Place pointer information at the end of the region
  113. // - Allows the data to be at the start for alignment purposes
  114. {
  115. let info_ofs = buf.as_ref().len() - info.len();
  116. let info_dst = &mut buf.as_mut()[info_ofs..];
  117. for (d, v) in Iterator::zip(info_dst.iter_mut(), info.iter()) {
  118. *d = *v;
  119. }
  120. }
  121. let src_ptr = data as *const u8;
  122. let dataptr = buf.as_mut_ptr().cast::<u8>();
  123. for i in 0..size {
  124. *dataptr.add(i) = *src_ptr.add(i);
  125. }
  126. std::mem::forget(val);
  127. Self {
  128. inner: StackNodeStorage::Stack(LazyStack {
  129. _align: [],
  130. buf,
  131. dropped: false,
  132. }),
  133. }
  134. }
  135. }
  136. /// Call the closure with the given factory to produce real [`VNode`].
  137. ///
  138. /// ```rust, ignore
  139. /// let f = LazyNodes::new(move |f| f.element("div", [], [], [] None));
  140. ///
  141. /// let fac = NodeFactory::new(&cx);
  142. ///
  143. /// let node = f.call(cac);
  144. /// ```
  145. #[must_use]
  146. pub fn call(self, f: NodeFactory<'a>) -> VNode<'a> {
  147. match self.inner {
  148. StackNodeStorage::Heap(mut lazy) => {
  149. lazy(Some(f)).expect("Closure should not be called twice")
  150. }
  151. StackNodeStorage::Stack(mut stack) => stack.call(f),
  152. }
  153. }
  154. }
  155. struct LazyStack {
  156. _align: [u64; 0],
  157. buf: StackHeapSize,
  158. dropped: bool,
  159. }
  160. impl LazyStack {
  161. fn call<'a>(&mut self, f: NodeFactory<'a>) -> VNode<'a> {
  162. let LazyStack { buf, .. } = self;
  163. let data = buf.as_ref();
  164. let info_size =
  165. mem::size_of::<*mut dyn FnMut(Option<NodeFactory<'a>>) -> Option<VNode<'a>>>()
  166. / mem::size_of::<usize>()
  167. - 1;
  168. let info_ofs = data.len() - info_size;
  169. let g: *mut dyn FnMut(Option<NodeFactory<'a>>) -> Option<VNode<'a>> =
  170. unsafe { make_fat_ptr(data[..].as_ptr() as usize, &data[info_ofs..]) };
  171. self.dropped = true;
  172. let clos = unsafe { &mut *g };
  173. clos(Some(f)).unwrap()
  174. }
  175. }
  176. impl Drop for LazyStack {
  177. fn drop(&mut self) {
  178. if !self.dropped {
  179. let LazyStack { buf, .. } = self;
  180. let data = buf.as_ref();
  181. let info_size = mem::size_of::<
  182. *mut dyn FnMut(Option<NodeFactory<'_>>) -> Option<VNode<'_>>,
  183. >() / mem::size_of::<usize>()
  184. - 1;
  185. let info_ofs = data.len() - info_size;
  186. let g: *mut dyn FnMut(Option<NodeFactory<'_>>) -> Option<VNode<'_>> =
  187. unsafe { make_fat_ptr(data[..].as_ptr() as usize, &data[info_ofs..]) };
  188. self.dropped = true;
  189. let clos = unsafe { &mut *g };
  190. clos(None);
  191. }
  192. }
  193. }
  194. /// Obtain mutable access to a pointer's words
  195. fn ptr_as_slice<T>(ptr: &mut T) -> &mut [usize] {
  196. assert!(mem::size_of::<T>() % mem::size_of::<usize>() == 0);
  197. let words = mem::size_of::<T>() / mem::size_of::<usize>();
  198. // SAFE: Points to valid memory (a raw pointer)
  199. unsafe { core::slice::from_raw_parts_mut(ptr as *mut _ as *mut usize, words) }
  200. }
  201. /// Re-construct a fat pointer
  202. unsafe fn make_fat_ptr<T: ?Sized>(data_ptr: usize, meta_vals: &[usize]) -> *mut T {
  203. let mut rv = mem::MaybeUninit::<*mut T>::uninit();
  204. {
  205. let s = ptr_as_slice(&mut rv);
  206. s[0] = data_ptr;
  207. s[1..].copy_from_slice(meta_vals);
  208. }
  209. let rv = rv.assume_init();
  210. assert_eq!(rv as *const (), data_ptr as *const ());
  211. rv
  212. }
  213. fn round_to_words(len: usize) -> usize {
  214. (len + mem::size_of::<usize>() - 1) / mem::size_of::<usize>()
  215. }
  216. // #[cfg(test)]
  217. // mod tests {
  218. // use super::*;
  219. // use crate::innerlude::{Element, Scope, VirtualDom};
  220. // #[test]
  221. // fn it_works() {
  222. // fn app(cx: Scope<()>) -> Element {
  223. // cx.render(LazyNodes::new(|f| f.text(format_args!("hello world!"))))
  224. // }
  225. // let mut dom = VirtualDom::new(app);
  226. // dom.rebuild();
  227. // let g = dom.base_scope().root_node();
  228. // dbg!(g);
  229. // }
  230. // #[test]
  231. // fn it_drops() {
  232. // use std::rc::Rc;
  233. // struct AppProps {
  234. // inner: Rc<i32>,
  235. // }
  236. // fn app(cx: Scope<AppProps>) -> Element {
  237. // struct DropInner {
  238. // id: i32,
  239. // }
  240. // impl Drop for DropInner {
  241. // fn drop(&mut self) {
  242. // eprintln!("dropping inner");
  243. // }
  244. // }
  245. // let caller = {
  246. // let it = (0..10).map(|i| {
  247. // let val = cx.props.inner.clone();
  248. // LazyNodes::new(move |f| {
  249. // eprintln!("hell closure");
  250. // let inner = DropInner { id: i };
  251. // f.text(format_args!("hello world {:?}, {:?}", inner.id, val))
  252. // })
  253. // });
  254. // LazyNodes::new(|f| {
  255. // eprintln!("main closure");
  256. // f.fragment_from_iter(it)
  257. // })
  258. // };
  259. // cx.render(caller)
  260. // }
  261. // let inner = Rc::new(0);
  262. // let mut dom = VirtualDom::new_with_props(
  263. // app,
  264. // AppProps {
  265. // inner: inner.clone(),
  266. // },
  267. // );
  268. // dom.rebuild();
  269. // drop(dom);
  270. // assert_eq!(Rc::strong_count(&inner), 1);
  271. // }
  272. // }