1
0

scope.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. use crate::component::ScopeIdx;
  2. use crate::context::hooks::Hook;
  3. use crate::innerlude::*;
  4. use crate::nodes::VNode;
  5. use bumpalo::Bump;
  6. use std::{
  7. any::{Any, TypeId},
  8. cell::RefCell,
  9. marker::PhantomData,
  10. ops::Deref,
  11. };
  12. pub trait Properties: PartialEq {}
  13. // just for now
  14. impl<T: PartialEq> Properties for T {}
  15. pub trait Scoped {
  16. fn run(&mut self);
  17. fn compare_props(&self, new: &dyn std::any::Any) -> bool;
  18. fn call_listener(&mut self, trigger: EventTrigger);
  19. fn new_frame<'bump>(&'bump self) -> &'bump VNode<'bump>;
  20. fn old_frame<'bump>(&'bump self) -> &'bump VNode<'bump>;
  21. }
  22. /// Every component in Dioxus is represented by a `Scope`.
  23. ///
  24. /// Scopes contain the state for hooks, the component's props, and other lifecycle information.
  25. ///
  26. /// Scopes are allocated in a generational arena. As components are mounted/unmounted, they will replace slots of dead components.
  27. /// The actual contents of the hooks, though, will be allocated with the standard allocator. These should not allocate as frequently.
  28. pub struct Scope<P: Properties> {
  29. // Map to the parent
  30. pub parent: Option<ScopeIdx>,
  31. // our own index
  32. pub myidx: ScopeIdx,
  33. pub caller: FC<P>,
  34. pub props: P,
  35. // ==========================
  36. // slightly unsafe stuff
  37. // ==========================
  38. // an internal, highly efficient storage of vnodes
  39. pub frames: ActiveFrame,
  40. // These hooks are actually references into the hook arena
  41. // These two could be combined with "OwningRef" to remove unsafe usage
  42. // or we could dedicate a tiny bump arena just for them
  43. // could also use ourborous
  44. pub hooks: RefCell<Vec<*mut Hook>>,
  45. pub hook_arena: typed_arena::Arena<Hook>,
  46. // Unsafety:
  47. // - is self-refenrential and therefore needs to point into the bump
  48. // Stores references into the listeners attached to the vnodes
  49. // NEEDS TO BE PRIVATE
  50. listeners: RefCell<Vec<*const dyn Fn(crate::events::VirtualEvent)>>,
  51. }
  52. // instead of having it as a trait method, we use a single function
  53. // todo: do the unsafety magic stuff to erase the type of p
  54. pub fn create_scoped<P: Properties + 'static>(
  55. caller: FC<P>,
  56. props: P,
  57. myidx: ScopeIdx,
  58. parent: Option<ScopeIdx>,
  59. ) -> Box<dyn Scoped> {
  60. let hook_arena = typed_arena::Arena::new();
  61. let hooks = RefCell::new(Vec::new());
  62. let listeners = Default::default();
  63. let old_frame = BumpFrame {
  64. bump: Bump::new(),
  65. head_node: VNode::text(""),
  66. };
  67. let new_frame = BumpFrame {
  68. bump: Bump::new(),
  69. head_node: VNode::text(""),
  70. };
  71. let frames = ActiveFrame::from_frames(old_frame, new_frame);
  72. Box::new(Scope {
  73. myidx,
  74. hook_arena,
  75. hooks,
  76. caller,
  77. frames,
  78. listeners,
  79. parent,
  80. props,
  81. })
  82. }
  83. impl<P: Properties + 'static> Scoped for Scope<P> {
  84. /// Create a new context and run the component with references from the Virtual Dom
  85. /// This function downcasts the function pointer based on the stored props_type
  86. ///
  87. /// Props is ?Sized because we borrow the props and don't need to know the size. P (sized) is used as a marker (unsized)
  88. fn run<'bump>(&'bump mut self) {
  89. let frame = {
  90. let frame = self.frames.next();
  91. frame.bump.reset();
  92. frame
  93. };
  94. let node_slot = std::rc::Rc::new(RefCell::new(None));
  95. let ctx: Context<'bump> = Context {
  96. arena: &self.hook_arena,
  97. hooks: &self.hooks,
  98. bump: &frame.bump,
  99. idx: 0.into(),
  100. _p: PhantomData {},
  101. final_nodes: node_slot.clone(),
  102. scope: self.myidx,
  103. listeners: &self.listeners,
  104. };
  105. // Note that the actual modification of the vnode head element occurs during this call
  106. // let _: DomTree = caller(ctx, props);
  107. let _: DomTree = (self.caller)(ctx, &self.props);
  108. /*
  109. SAFETY ALERT
  110. DO NOT USE THIS VNODE WITHOUT THE APPOPRIATE ACCESSORS.
  111. KEEPING THIS STATIC REFERENCE CAN LEAD TO UB.
  112. Some things to note:
  113. - The VNode itself is bound to the lifetime, but it itself is owned by scope.
  114. - The VNode has a private API and can only be used from accessors.
  115. - Public API cannot drop or destructure VNode
  116. */
  117. frame.head_node = node_slot
  118. .deref()
  119. .borrow_mut()
  120. .take()
  121. .expect("Viewing did not happen");
  122. }
  123. fn compare_props(&self, new: &Any) -> bool {
  124. new.downcast_ref::<P>()
  125. .map(|f| &self.props == f)
  126. .expect("Props should not be of a different type")
  127. }
  128. // A safe wrapper around calling listeners
  129. // calling listeners will invalidate the list of listeners
  130. // The listener list will be completely drained because the next frame will write over previous listeners
  131. fn call_listener(&mut self, trigger: EventTrigger) {
  132. let EventTrigger {
  133. listener_id,
  134. event: source,
  135. ..
  136. } = trigger;
  137. unsafe {
  138. let listener = self
  139. .listeners
  140. .borrow()
  141. .get(listener_id as usize)
  142. .expect("Listener should exist if it was triggered")
  143. .as_ref()
  144. .unwrap();
  145. // Run the callback with the user event
  146. log::debug!("Running listener");
  147. listener(source);
  148. log::debug!("Running listener");
  149. // drain all the event listeners
  150. // if we don't, then they'll stick around and become invalid
  151. // big big big big safety issue
  152. self.listeners.borrow_mut().drain(..);
  153. }
  154. }
  155. fn new_frame<'bump>(&'bump self) -> &'bump VNode<'bump> {
  156. self.frames.current_head_node()
  157. }
  158. fn old_frame<'bump>(&'bump self) -> &'bump VNode<'bump> {
  159. self.frames.prev_head_node()
  160. }
  161. }
  162. // ==========================
  163. // Active-frame related code
  164. // ==========================
  165. // todo, do better with the active frame stuff
  166. // somehow build this vnode with a lifetime tied to self
  167. // This root node has "static" lifetime, but it's really not static.
  168. // It's goverened by the oldest of the two frames and is switched every time a new render occurs
  169. // Use this node as if it were static is unsafe, and needs to be fixed with ourborous or owning ref
  170. // ! do not copy this reference are things WILL break !
  171. pub struct ActiveFrame {
  172. pub idx: RefCell<usize>,
  173. pub frames: [BumpFrame; 2],
  174. }
  175. pub struct BumpFrame {
  176. pub bump: Bump,
  177. pub head_node: VNode<'static>,
  178. }
  179. impl ActiveFrame {
  180. fn from_frames(a: BumpFrame, b: BumpFrame) -> Self {
  181. Self {
  182. idx: 0.into(),
  183. frames: [a, b],
  184. }
  185. }
  186. fn current_head_node<'b>(&'b self) -> &'b VNode<'b> {
  187. let raw_node = match *self.idx.borrow() & 1 == 0 {
  188. true => &self.frames[0],
  189. false => &self.frames[1],
  190. };
  191. // Give out our self-referential item with our own borrowed lifetime
  192. unsafe {
  193. let unsafe_head = &raw_node.head_node;
  194. let safe_node = std::mem::transmute::<&VNode<'static>, &VNode<'b>>(unsafe_head);
  195. safe_node
  196. }
  197. }
  198. fn prev_head_node<'b>(&'b self) -> &'b VNode<'b> {
  199. let raw_node = match *self.idx.borrow() & 1 != 0 {
  200. true => &self.frames[0],
  201. false => &self.frames[1],
  202. };
  203. // Give out our self-referential item with our own borrowed lifetime
  204. unsafe {
  205. let unsafe_head = &raw_node.head_node;
  206. let safe_node = std::mem::transmute::<&VNode<'static>, &VNode<'b>>(unsafe_head);
  207. safe_node
  208. }
  209. }
  210. fn next(&mut self) -> &mut BumpFrame {
  211. *self.idx.borrow_mut() += 1;
  212. if *self.idx.borrow() % 2 == 0 {
  213. &mut self.frames[0]
  214. } else {
  215. &mut self.frames[1]
  216. }
  217. }
  218. }
  219. #[cfg(test)]
  220. mod tests {
  221. use super::*;
  222. use crate::prelude::*;
  223. static ListenerTest: FC<()> = |ctx, props| {
  224. ctx.render(html! {
  225. <div onclick={|_| println!("Hell owlrld")}>
  226. "hello"
  227. </div>
  228. })
  229. };
  230. #[test]
  231. fn test_scope() {
  232. let example: FC<()> = |ctx, props| {
  233. use crate::builder::*;
  234. ctx.render(|ctx| {
  235. builder::ElementBuilder::new(ctx, "div")
  236. .child(text("a"))
  237. .finish()
  238. })
  239. };
  240. let props = ();
  241. let parent = None;
  242. let mut nodes = generational_arena::Arena::new();
  243. nodes.insert_with(|myidx| {
  244. let scope = create_scoped(example, props, myidx, parent);
  245. });
  246. }
  247. #[derive(Debug)]
  248. struct ExampleProps<'src> {
  249. name: &'src String,
  250. }
  251. #[derive(Debug)]
  252. struct EmptyProps<'src> {
  253. name: &'src String,
  254. }
  255. use crate::{builder::*, hooks::use_ref};
  256. fn example_fc<'a>(ctx: Context<'a>, props: &'a EmptyProps) -> DomTree {
  257. let (content, _): (&'a String, _) = crate::hooks::use_state(&ctx, || "abcd".to_string());
  258. let childprops: ExampleProps<'a> = ExampleProps { name: content };
  259. ctx.render(move |c| {
  260. builder::ElementBuilder::new(c, "div")
  261. .child(text(props.name))
  262. .child(virtual_child::<ExampleProps>(
  263. c.bump,
  264. childprops,
  265. child_example,
  266. ))
  267. .finish()
  268. })
  269. }
  270. fn child_example<'b>(ctx: Context<'b>, props: &'b ExampleProps) -> DomTree {
  271. ctx.render(move |ctx| {
  272. builder::ElementBuilder::new(ctx, "div")
  273. .child(text(props.name))
  274. .finish()
  275. })
  276. }
  277. static CHILD: FC<ExampleProps> = |ctx, props: &'_ ExampleProps| {
  278. ctx.render(move |ctx| {
  279. builder::ElementBuilder::new(ctx, "div")
  280. .child(text(props.name))
  281. .finish()
  282. })
  283. };
  284. #[test]
  285. fn test_borrowed_scope() {
  286. let example: FC<EmptyProps> = |ctx, props| {
  287. ctx.render(move |b| {
  288. builder::ElementBuilder::new(b, "div")
  289. .child(virtual_child(
  290. b.bump,
  291. ExampleProps { name: props.name },
  292. CHILD,
  293. ))
  294. .finish()
  295. })
  296. };
  297. let source_text = "abcd123".to_string();
  298. let props = ExampleProps { name: &source_text };
  299. }
  300. }
  301. #[cfg(asd)]
  302. mod old {
  303. /// The ComponentCaller struct is an opaque object that encapsultes the memoization and running functionality for FC
  304. ///
  305. /// It's opaque because during the diffing mechanism, the type of props is sealed away in a closure. This makes it so
  306. /// scope doesn't need to be generic
  307. pub struct ComponentCaller {
  308. // used as a memoization strategy
  309. comparator: Box<dyn Fn(&Box<dyn Any>) -> bool>,
  310. // used to actually run the component
  311. // encapsulates props
  312. runner: Box<dyn Fn(Context) -> DomTree>,
  313. props_type: TypeId,
  314. // the actual FC<T>
  315. raw: *const (),
  316. }
  317. impl ComponentCaller {
  318. fn new<P>(props: P) -> Self {
  319. let comparator = Box::new(|f| false);
  320. todo!();
  321. // Self { comparator }
  322. }
  323. fn update_props<P>(props: P) {}
  324. }
  325. }