|
@@ -12,6 +12,7 @@ use std::{
|
|
|
marker::PhantomData,
|
|
|
ops::{Deref, DerefMut},
|
|
|
sync::atomic::AtomicUsize,
|
|
|
+ sync::atomic::Ordering,
|
|
|
todo,
|
|
|
};
|
|
|
|
|
@@ -40,34 +41,23 @@ pub struct Scope {
|
|
|
|
|
|
// lying, cheating reference >:(
|
|
|
pub props: Box<dyn std::any::Any>,
|
|
|
- // pub props: Box<dyn Properties>,
|
|
|
|
|
|
- //
|
|
|
// pub props_type: TypeId,
|
|
|
pub caller: *const (),
|
|
|
}
|
|
|
|
|
|
impl Scope {
|
|
|
// create a new scope from a function
|
|
|
- pub fn new<'a, P1, P2: 'static>(
|
|
|
- // pub fn new<'a, P: Properties, PFree: P + 'a, PLocked: P + 'static>(
|
|
|
- f: FC<P1>,
|
|
|
- props: P1,
|
|
|
- parent: Option<Index>,
|
|
|
- ) -> Self
|
|
|
-// where
|
|
|
- // PFree: 'a,
|
|
|
- // PLocked: 'static,
|
|
|
- {
|
|
|
- // Capture the props type
|
|
|
- // let props_type = TypeId::of::<P>();
|
|
|
+ pub fn new<'a, P1, P2: 'static>(f: FC<P1>, props: P1, parent: Option<Index>) -> Self {
|
|
|
let hook_arena = typed_arena::Arena::new();
|
|
|
let hooks = RefCell::new(Vec::new());
|
|
|
|
|
|
// Capture the caller
|
|
|
let caller = f as *const ();
|
|
|
|
|
|
- let listeners = Vec::new();
|
|
|
+ let listeners: Vec<Box<dyn Fn()>> = vec![Box::new(|| {
|
|
|
+ log::info!("Base listener called");
|
|
|
+ })];
|
|
|
|
|
|
let old_frame = BumpFrame {
|
|
|
bump: Bump::new(),
|
|
@@ -84,16 +74,11 @@ impl Scope {
|
|
|
// box the props
|
|
|
let props = Box::new(props);
|
|
|
|
|
|
- // erase the lifetime
|
|
|
- // we'll manage this with dom lifecycle
|
|
|
-
|
|
|
let props = unsafe { std::mem::transmute::<_, Box<P2>>(props) };
|
|
|
|
|
|
- // todo!()
|
|
|
Self {
|
|
|
hook_arena,
|
|
|
hooks,
|
|
|
- // props_type,
|
|
|
caller,
|
|
|
frames,
|
|
|
listeners,
|
|
@@ -102,13 +87,6 @@ impl Scope {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /// Update this component's props with a new set of props, remotely
|
|
|
- ///
|
|
|
- ///
|
|
|
- pub(crate) fn update_props<'a, P>(&self, _new_props: P) -> crate::error::Result<()> {
|
|
|
- Ok(())
|
|
|
- }
|
|
|
-
|
|
|
/// Create a new context and run the component with references from the Virtual Dom
|
|
|
/// This function downcasts the function pointer based on the stored props_type
|
|
|
///
|
|
@@ -120,67 +98,67 @@ impl Scope {
|
|
|
frame
|
|
|
};
|
|
|
|
|
|
+ let node_slot = std::rc::Rc::new(RefCell::new(None));
|
|
|
let ctx: Context<'bump> = Context {
|
|
|
arena: &self.hook_arena,
|
|
|
hooks: &self.hooks,
|
|
|
bump: &frame.bump,
|
|
|
idx: 0.into(),
|
|
|
_p: PhantomData {},
|
|
|
+ final_nodes: node_slot.clone(),
|
|
|
};
|
|
|
|
|
|
unsafe {
|
|
|
+ /*
|
|
|
+ SAFETY ALERT
|
|
|
+
|
|
|
+ This particular usage of transmute is outlined in its docs https://doc.rust-lang.org/std/mem/fn.transmute.html
|
|
|
+ We hide the generic bound on the function item by casting it to raw pointer. When the function is actually called,
|
|
|
+ we transmute the function back using the props as reference.
|
|
|
+
|
|
|
+ we could do a better check to make sure that the TypeID is correct before casting
|
|
|
+ --
|
|
|
+ This is safe because we check that the generic type matches before casting.
|
|
|
+ */
|
|
|
// we use plocked to be able to remove the borrowed lifetime
|
|
|
// these lifetimes could be very broken, so we need to dynamically manage them
|
|
|
let caller = std::mem::transmute::<*const (), FC<PLocked>>(self.caller);
|
|
|
let props = self.props.downcast_ref::<PLocked>().unwrap();
|
|
|
+
|
|
|
+ // Note that the actual modification of the vnode head element occurs during this call
|
|
|
let _nodes: DomTree = caller(ctx, props);
|
|
|
- todo!("absorb domtree into self")
|
|
|
- // let nodes: VNode<'bump> = caller(ctx, props);
|
|
|
|
|
|
- // let unsafe_node = std::mem::transmute::<VNode<'bump>, VNode<'static>>(nodes);
|
|
|
- // frame.head_node = unsafe_node;
|
|
|
+ /*
|
|
|
+ SAFETY ALERT
|
|
|
+
|
|
|
+ DO NOT USE THIS VNODE WITHOUT THE APPOPRIATE ACCESSORS.
|
|
|
+ KEEPING THIS STATIC REFERENCE CAN LEAD TO UB.
|
|
|
+
|
|
|
+ Some things to note:
|
|
|
+ - The VNode itself is bound to the lifetime, but it itself is owned by scope.
|
|
|
+ - The VNode has a private API and can only be used from accessors.
|
|
|
+ - Public API cannot drop or destructure VNode
|
|
|
+ */
|
|
|
+ // the nodes we care about have been unsafely extended to a static lifetime in context
|
|
|
+ frame.head_node = node_slot
|
|
|
+ .deref()
|
|
|
+ .borrow_mut()
|
|
|
+ .take()
|
|
|
+ .expect("Viewing did not happen");
|
|
|
}
|
|
|
- /*
|
|
|
- SAFETY ALERT
|
|
|
-
|
|
|
- This particular usage of transmute is outlined in its docs https://doc.rust-lang.org/std/mem/fn.transmute.html
|
|
|
- We hide the generic bound on the function item by casting it to raw pointer. When the function is actually called,
|
|
|
- we transmute the function back using the props as reference.
|
|
|
-
|
|
|
- we could do a better check to make sure that the TypeID is correct before casting
|
|
|
- --
|
|
|
- This is safe because we check that the generic type matches before casting.
|
|
|
- */
|
|
|
-
|
|
|
- /*
|
|
|
- SAFETY ALERT
|
|
|
-
|
|
|
- DO NOT USE THIS VNODE WITHOUT THE APPOPRIATE ACCESSORS.
|
|
|
- KEEPING THIS STATIC REFERENCE CAN LEAD TO UB.
|
|
|
-
|
|
|
- Some things to note:
|
|
|
- - The VNode itself is bound to the lifetime, but it itself is owned by scope.
|
|
|
- - The VNode has a private API and can only be used from accessors.
|
|
|
- - Public API cannot drop or destructure VNode
|
|
|
- */
|
|
|
}
|
|
|
|
|
|
/// Accessor to get the root node and its children (safely)\
|
|
|
/// Scope is self-referntial, so we are forced to use the 'static lifetime to cheat
|
|
|
- pub fn current_root_node<'bump>(&'bump self) -> &'bump VNode<'bump> {
|
|
|
+ pub fn new_frame<'bump>(&'bump self) -> &'bump VNode<'bump> {
|
|
|
self.frames.current_head_node()
|
|
|
}
|
|
|
|
|
|
- pub fn prev_root_node<'bump>(&'bump self) -> &'bump VNode<'bump> {
|
|
|
- todo!()
|
|
|
+ pub fn old_frame<'bump>(&'bump self) -> &'bump VNode<'bump> {
|
|
|
+ self.frames.prev_head_node()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-pub struct BumpFrame {
|
|
|
- pub bump: Bump,
|
|
|
- pub head_node: VNode<'static>,
|
|
|
-}
|
|
|
-
|
|
|
// todo, do better with the active frame stuff
|
|
|
// somehow build this vnode with a lifetime tied to self
|
|
|
// This root node has "static" lifetime, but it's really not static.
|
|
@@ -192,6 +170,11 @@ pub struct ActiveFrame {
|
|
|
pub frames: [BumpFrame; 2],
|
|
|
}
|
|
|
|
|
|
+pub struct BumpFrame {
|
|
|
+ pub bump: Bump,
|
|
|
+ pub head_node: VNode<'static>,
|
|
|
+}
|
|
|
+
|
|
|
impl ActiveFrame {
|
|
|
fn from_frames(a: BumpFrame, b: BumpFrame) -> Self {
|
|
|
Self {
|
|
@@ -201,8 +184,23 @@ impl ActiveFrame {
|
|
|
}
|
|
|
|
|
|
fn current_head_node<'b>(&'b self) -> &'b VNode<'b> {
|
|
|
- let cur_idx = self.idx.borrow().load(std::sync::atomic::Ordering::Relaxed) % 1;
|
|
|
- let raw_node = &self.frames[cur_idx];
|
|
|
+ let raw_node = match self.idx.borrow().load(Ordering::Relaxed) & 1 != 0 {
|
|
|
+ true => &self.frames[0],
|
|
|
+ false => &self.frames[1],
|
|
|
+ };
|
|
|
+ unsafe {
|
|
|
+ let unsafe_head = &raw_node.head_node;
|
|
|
+ let safe_node = std::mem::transmute::<&VNode<'static>, &VNode<'b>>(unsafe_head);
|
|
|
+ safe_node
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fn prev_head_node<'b>(&'b self) -> &'b VNode<'b> {
|
|
|
+ let raw_node = match self.idx.borrow().load(Ordering::Relaxed) & 1 == 0 {
|
|
|
+ true => &self.frames[0],
|
|
|
+ false => &self.frames[1],
|
|
|
+ };
|
|
|
+
|
|
|
unsafe {
|
|
|
let unsafe_head = &raw_node.head_node;
|
|
|
let safe_node = std::mem::transmute::<&VNode<'static>, &VNode<'b>>(unsafe_head);
|
|
@@ -211,8 +209,8 @@ impl ActiveFrame {
|
|
|
}
|
|
|
|
|
|
fn next(&mut self) -> &mut BumpFrame {
|
|
|
- self.idx.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
|
|
- let cur = self.idx.borrow().load(std::sync::atomic::Ordering::Relaxed);
|
|
|
+ self.idx.fetch_add(1, Ordering::Relaxed);
|
|
|
+ let cur = self.idx.borrow().load(Ordering::Relaxed);
|
|
|
match cur % 1 {
|
|
|
1 => &mut self.frames[1],
|
|
|
0 => &mut self.frames[0],
|
|
@@ -276,7 +274,7 @@ mod tests {
|
|
|
})
|
|
|
}
|
|
|
|
|
|
- fn child_example(ctx: Context, props: &ExampleProps) -> DomTree {
|
|
|
+ fn child_example<'b>(ctx: Context<'b>, props: &'b ExampleProps) -> DomTree {
|
|
|
ctx.view(move |b| {
|
|
|
div(b)
|
|
|
.child(text(props.name))
|