2021-02-14 23:03:16 +00:00
|
|
|
use crate::context::hooks::Hook;
|
2021-02-12 21:11:33 +00:00
|
|
|
use crate::innerlude::*;
|
2021-02-07 22:38:17 +00:00
|
|
|
use crate::nodes::VNode;
|
|
|
|
use bumpalo::Bump;
|
2021-02-08 21:57:34 +00:00
|
|
|
use generational_arena::Index;
|
2021-02-17 15:53:55 +00:00
|
|
|
use owning_ref::StableAddress;
|
2021-02-07 22:38:17 +00:00
|
|
|
use std::{
|
2021-02-17 15:53:55 +00:00
|
|
|
any::TypeId,
|
|
|
|
borrow::{Borrow, BorrowMut},
|
|
|
|
cell::{RefCell, UnsafeCell},
|
|
|
|
future::Future,
|
|
|
|
marker::PhantomData,
|
|
|
|
ops::{Deref, DerefMut},
|
|
|
|
sync::atomic::AtomicUsize,
|
|
|
|
todo,
|
2021-02-07 22:38:17 +00:00
|
|
|
};
|
|
|
|
|
2021-02-08 00:14:04 +00:00
|
|
|
/// Every component in Dioxus is represented by a `Scope`.
|
|
|
|
///
|
|
|
|
/// Scopes contain the state for hooks, the component's props, and other lifecycle information.
|
|
|
|
///
|
|
|
|
/// Scopes are allocated in a generational arena. As components are mounted/unmounted, they will replace slots of dead components.
|
|
|
|
/// The actual contents of the hooks, though, will be allocated with the standard allocator. These should not allocate as frequently.
|
2021-02-19 01:04:25 +00:00
|
|
|
pub struct Scope {
|
|
|
|
// pub(crate) struct Scope {
|
2021-02-12 08:07:35 +00:00
|
|
|
// TODO @Jon
|
2021-02-08 00:14:04 +00:00
|
|
|
// These hooks are actually references into the hook arena
|
|
|
|
// These two could be combined with "OwningRef" to remove unsafe usage
|
2021-02-12 08:07:35 +00:00
|
|
|
// could also use ourborous
|
2021-02-12 21:11:33 +00:00
|
|
|
pub hooks: RefCell<Vec<*mut Hook>>,
|
|
|
|
pub hook_arena: typed_arena::Arena<Hook>,
|
2021-02-08 00:14:04 +00:00
|
|
|
|
2021-02-08 21:57:34 +00:00
|
|
|
// Map to the parent
|
2021-02-12 21:11:33 +00:00
|
|
|
pub parent: Option<Index>,
|
2021-02-08 21:57:34 +00:00
|
|
|
|
2021-02-12 08:07:35 +00:00
|
|
|
// todo, do better with the active frame stuff
|
|
|
|
// somehow build this vnode with a lifetime tied to self
|
2021-02-14 23:03:16 +00:00
|
|
|
// This root node has "static" lifetime, but it's really not static.
|
|
|
|
// It's goverened by the oldest of the two frames and is switched every time a new render occurs
|
|
|
|
// Use this node as if it were static is unsafe, and needs to be fixed with ourborous or owning ref
|
|
|
|
// ! do not copy this reference are things WILL break !
|
2021-02-17 15:53:55 +00:00
|
|
|
pub frames: ActiveFrame,
|
2021-02-12 08:07:35 +00:00
|
|
|
|
|
|
|
// IE Which listeners need to be woken up?
|
2021-02-12 21:11:33 +00:00
|
|
|
pub listeners: Vec<Box<dyn Fn()>>,
|
2021-02-12 08:07:35 +00:00
|
|
|
|
2021-02-18 01:56:53 +00:00
|
|
|
pub props: Box<dyn std::any::Any>,
|
|
|
|
|
2021-02-12 08:07:35 +00:00
|
|
|
//
|
2021-02-12 21:11:33 +00:00
|
|
|
pub props_type: TypeId,
|
|
|
|
pub caller: *const i32,
|
2021-02-07 22:38:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Scope {
|
|
|
|
// create a new scope from a function
|
2021-02-19 01:04:25 +00:00
|
|
|
pub fn new<T: 'static>(
|
|
|
|
// pub(crate) fn new<T: 'static>(
|
2021-02-18 01:56:53 +00:00
|
|
|
f: FC<T>,
|
|
|
|
props: impl Properties + 'static,
|
|
|
|
parent: Option<Index>,
|
|
|
|
) -> Self {
|
2021-02-07 22:38:17 +00:00
|
|
|
// Capture the props type
|
|
|
|
let props_type = TypeId::of::<T>();
|
2021-02-08 21:57:34 +00:00
|
|
|
let hook_arena = typed_arena::Arena::new();
|
2021-02-07 22:38:17 +00:00
|
|
|
let hooks = RefCell::new(Vec::new());
|
|
|
|
|
2021-02-14 23:03:16 +00:00
|
|
|
// Capture the caller
|
2021-02-07 22:38:17 +00:00
|
|
|
let caller = f as *const i32;
|
|
|
|
|
2021-02-12 08:07:35 +00:00
|
|
|
let listeners = Vec::new();
|
|
|
|
|
2021-02-18 01:56:53 +00:00
|
|
|
let old_frame = BumpFrame {
|
|
|
|
bump: Bump::new(),
|
|
|
|
head_node: VNode::text(""),
|
|
|
|
};
|
2021-02-12 08:07:35 +00:00
|
|
|
|
2021-02-18 01:56:53 +00:00
|
|
|
let new_frame = BumpFrame {
|
|
|
|
bump: Bump::new(),
|
|
|
|
head_node: VNode::text(""),
|
|
|
|
};
|
2021-02-14 23:03:16 +00:00
|
|
|
|
2021-02-17 15:53:55 +00:00
|
|
|
let frames = ActiveFrame::from_frames(old_frame, new_frame);
|
2021-02-18 01:56:53 +00:00
|
|
|
let props = Box::new(props);
|
2021-02-12 08:07:35 +00:00
|
|
|
|
2021-02-07 22:38:17 +00:00
|
|
|
Self {
|
2021-02-08 21:57:34 +00:00
|
|
|
hook_arena,
|
2021-02-07 22:38:17 +00:00
|
|
|
hooks,
|
|
|
|
props_type,
|
|
|
|
caller,
|
2021-02-17 15:53:55 +00:00
|
|
|
frames,
|
2021-02-12 08:07:35 +00:00
|
|
|
listeners,
|
2021-02-08 21:57:34 +00:00
|
|
|
parent,
|
2021-02-18 01:56:53 +00:00
|
|
|
props,
|
2021-02-07 22:38:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-18 01:56:53 +00:00
|
|
|
/// Update this component's props with a new set of props
|
|
|
|
///
|
|
|
|
///
|
|
|
|
pub(crate) fn update_props<P: Properties + Sized + 'static>(
|
|
|
|
&mut self,
|
|
|
|
new_props: Box<P>,
|
|
|
|
) -> crate::error::Result<()> {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-02-12 08:07:35 +00:00
|
|
|
/// 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
|
|
|
|
///
|
2021-02-12 21:11:33 +00:00
|
|
|
/// Props is ?Sized because we borrow the props and don't need to know the size. P (sized) is used as a marker (unsized)
|
2021-02-19 01:04:25 +00:00
|
|
|
pub fn run<'bump, P: Properties + Sized + 'static>(&'bump mut self) {
|
2021-02-18 01:56:53 +00:00
|
|
|
let frame = {
|
|
|
|
let frame = self.frames.next();
|
|
|
|
frame.bump.reset();
|
|
|
|
frame
|
|
|
|
};
|
|
|
|
|
|
|
|
let ctx: Context<'bump> = Context {
|
|
|
|
arena: &self.hook_arena,
|
|
|
|
hooks: &self.hooks,
|
|
|
|
bump: &frame.bump,
|
|
|
|
idx: 0.into(),
|
|
|
|
_p: PhantomData {},
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
let caller = unsafe { std::mem::transmute::<*const i32, FC<P>>(self.caller) };
|
|
|
|
let nodes: VNode<'bump> = caller(ctx, self.props.downcast_ref::<P>().unwrap());
|
|
|
|
|
|
|
|
/*
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
|
|
|
|
let unsafe_node = unsafe { std::mem::transmute::<VNode<'bump>, VNode<'static>>(nodes) };
|
|
|
|
frame.head_node = unsafe_node;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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> {
|
2021-02-19 01:04:25 +00:00
|
|
|
self.frames.current_head_node()
|
2021-02-18 01:56:53 +00:00
|
|
|
}
|
|
|
|
pub fn prev_root_node<'bump>(&'bump self) -> &'bump VNode<'bump> {
|
2021-02-14 23:03:16 +00:00
|
|
|
todo!()
|
2021-02-12 08:07:35 +00:00
|
|
|
}
|
2021-02-07 22:38:17 +00:00
|
|
|
}
|
2021-02-18 01:56:53 +00:00
|
|
|
|
|
|
|
pub struct BumpFrame {
|
|
|
|
pub bump: Bump,
|
|
|
|
pub head_node: VNode<'static>,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct ActiveFrame {
|
|
|
|
pub idx: AtomicUsize,
|
|
|
|
pub frames: [BumpFrame; 2],
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ActiveFrame {
|
|
|
|
fn from_frames(a: BumpFrame, b: BumpFrame) -> Self {
|
|
|
|
Self {
|
|
|
|
idx: 0.into(),
|
|
|
|
frames: [a, b],
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-19 01:04:25 +00:00
|
|
|
fn current_head_node<'b>(&'b self) -> &'b VNode<'b> {
|
|
|
|
let cur_idx = self.idx.borrow().load(std::sync::atomic::Ordering::Relaxed);
|
|
|
|
let raw_node = &self.frames[cur_idx];
|
|
|
|
unsafe {
|
|
|
|
let unsafe_head = &raw_node.head_node;
|
|
|
|
let safe_node = std::mem::transmute::<&VNode<'static>, &VNode<'b>>(unsafe_head);
|
|
|
|
safe_node
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-18 01:56:53 +00:00
|
|
|
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);
|
|
|
|
match cur % 1 {
|
|
|
|
1 => &mut self.frames[1],
|
|
|
|
0 => &mut self.frames[0],
|
|
|
|
_ => unreachable!("mod cannot by non-zero"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|