chore: wipe away old src

This commit is contained in:
Jonathan Kelley committed 2022-11-20 15:58:05 -08:00
1 parent 493591400f
commit d407035931
29 files changed
+187 -6819

No files matched your search

+26 -9
View File
@@ -7,20 +7,33 @@ use crate::{
Element,
};
pub trait AnyProps<'a> {
fn as_ptr(&self) -> *const ();
/// A trait that essentially allows VComponentProps to be used generically
pub unsafe trait AnyProps<'a> {
fn props_ptr(&self) -> *const ();
fn render(&'a self, bump: &'a ScopeState) -> RenderReturn<'a>;
unsafe fn memoize(&self, other: &dyn AnyProps) -> bool;
}
pub(crate) struct VComponentProps<'a, P, A, F: ComponentReturn<'a, A> = Element<'a>> {
pub(crate) struct VProps<'a, P, A, F: ComponentReturn<'a, A> = Element<'a>> {
pub render_fn: fn(Scope<'a, P>) -> F,
pub memo: unsafe fn(&P, &P) -> bool,
pub props: P,
pub _marker: PhantomData<A>,
// pub props: PropsAllocation<P>,
_marker: PhantomData<A>,
}
impl<'a, P, A, F: ComponentReturn<'a, A>> VComponentProps<'a, P, A, F> {
enum PropsAllocation<P> {
/// If it's memoized, then the heap owns the props
Memoized(*mut P),
/// If it's borrowed, then the parent owns the props
Borrowed(P),
}
impl<'a, P, A, F> VProps<'a, P, A, F>
where
F: ComponentReturn<'a, A>,
{
pub(crate) fn new(
render_fn: fn(Scope<'a, P>) -> F,
memo: unsafe fn(&P, &P) -> bool,
@@ -30,13 +43,17 @@ impl<'a, P, A, F: ComponentReturn<'a, A>> VComponentProps<'a, P, A, F> {
render_fn,
memo,
props,
// props: PropsAllocation::Borrowed(props),
_marker: PhantomData,
}
}
}
impl<'a, P, A, F: ComponentReturn<'a, A>> AnyProps<'a> for VComponentProps<'a, P, A, F> {
fn as_ptr(&self) -> *const () {
unsafe impl<'a, P, A, F> AnyProps<'a> for VProps<'a, P, A, F>
where
F: ComponentReturn<'a, A>,
{
fn props_ptr(&self) -> *const () {
&self.props as *const _ as *const ()
}
@@ -45,8 +62,8 @@ impl<'a, P, A, F: ComponentReturn<'a, A>> AnyProps<'a> for VComponentProps<'a, P
// you *must* make this check *before* calling this method
// if your functions are not the same, then you will downcast a pointer into a different type (UB)
unsafe fn memoize(&self, other: &dyn AnyProps) -> bool {
let real_other: &P = &*(other.as_ptr() as *const _ as *const P);
let real_us: &P = &*(self.as_ptr() as *const _ as *const P);
let real_other: &P = &*(other.props_ptr() as *const _ as *const P);
let real_us: &P = &*(self.props_ptr() as *const _ as *const P);
(self.memo)(real_us, real_other)
}
+6 -9
View File
@@ -70,11 +70,7 @@ impl VirtualDom {
&template.dynamic_nodes[*id],
*id,
),
DynamicNode::Text(VText {
id: slot,
value,
inner,
}) => {
DynamicNode::Text(VText { id: slot, value }) => {
let id = self.next_element(template, template.template.node_paths[*id]);
slot.set(id);
mutations.push(CreateTextNode { value, id });
@@ -239,7 +235,7 @@ impl VirtualDom {
idx: usize,
) -> usize {
match &node {
DynamicNode::Text(VText { id, value, inner }) => {
DynamicNode::Text(VText { id, value }) => {
let new_id = self.next_element(template, template.template.node_paths[idx]);
id.set(new_id);
mutations.push(HydrateText {
@@ -251,9 +247,10 @@ impl VirtualDom {
}
DynamicNode::Component(component) => {
let scope = self
.new_scope(unsafe { std::mem::transmute(component.props.get()) })
.id;
let props = component.props.replace(None).unwrap();
let prop_ptr = unsafe { std::mem::transmute(props.as_ref()) };
let scope = self.new_scope(prop_ptr).id;
component.props.replace(Some(props));
component.scope.set(Some(scope));
+123 -83
View File
@@ -5,7 +5,8 @@ use crate::innerlude::{Mutations, VComponent, VFragment, VText};
use crate::virtual_dom::VirtualDom;
use crate::{Attribute, AttributeValue, TemplateNode};
use crate::any_props::VComponentProps;
use crate::any_props::VProps;
use DynamicNode::*;
use crate::mutations::Mutation;
use crate::nodes::{DynamicNode, Template, TemplateId};
@@ -77,7 +78,7 @@ impl<'b> VirtualDom {
use RenderReturn::{Async, Sync};
match (left, right) {
// diff
(Sync(Some(l)), Sync(Some(r))) => self.diff_node(m, l, r),
(Sync(Some(l)), Sync(Some(r))) => self.diff_vnode(m, l, r),
// remove old with placeholder
(Sync(Some(l)), Sync(None)) | (Sync(Some(l)), Async(_)) => {
@@ -91,7 +92,7 @@ impl<'b> VirtualDom {
(Sync(None), Sync(Some(_))) => {}
(Async(_), Sync(Some(v))) => {}
// nothing...
// nothing... just transfer the placeholders over
(Async(_), Async(_))
| (Sync(None), Sync(None))
| (Sync(None), Async(_))
@@ -99,23 +100,20 @@ impl<'b> VirtualDom {
}
}
pub fn diff_node(
pub fn diff_vnode(
&mut self,
muts: &mut Mutations<'b>,
left_template: &'b VNode<'b>,
right_template: &'b VNode<'b>,
) {
if left_template.template.id != right_template.template.id {
// do a light diff of the roots nodes.
todo!("lightly diff roots and replace");
return;
return self.light_diff_templates(muts, left_template, right_template);
}
for (_idx, (left_attr, right_attr)) in left_template
for (left_attr, right_attr) in left_template
.dynamic_attrs
.iter()
.zip(right_template.dynamic_attrs.iter())
.enumerate()
{
// Move over the ID from the old to the new
right_attr
@@ -135,67 +133,76 @@ impl<'b> VirtualDom {
}
}
for (idx, (left_node, right_node)) in left_template
for (left_node, right_node) in left_template
.dynamic_nodes
.iter()
.zip(right_template.dynamic_nodes.iter())
.enumerate()
{
use DynamicNode::*;
match (left_node, right_node) {
(Component(left), Component(right)) => self.diff_vcomponent(muts, left, right),
(Text(left), Text(right)) => self.diff_vtext(muts, left, right),
(Fragment(left), Fragment(right)) => self.diff_vfragment(muts, left, right),
(Placeholder(_), Placeholder(_)) => todo!(),
// Make sure to drop all the fragment children properly
(DynamicNode::Fragment { .. }, right) => todo!(),
(Text(left), right) => {
todo!()
// let m = self.create_dynamic_node(muts, right_template, right, idx);
// muts.push(Mutation::ReplaceWith { id: lid.get(), m });
}
(Placeholder(_), _) => todo!(),
// Make sure to drop the component properly
(Component { .. }, right) => {
// remove all the component roots except for the first
// replace the first with the new node
let m = self.create_dynamic_node(muts, right_template, right, idx);
todo!()
}
(Placeholder(left), Placeholder(right)) => right.set(left.get()),
_ => self.replace(muts, left_template, right_template, left_node, right_node),
};
}
}
fn replace(
&mut self,
muts: &mut Mutations<'b>,
left_template: &'b VNode<'b>,
right_template: &'b VNode<'b>,
left: &'b DynamicNode<'b>,
right: &'b DynamicNode<'b>,
) {
}
fn diff_vcomponent(
&mut self,
muts: &mut Mutations<'b>,
left: &'b VComponent<'b>,
right: &'b VComponent<'b>,
) {
let left_props = unsafe { &mut *left.props.get() };
let right_props = unsafe { &mut *right.props.get() };
// Due to how templates work, we should never get two different components. The only way we could enter
// this codepath is through "light_diff", but we check there that the pointers are the same
assert_eq!(left.render_fn, right.render_fn);
// Ensure these two props are of the same component type
match left_props.as_ptr() == right_props.as_ptr() {
true => {
//
if left.static_props {
let props_are_same = unsafe { left_props.memoize(right_props) };
/*
if props_are_same {
//
} else {
//
}
} else {
}
}
false => todo!(),
let left = rsx!{ Component {} }
let right = rsx!{ Component {} }
*/
// Make sure the new vcomponent has the right scopeid associated to it
let scope_id = left.scope.get().unwrap();
right.scope.set(Some(scope_id));
// copy out the box for both
let old = left.props.replace(None).unwrap();
let new = right.props.replace(None).unwrap();
// If the props are static, then we try to memoize by setting the new with the old
// The target scopestate still has the reference to the old props, so there's no need to update anything
// This also implicitly drops the new props since they're not used
if left.static_props && unsafe { old.memoize(new.as_ref()) } {
return right.props.set(Some(old));
}
// If the props are dynamic *or* the memoization failed, then we need to diff the props
// First, move over the props from the old to the new, dropping old props in the process
self.scopes[scope_id.0].props = unsafe { std::mem::transmute(new.as_ref()) };
right.props.set(Some(new));
// Now run the component and diff it
self.run_scope(scope_id);
self.diff_scope(muts, scope_id);
}
/// Lightly diff the two templates, checking only their roots.
@@ -236,44 +243,23 @@ impl<'b> VirtualDom {
/// Component { ..props }
/// }
/// ```
fn light_diff_tempaltes(
fn light_diff_templates(
&mut self,
muts: &mut Mutations<'b>,
left: &'b VNode<'b>,
right: &'b VNode<'b>,
) -> bool {
if left.template.roots.len() != right.template.roots.len() {
return false;
) {
if let Some(components) = matching_components(left, right) {
components
.into_iter()
.for_each(|(l, r)| self.diff_vcomponent(muts, l, r))
}
let mut components = vec![];
// run through the components, ensuring they're the same
for (l, r) in left.template.roots.iter().zip(right.template.roots.iter()) {
match (l, r) {
(TemplateNode::Dynamic(l), TemplateNode::Dynamic(r)) => {
components.push(match (&left.dynamic_nodes[*l], &right.dynamic_nodes[*r]) {
(DynamicNode::Component(l), DynamicNode::Component(r)) => {
let l_props = unsafe { &*l.props.get() };
let r_props = unsafe { &*r.props.get() };
(l, r)
}
_ => return false,
})
}
_ => return false,
}
}
// run through the components, diffing them
for (l, r) in components {
self.diff_vcomponent(muts, l, r);
}
true
}
/// Diff the two text nodes
///
/// This just moves the ID of the old node over to the new node, and then sets the text of the new node if it's
/// different.
fn diff_vtext(&mut self, muts: &mut Mutations<'b>, left: &'b VText<'b>, right: &'b VText<'b>) {
right.id.set(left.id.get());
if left.value != right.value {
@@ -297,6 +283,7 @@ impl<'b> VirtualDom {
// todo!()
// }
// (_, []) => {
// // if this fragment is the only child of its parent, then we can use the "RemoveAllChildren" mutation
// todo!()
// }
// _ => {
@@ -319,8 +306,6 @@ impl<'b> VirtualDom {
// }
// }
// }
todo!()
}
// Diff children that are not keyed.
@@ -351,7 +336,7 @@ impl<'b> VirtualDom {
}
for (new, old) in new.iter().zip(old.iter()) {
self.diff_node(muts, old, new);
self.diff_vnode(muts, old, new);
}
}
@@ -693,3 +678,58 @@ impl<'b> VirtualDom {
//
}
}
fn matching_components<'a>(
left: &'a VNode<'a>,
right: &'a VNode<'a>,
) -> Option<Vec<(&'a VComponent<'a>, &'a VComponent<'a>)>> {
if left.template.roots.len() != right.template.roots.len() {
return None;
}
// run through the components, ensuring they're the same
left.template
.roots
.iter()
.zip(right.template.roots.iter())
.map(|(l, r)| {
let (l, r) = match (l, r) {
(TemplateNode::Dynamic(l), TemplateNode::Dynamic(r)) => (l, r),
_ => return None,
};
let (l, r) = match (&left.dynamic_nodes[*l], &right.dynamic_nodes[*r]) {
(Component(l), Component(r)) => (l, r),
_ => return None,
};
(l.render_fn == r.render_fn).then(|| (l, r))
})
.collect()
}
/// We can apply various optimizations to dynamic nodes that are the single child of their parent.
///
/// IE
/// - for text - we can use SetTextContent
/// - for clearning children we can use RemoveChildren
/// - for appending children we can use AppendChildren
fn is_dyn_node_only_child(node: &VNode, idx: usize) -> bool {
let path = node.template.node_paths[idx];
// use a loop to index every static node's children until the path has run out
// only break if the last path index is a dynamic node
let mut static_node = &node.template.roots[path[0] as usize];
for i in 1..path.len() - 1 {
match static_node {
TemplateNode::Element { children, .. } => static_node = &children[path[i] as usize],
_ => return false,
}
}
match static_node {
TemplateNode::Element { children, .. } => children.len() == 1,
_ => false,
}
}
+9 -11
View File
@@ -1,4 +1,5 @@
use std::{
any::Any,
cell::{Cell, RefCell},
fmt::Arguments,
};
@@ -8,7 +9,7 @@ use bumpalo::Bump;
use std::future::Future;
use crate::{
any_props::{AnyProps, VComponentProps},
any_props::{AnyProps, VProps},
arena::ElementId,
innerlude::{DynamicNode, EventHandler, VComponent, VFragment, VText},
Attribute, AttributeValue, Element, LazyNodes, Properties, Scope, ScopeState, VNode,
@@ -21,7 +22,6 @@ impl ScopeState {
DynamicNode::Text(VText {
id: Cell::new(ElementId(0)),
value: text,
inner: false,
})
}
@@ -76,10 +76,11 @@ impl ScopeState {
P: Properties + 'a,
{
let as_component = component;
let vcomp = VComponentProps::new(as_component, P::memoize, props);
let as_dyn = self.bump().alloc(vcomp) as &mut dyn AnyProps;
let detached_dyn: *mut dyn AnyProps = unsafe { std::mem::transmute(as_dyn) };
let vcomp = VProps::new(as_component, P::memoize, props);
let as_dyn: Box<dyn AnyProps<'a>> = Box::new(vcomp);
let extended: Box<dyn AnyProps> = unsafe { std::mem::transmute(as_dyn) };
// let as_dyn: &dyn AnyProps = self.bump().alloc(vcomp);
// todo: clean up borrowed props
// if !P::IS_STATIC {
// let vcomp = &*vcomp;
@@ -89,8 +90,9 @@ impl ScopeState {
DynamicNode::Component(VComponent {
name: fn_name,
render_fn: component as *const (),
static_props: P::IS_STATIC,
props: Cell::new(detached_dyn),
props: Cell::new(Some(extended)),
placeholder: Cell::new(None),
scope: Cell::new(None),
})
@@ -178,7 +180,6 @@ impl<'a, 'b> IntoDynNode<'a> for LazyNodes<'a, 'b> {
fn into_vnode(self, cx: &'a ScopeState) -> DynamicNode<'a> {
DynamicNode::Fragment(VFragment {
nodes: cx.bump().alloc([self.call(cx)]),
inner: false,
})
}
}
@@ -248,10 +249,7 @@ where
match children.len() {
0 => DynamicNode::Placeholder(Cell::new(ElementId(0))),
_ => DynamicNode::Fragment(VFragment {
inner: false,
nodes: children,
}),
_ => DynamicNode::Fragment(VFragment { nodes: children }),
}
}
}
+13 -55
View File
@@ -131,26 +131,35 @@ impl<'a> DynamicNode<'a> {
}
}
#[derive(Debug)]
pub struct VComponent<'a> {
pub name: &'static str,
pub static_props: bool,
pub props: Cell<*mut dyn AnyProps<'a>>,
pub placeholder: Cell<Option<ElementId>>,
pub scope: Cell<Option<ScopeId>>,
pub props: Cell<Option<Box<dyn AnyProps<'a> + 'a>>>,
pub render_fn: *const (),
}
impl<'a> std::fmt::Debug for VComponent<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("VComponent")
.field("name", &self.name)
.field("static_props", &self.static_props)
.field("placeholder", &self.placeholder)
.field("scope", &self.scope)
.finish()
}
}
#[derive(Debug)]
pub struct VText<'a> {
pub id: Cell<ElementId>,
pub value: &'a str,
pub inner: bool,
}
#[derive(Debug)]
pub struct VFragment<'a> {
pub nodes: &'a [VNode<'a>],
pub inner: bool,
}
#[derive(Debug)]
@@ -268,54 +277,3 @@ fn what_are_the_sizes() {
dbg!(std::mem::size_of::<Template>());
dbg!(std::mem::size_of::<TemplateNode>());
}
/*
SSR includes data-id which allows O(1) hydration
we read the edit stream dn then we can just rehydare
ideas:
- IDs for lookup
- use edit stream to hydrate
- write comments to dom that specify size of children
IDs for lookups
- adds noise to generated html
- doesnt work for text nodes
- suspense could cause ordering to be weird
Names for lookups:
- label each root or something with the template name
- label each dynamic node with a path
- noisy too
- allows reverse lookups
Ideal:
- no noise in the dom
- fast, ideally O(1)
- able to pick apart text nodes that get merged during SSR
--> render vdom
--> traverse vdom and real dom simultaneously
IE
div {
div {
div {
"thing"
}
}
}
*/
+8 -1
View File
@@ -1,6 +1,7 @@
use crate::{
any_props::AnyProps,
bump_frame::BumpFrame,
diff::DirtyScope,
factory::RenderReturn,
innerlude::{SuspenseId, SuspenseLeaf},
scheduler::RcWake,
@@ -16,7 +17,7 @@ use std::{
};
impl VirtualDom {
pub(super) fn new_scope(&mut self, props: *mut dyn AnyProps<'static>) -> &mut ScopeState {
pub(super) fn new_scope(&mut self, props: *const dyn AnyProps<'static>) -> &mut ScopeState {
let parent = self.acquire_current_scope_raw();
let entry = self.scopes.vacant_entry();
let height = unsafe { parent.map(|f| (*f).height).unwrap_or(0) + 1 };
@@ -125,6 +126,12 @@ impl VirtualDom {
// And move the render generation forward by one
scope.render_cnt.set(scope.render_cnt.get() + 1);
// remove this scope from dirty scopes
self.dirty_scopes.remove(&DirtyScope {
height: scope.height,
id: scope.id,
});
// rebind the lifetime now that its stored internally
unsafe { mem::transmute(alloced) }
}
+2 -2
View File
@@ -3,7 +3,7 @@
//! This module provides the primary mechanics to create a hook-based, concurrent VDOM for Rust.
use crate::{
any_props::VComponentProps,
any_props::VProps,
arena::ElementId,
arena::ElementRef,
diff::DirtyScope,
@@ -240,7 +240,7 @@ impl VirtualDom {
finished_fibers: Vec::new(),
};
let root = dom.new_scope(Box::into_raw(Box::new(VComponentProps::new(
let root = dom.new_scope(Box::leak(Box::new(VProps::new(
root,
|_, _| unreachable!(),
root_props,