use interning to leak less memory
This commit is contained in:
1 parent
2131e5658b
commit
6b19229b53
2 files changed
+49
-31
No files matched your search
@@ -12,3 +12,4 @@ syn = { version = "1.0", features = ["full", "extra-traits"] }
|
|||||||
quote = { version = "1.0" }
|
quote = { version = "1.0" }
|
||||||
dioxus-core = { path = "../core", features = ["serialize"] }
|
dioxus-core = { path = "../core", features = ["serialize"] }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
internment = "0.7.0"
|
||||||
+48
-31
@@ -19,12 +19,15 @@ mod hot_reloading_context;
|
|||||||
mod ifmt;
|
mod ifmt;
|
||||||
mod node;
|
mod node;
|
||||||
|
|
||||||
|
use std::{borrow::Borrow, hash::Hash};
|
||||||
|
|
||||||
// Re-export the namespaces into each other
|
// Re-export the namespaces into each other
|
||||||
pub use component::*;
|
pub use component::*;
|
||||||
use dioxus_core::{Template, TemplateAttribute, TemplateNode};
|
use dioxus_core::{Template, TemplateAttribute, TemplateNode};
|
||||||
pub use element::*;
|
pub use element::*;
|
||||||
use hot_reloading_context::{Empty, HotReloadingContext};
|
use hot_reloading_context::{Empty, HotReloadingContext};
|
||||||
pub use ifmt::*;
|
pub use ifmt::*;
|
||||||
|
use internment::Intern;
|
||||||
pub use node::*;
|
pub use node::*;
|
||||||
|
|
||||||
// imports
|
// imports
|
||||||
@@ -35,6 +38,13 @@ use syn::{
|
|||||||
Result, Token,
|
Result, Token,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// interns a object into a static object, resusing the value if it already exists
|
||||||
|
fn intern<'a, T: Eq + Hash + Send + Sync + ?Sized + 'static>(
|
||||||
|
s: impl Into<Intern<T>>,
|
||||||
|
) -> &'static T {
|
||||||
|
s.into().as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
/// Fundametnally, every CallBody is a template
|
/// Fundametnally, every CallBody is a template
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct CallBody<Ctx: HotReloadingContext = Empty> {
|
pub struct CallBody<Ctx: HotReloadingContext = Empty> {
|
||||||
@@ -47,15 +57,20 @@ pub struct CallBody<Ctx: HotReloadingContext = Empty> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: HotReloadingContext> CallBody<Ctx> {
|
impl<Ctx: HotReloadingContext> CallBody<Ctx> {
|
||||||
|
/// This will try to create a new template from the current body and the previous body. This will return None if the rsx has some dynamic part that has changed.
|
||||||
/// This function intentionally leaks memory to create a static template.
|
/// This function intentionally leaks memory to create a static template.
|
||||||
/// Keeping the template static allows us to simplify the core of dioxus and leaking memory in dev mode is less of an issue.
|
/// Keeping the template static allows us to simplify the core of dioxus and leaking memory in dev mode is less of an issue.
|
||||||
/// the previous_location is the location of the previous template at the time the template was originally compiled.
|
/// the previous_location is the location of the previous template at the time the template was originally compiled.
|
||||||
pub fn leak_template(&self, previous_location: &'static str) -> Template {
|
pub fn update_template(
|
||||||
|
&self,
|
||||||
|
template: Option<&CallBody<Ctx>>,
|
||||||
|
location: &'static str,
|
||||||
|
) -> Option<Template> {
|
||||||
let mut renderer: TemplateRenderer<Ctx> = TemplateRenderer {
|
let mut renderer: TemplateRenderer<Ctx> = TemplateRenderer {
|
||||||
roots: &self.roots,
|
roots: &self.roots,
|
||||||
phantom: std::marker::PhantomData,
|
phantom: std::marker::PhantomData,
|
||||||
};
|
};
|
||||||
renderer.leak_template(previous_location)
|
renderer.update_template(template, location)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,7 +127,11 @@ pub struct TemplateRenderer<'a, Ctx: HotReloadingContext = Empty> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Ctx: HotReloadingContext> TemplateRenderer<'a, Ctx> {
|
impl<'a, Ctx: HotReloadingContext> TemplateRenderer<'a, Ctx> {
|
||||||
fn leak_template(&mut self, previous_location: &'static str) -> Template<'static> {
|
fn update_template(
|
||||||
|
&mut self,
|
||||||
|
previous_call: Option<&CallBody<Ctx>>,
|
||||||
|
location: &'static str,
|
||||||
|
) -> Option<Template<'static>> {
|
||||||
let mut context: DynamicContext<Ctx> = DynamicContext::default();
|
let mut context: DynamicContext<Ctx> = DynamicContext::default();
|
||||||
|
|
||||||
let roots: Vec<_> = self
|
let roots: Vec<_> = self
|
||||||
@@ -121,32 +140,32 @@ impl<'a, Ctx: HotReloadingContext> TemplateRenderer<'a, Ctx> {
|
|||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(idx, root)| {
|
.map(|(idx, root)| {
|
||||||
context.current_path.push(idx as u8);
|
context.current_path.push(idx as u8);
|
||||||
let out = context.leak_node(root);
|
let out = context.update_node(root);
|
||||||
context.current_path.pop();
|
context.current_path.pop();
|
||||||
out
|
out
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Template {
|
Some(Template {
|
||||||
name: previous_location,
|
name: location,
|
||||||
roots: Box::leak(roots.into_boxed_slice()),
|
roots: intern(roots.as_slice()),
|
||||||
node_paths: Box::leak(
|
node_paths: intern(
|
||||||
context
|
context
|
||||||
.node_paths
|
.node_paths
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|path| &*Box::leak(path.into_boxed_slice()))
|
.map(|path| intern(path.as_slice()))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.into_boxed_slice(),
|
.as_slice(),
|
||||||
),
|
),
|
||||||
attr_paths: Box::leak(
|
attr_paths: intern(
|
||||||
context
|
context
|
||||||
.attr_paths
|
.attr_paths
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|path| &*Box::leak(path.into_boxed_slice()))
|
.map(|path| intern(path.as_slice()))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.into_boxed_slice(),
|
.as_slice(),
|
||||||
),
|
),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,8 +228,9 @@ impl<'a, Ctx: HotReloadingContext> ToTokens for TemplateRenderer<'a, Ctx> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// As we print out the dynamic nodes, we want to keep track of them in a linear fashion
|
|
||||||
// We'll use the size of the vecs to determine the index of the dynamic node in the final
|
// As we create the dynamic nodes, we want to keep track of them in a linear fashion
|
||||||
|
// We'll use the size of the vecs to determine the index of the dynamic node in the final output
|
||||||
pub struct DynamicContext<'a, Ctx: HotReloadingContext> {
|
pub struct DynamicContext<'a, Ctx: HotReloadingContext> {
|
||||||
dynamic_nodes: Vec<&'a BodyNode>,
|
dynamic_nodes: Vec<&'a BodyNode>,
|
||||||
dynamic_attributes: Vec<&'a ElementAttrNamed>,
|
dynamic_attributes: Vec<&'a ElementAttrNamed>,
|
||||||
@@ -236,7 +256,7 @@ impl<'a, Ctx: HotReloadingContext> Default for DynamicContext<'a, Ctx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Ctx: HotReloadingContext> DynamicContext<'a, Ctx> {
|
impl<'a, Ctx: HotReloadingContext> DynamicContext<'a, Ctx> {
|
||||||
fn leak_node(&mut self, root: &'a BodyNode) -> TemplateNode<'static> {
|
fn update_node(&mut self, root: &'a BodyNode) -> TemplateNode<'static> {
|
||||||
match root {
|
match root {
|
||||||
BodyNode::Element(el) => {
|
BodyNode::Element(el) => {
|
||||||
// dynamic attributes
|
// dynamic attributes
|
||||||
@@ -250,7 +270,7 @@ impl<'a, Ctx: HotReloadingContext> DynamicContext<'a, Ctx> {
|
|||||||
|
|
||||||
let element_name_rust = el.name.to_string();
|
let element_name_rust = el.name.to_string();
|
||||||
|
|
||||||
let static_attrs: Vec<_> = el
|
let static_attrs: Vec<TemplateAttribute<'static>> = el
|
||||||
.attributes
|
.attributes
|
||||||
.iter()
|
.iter()
|
||||||
.map(|attr| match &attr.attr {
|
.map(|attr| match &attr.attr {
|
||||||
@@ -259,14 +279,11 @@ impl<'a, Ctx: HotReloadingContext> DynamicContext<'a, Ctx> {
|
|||||||
let attribute_name_rust = name.to_string();
|
let attribute_name_rust = name.to_string();
|
||||||
let (name, namespace) =
|
let (name, namespace) =
|
||||||
Ctx::map_attribute(&element_name_rust, &attribute_name_rust)
|
Ctx::map_attribute(&element_name_rust, &attribute_name_rust)
|
||||||
.unwrap_or((
|
.unwrap_or((intern(attribute_name_rust.as_str()), None));
|
||||||
Box::leak(attribute_name_rust.into_boxed_str()),
|
|
||||||
None,
|
|
||||||
));
|
|
||||||
TemplateAttribute::Static {
|
TemplateAttribute::Static {
|
||||||
name,
|
name,
|
||||||
namespace,
|
namespace,
|
||||||
value: Box::leak(value.value().into_boxed_str()),
|
value: intern(value.value().as_str()),
|
||||||
// name: dioxus_elements::#el_name::#name.0,
|
// name: dioxus_elements::#el_name::#name.0,
|
||||||
// namespace: dioxus_elements::#el_name::#name.1,
|
// namespace: dioxus_elements::#el_name::#name.1,
|
||||||
// value: #value,
|
// value: #value,
|
||||||
@@ -279,9 +296,9 @@ impl<'a, Ctx: HotReloadingContext> DynamicContext<'a, Ctx> {
|
|||||||
ElementAttr::CustomAttrText { name, value } if value.is_static() => {
|
ElementAttr::CustomAttrText { name, value } if value.is_static() => {
|
||||||
let value = value.source.as_ref().unwrap();
|
let value = value.source.as_ref().unwrap();
|
||||||
TemplateAttribute::Static {
|
TemplateAttribute::Static {
|
||||||
name: Box::leak(name.value().into_boxed_str()),
|
name: intern(name.value().as_str()),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
value: Box::leak(value.value().into_boxed_str()),
|
value: intern(value.value().as_str()),
|
||||||
// todo: we don't diff these so we never apply the volatile flag
|
// todo: we don't diff these so we never apply the volatile flag
|
||||||
// volatile: dioxus_elements::#el_name::#name.2,
|
// volatile: dioxus_elements::#el_name::#name.2,
|
||||||
}
|
}
|
||||||
@@ -306,26 +323,26 @@ impl<'a, Ctx: HotReloadingContext> DynamicContext<'a, Ctx> {
|
|||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(idx, root)| {
|
.map(|(idx, root)| {
|
||||||
self.current_path.push(idx as u8);
|
self.current_path.push(idx as u8);
|
||||||
let out = self.leak_node(root);
|
let out = self.update_node(root);
|
||||||
self.current_path.pop();
|
self.current_path.pop();
|
||||||
out
|
out
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let (tag, namespace) = Ctx::map_element(&element_name_rust)
|
let (tag, namespace) = Ctx::map_element(&element_name_rust)
|
||||||
.unwrap_or((Box::leak(element_name_rust.into_boxed_str()), None));
|
.unwrap_or((intern(element_name_rust.as_str()), None));
|
||||||
TemplateNode::Element {
|
TemplateNode::Element {
|
||||||
tag,
|
tag,
|
||||||
namespace,
|
namespace,
|
||||||
attrs: Box::leak(static_attrs.into_boxed_slice()),
|
attrs: intern(static_attrs.into_boxed_slice()),
|
||||||
children: Box::leak(children.into_boxed_slice()),
|
children: intern(children.as_slice()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BodyNode::Text(text) if text.is_static() => {
|
BodyNode::Text(text) if text.is_static() => {
|
||||||
let text = text.source.as_ref().unwrap();
|
let text = text.source.as_ref().unwrap();
|
||||||
TemplateNode::Text {
|
TemplateNode::Text {
|
||||||
text: Box::leak(text.value().into_boxed_str()),
|
text: intern(text.value().as_str()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -490,7 +507,7 @@ fn template() {
|
|||||||
|
|
||||||
let call_body: CallBody<Mock> = syn::parse2(input).unwrap();
|
let call_body: CallBody<Mock> = syn::parse2(input).unwrap();
|
||||||
|
|
||||||
let template = call_body.leak_template("testing");
|
let template = call_body.update_template(None, "testing").unwrap();
|
||||||
|
|
||||||
dbg!(template);
|
dbg!(template);
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user