Browse Source

fix render macro

Evan Almloff 1 year ago
parent
commit
0a35b5a670
3 changed files with 53 additions and 4 deletions
  1. 9 2
      packages/core/src/arena.rs
  2. 41 1
      packages/core/src/scopes.rs
  3. 3 1
      packages/rsx/src/lib.rs

+ 9 - 2
packages/core/src/arena.rs

@@ -1,4 +1,4 @@
-use std::ptr::NonNull;
+use crate::ScopeState;use std::ptr::NonNull;
 
 use crate::{
     innerlude::DirtyScope, nodes::RenderReturn, nodes::VNode, virtual_dom::VirtualDom,
@@ -174,7 +174,14 @@ impl VirtualDom {
         scope.borrowed_props.borrow_mut().clear();
 
         // Now that all the references are gone, we can safely drop our own references in our listeners.
-        let mut listeners = scope.attributes_to_drop_before_render.borrow_mut();
+        scope.drop_listeners();
+    }
+}
+
+impl ScopeState {
+    /// Drop all listeners that were allocated in the bump allocator.
+    pub(crate) fn drop_listeners(&self) {
+        let mut listeners = self.attributes_to_drop_before_render.borrow_mut();
         listeners.drain(..).for_each(|listener| {
             let listener = unsafe { &*listener };
             if let AttributeValue::Listener(l) = &listener.value {

+ 41 - 1
packages/core/src/scopes.rs

@@ -102,6 +102,7 @@ pub struct ScopeState {
 
 impl Drop for ScopeState {
     fn drop(&mut self) {
+        self.drop_listeners();
         self.runtime.remove_context(self.context_id);
     }
 }
@@ -345,7 +346,7 @@ impl<'src> ScopeState {
     ///     // Actually build the tree and allocate it
     ///     cx.render(lazy_tree)
     /// }
-    ///```
+    /// ```
     pub fn render(&'src self, rsx: LazyNodes<'src, '_>) -> Element<'src> {
         let element = rsx.call(self);
 
@@ -387,6 +388,45 @@ impl<'src> ScopeState {
         Some(element)
     }
 
+    #[doc(hidden)]
+    /// Take a [`crate::VNode`] and link it to a [`ScopeState`]
+    /// 
+    /// This is used internally in the render macro
+    pub fn link_node(&'src self, element: &crate::VNode<'src>) {
+        let mut listeners = self.attributes_to_drop_before_render.borrow_mut();
+        for attr in element.dynamic_attrs {
+            match attr.value {
+                // We need to drop listeners before the next render because they may borrow data from the borrowed props which will be dropped
+                AttributeValue::Listener(_) => {
+                    let unbounded = unsafe { std::mem::transmute(attr as *const Attribute) };
+                    listeners.push(unbounded);
+                }
+                // We need to drop any values manually to make sure that their drop implementation is called before the next render
+                AttributeValue::Any(_) => {
+                    let unbounded = unsafe { std::mem::transmute(attr as *const Attribute) };
+                    self.previous_frame().add_attribute_to_drop(unbounded);
+                }
+
+                _ => (),
+            }
+        }
+
+        let mut props = self.borrowed_props.borrow_mut();
+        let mut drop_props = self
+            .previous_frame()
+            .props_to_drop_before_reset
+            .borrow_mut();
+        for node in element.dynamic_nodes {
+            if let DynamicNode::Component(comp) = node {
+                let unbounded = unsafe { std::mem::transmute(comp as *const VComponent) };
+                if !comp.static_props {
+                    props.push(unbounded);
+                }
+                drop_props.push(unbounded);
+            }
+        }
+    }
+
     /// Create a dynamic text node using [`Arguments`] and the [`ScopeState`]'s internal [`Bump`] allocator
     pub fn text_node(&'src self, args: Arguments) -> DynamicNode<'src> {
         DynamicNode::Text(VText {

+ 3 - 1
packages/rsx/src/lib.rs

@@ -139,7 +139,9 @@ impl ToTokens for RenderCallBody {
         out_tokens.append_all(quote! {
             Some({
                 let __cx = cx;
-                #body
+                let __element = { #body };
+                cx.link_node(&__element);
+                __element
             })
         })
     }