lazynodes.rs 10 KB

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