2022-09-30 19:03:06 +00:00
use crate ::{
dynamic_template_context ::TemplateContext ,
innerlude ::* ,
template ::{
TemplateAttribute , TemplateElement , TemplateNode , TemplateNodeId , TemplateNodeType ,
TemplateValue , TextTemplateSegment ,
} ,
unsafe_utils ::extend_vnode ,
} ;
2021-12-26 19:22:30 +00:00
use bumpalo ::Bump ;
2021-11-05 21:15:59 +00:00
use futures_channel ::mpsc ::UnboundedSender ;
2021-12-26 19:22:30 +00:00
use fxhash ::FxHashMap ;
2021-12-21 03:33:13 +00:00
use slab ::Slab ;
2021-07-09 05:42:26 +00:00
use std ::{
any ::{ Any , TypeId } ,
2022-09-30 19:03:06 +00:00
cell ::{ Cell , Ref , RefCell } ,
2022-02-04 23:05:55 +00:00
collections ::{ HashMap , HashSet } ,
2021-07-09 05:42:26 +00:00
future ::Future ,
2021-12-13 00:47:13 +00:00
pin ::Pin ,
2021-07-11 18:49:52 +00:00
rc ::Rc ,
2022-02-23 18:48:14 +00:00
sync ::Arc ,
2021-07-09 05:42:26 +00:00
} ;
2022-01-30 19:08:03 +00:00
/// for traceability, we use the raw fn pointer to identify the function
2022-01-31 07:40:12 +00:00
/// we also get the component name, but that's not necessarily unique in the app
pub ( crate ) type ComponentPtr = * mut std ::os ::raw ::c_void ;
2021-12-21 03:33:13 +00:00
pub ( crate ) struct Heuristic {
hook_arena_size : usize ,
node_arena_size : usize ,
}
// a slab-like arena with stable references even when new scopes are allocated
// uses a bump arena as a backing
//
// has an internal heuristics engine to pre-allocate arenas to the right size
pub ( crate ) struct ScopeArena {
2021-12-26 19:22:30 +00:00
pub scope_gen : Cell < usize > ,
2021-12-21 03:33:13 +00:00
pub bump : Bump ,
pub scopes : RefCell < FxHashMap < ScopeId , * mut ScopeState > > ,
2022-01-31 07:40:12 +00:00
pub heuristics : RefCell < FxHashMap < ComponentPtr , Heuristic > > ,
2021-12-21 03:33:13 +00:00
pub free_scopes : RefCell < Vec < * mut ScopeState > > ,
pub nodes : RefCell < Slab < * const VNode < 'static > > > ,
2021-12-26 19:22:30 +00:00
pub tasks : Rc < TaskQueue > ,
2022-09-30 19:03:06 +00:00
pub template_resolver : RefCell < TemplateResolver > ,
pub templates : Rc < RefCell < FxHashMap < TemplateId , Rc < RefCell < Template > > > > > ,
// this is used to store intermidiate artifacts of creating templates, so that the lifetime aligns with Mutations<'bump>.
pub template_bump : Bump ,
2021-12-21 03:33:13 +00:00
}
impl ScopeArena {
pub ( crate ) fn new ( sender : UnboundedSender < SchedulerMsg > ) -> Self {
let bump = Bump ::new ( ) ;
// allocate a container for the root element
// this will *never* show up in the diffing process
// todo: figure out why this is necessary. i forgot. whoops.
let el = bump . alloc ( VElement {
2021-12-21 05:46:10 +00:00
tag : " root " ,
2021-12-21 03:33:13 +00:00
namespace : None ,
key : None ,
2021-12-21 05:46:10 +00:00
id : Cell ::new ( Some ( ElementId ( 0 ) ) ) ,
parent : Default ::default ( ) ,
2021-12-21 03:33:13 +00:00
listeners : & [ ] ,
attributes : & [ ] ,
children : & [ ] ,
} ) ;
let node = bump . alloc ( VNode ::Element ( el ) ) ;
let mut nodes = Slab ::new ( ) ;
let root_id = nodes . insert ( unsafe { std ::mem ::transmute ( node as * const _ ) } ) ;
debug_assert_eq! ( root_id , 0 ) ;
Self {
2021-12-26 19:22:30 +00:00
scope_gen : Cell ::new ( 0 ) ,
2021-12-21 03:33:13 +00:00
bump ,
scopes : RefCell ::new ( FxHashMap ::default ( ) ) ,
heuristics : RefCell ::new ( FxHashMap ::default ( ) ) ,
free_scopes : RefCell ::new ( Vec ::new ( ) ) ,
nodes : RefCell ::new ( nodes ) ,
2022-02-27 22:03:43 +00:00
tasks : Rc ::new ( TaskQueue {
tasks : RefCell ::new ( FxHashMap ::default ( ) ) ,
task_map : RefCell ::new ( FxHashMap ::default ( ) ) ,
gen : Cell ::new ( 0 ) ,
sender ,
} ) ,
2022-09-30 19:03:06 +00:00
template_resolver : RefCell ::new ( TemplateResolver ::default ( ) ) ,
templates : Rc ::new ( RefCell ::new ( FxHashMap ::default ( ) ) ) ,
template_bump : Bump ::new ( ) ,
2021-12-21 03:33:13 +00:00
}
}
/// Safety:
2022-01-22 03:43:43 +00:00
/// - Obtaining a mutable reference to any Scope is unsafe
2021-12-21 03:33:13 +00:00
/// - Scopes use interior mutability when sharing data into components
pub ( crate ) fn get_scope ( & self , id : ScopeId ) -> Option < & ScopeState > {
unsafe { self . scopes . borrow ( ) . get ( & id ) . map ( | f | & * * f ) }
}
pub ( crate ) fn get_scope_raw ( & self , id : ScopeId ) -> Option < * mut ScopeState > {
self . scopes . borrow ( ) . get ( & id ) . copied ( )
}
pub ( crate ) fn new_with_key (
& self ,
2022-01-31 07:40:12 +00:00
fc_ptr : ComponentPtr ,
2021-12-21 03:33:13 +00:00
vcomp : Box < dyn AnyProps > ,
parent_scope : Option < ScopeId > ,
2022-09-30 19:03:06 +00:00
container : GlobalNodeId ,
2021-12-21 03:33:13 +00:00
) -> ScopeId {
2021-12-21 04:31:33 +00:00
// Increment the ScopeId system. ScopeIDs are never reused
2021-12-26 19:22:30 +00:00
let new_scope_id = ScopeId ( self . scope_gen . get ( ) ) ;
self . scope_gen . set ( self . scope_gen . get ( ) + 1 ) ;
2021-12-21 04:31:33 +00:00
// Get the height of the scope
2021-12-21 03:33:13 +00:00
let height = parent_scope
2022-02-25 05:35:25 +00:00
. and_then ( | id | self . get_scope ( id ) . map ( | scope | scope . height + 1 ) )
2022-01-31 17:29:11 +00:00
. unwrap_or_default ( ) ;
2021-12-21 03:33:13 +00:00
2022-02-25 05:35:25 +00:00
let parent_scope = parent_scope . and_then ( | f | self . get_scope_raw ( f ) ) ;
2021-12-21 04:31:33 +00:00
2021-12-21 03:33:13 +00:00
/*
2022-01-22 03:43:43 +00:00
This scopearena aggressively reuses old scopes when possible .
2021-12-21 03:33:13 +00:00
We try to minimize the new allocations for props / arenas .
However , this will probably lead to some sort of fragmentation .
I ' m not exactly sure how to improve this today .
* /
2021-12-21 04:31:33 +00:00
if let Some ( old_scope ) = self . free_scopes . borrow_mut ( ) . pop ( ) {
// reuse the old scope
let scope = unsafe { & mut * old_scope } ;
2022-01-31 07:33:16 +00:00
scope . container = container ;
scope . our_arena_idx = new_scope_id ;
2021-12-21 04:31:33 +00:00
scope . parent_scope = parent_scope ;
scope . height = height ;
2022-01-30 19:08:03 +00:00
scope . fnptr = fc_ptr ;
2022-01-31 07:33:16 +00:00
scope . props . get_mut ( ) . replace ( vcomp ) ;
scope . frames [ 0 ] . reset ( ) ;
scope . frames [ 1 ] . reset ( ) ;
scope . shared_contexts . get_mut ( ) . clear ( ) ;
scope . items . get_mut ( ) . listeners . clear ( ) ;
scope . items . get_mut ( ) . borrowed_props . clear ( ) ;
scope . hook_idx . set ( 0 ) ;
scope . hook_vals . get_mut ( ) . clear ( ) ;
2021-12-21 04:31:33 +00:00
let any_item = self . scopes . borrow_mut ( ) . insert ( new_scope_id , scope ) ;
debug_assert! ( any_item . is_none ( ) ) ;
} else {
// else create a new scope
2022-01-30 19:08:03 +00:00
let ( node_capacity , hook_capacity ) = self
. heuristics
. borrow ( )
. get ( & fc_ptr )
. map ( | h | ( h . node_arena_size , h . hook_arena_size ) )
. unwrap_or_default ( ) ;
2021-12-21 04:31:33 +00:00
self . scopes . borrow_mut ( ) . insert (
new_scope_id ,
2022-01-30 19:08:03 +00:00
self . bump . alloc ( ScopeState {
2021-12-21 03:33:13 +00:00
container ,
2022-01-30 19:08:03 +00:00
our_arena_idx : new_scope_id ,
2021-12-21 03:33:13 +00:00
parent_scope ,
2022-01-30 19:08:03 +00:00
height ,
fnptr : fc_ptr ,
props : RefCell ::new ( Some ( vcomp ) ) ,
frames : [ BumpFrame ::new ( node_capacity ) , BumpFrame ::new ( node_capacity ) ] ,
generation : 0. into ( ) ,
tasks : self . tasks . clone ( ) ,
2022-07-03 04:11:28 +00:00
shared_contexts : RefCell ::default ( ) ,
2022-01-30 19:08:03 +00:00
items : RefCell ::new ( SelfReferentialItems {
2022-07-03 04:11:28 +00:00
listeners : Vec ::default ( ) ,
borrowed_props : Vec ::default ( ) ,
2022-01-30 19:08:03 +00:00
} ) ,
hook_arena : Bump ::new ( ) ,
hook_vals : RefCell ::new ( Vec ::with_capacity ( hook_capacity ) ) ,
2022-07-03 04:11:28 +00:00
hook_idx : Cell ::default ( ) ,
2022-09-30 19:03:06 +00:00
templates : self . templates . clone ( ) ,
2022-01-30 19:08:03 +00:00
} ) ,
2021-12-21 04:31:33 +00:00
) ;
2021-12-21 03:33:13 +00:00
}
new_scope_id
}
// Removes a scope and its descendents from the arena
2022-07-03 04:11:28 +00:00
pub fn try_remove ( & self , id : ScopeId ) {
2021-12-21 03:33:13 +00:00
self . ensure_drop_safety ( id ) ;
2022-02-27 22:03:43 +00:00
// Dispose of any ongoing tasks
let mut tasks = self . tasks . tasks . borrow_mut ( ) ;
let mut task_map = self . tasks . task_map . borrow_mut ( ) ;
if let Some ( cur_tasks ) = task_map . remove ( & id ) {
for task in cur_tasks {
tasks . remove ( & task ) ;
}
}
2022-02-04 23:05:55 +00:00
2021-12-21 03:33:13 +00:00
// Safety:
// - ensure_drop_safety ensures that no references to this scope are in use
// - this raw pointer is removed from the map
let scope = unsafe { & mut * self . scopes . borrow_mut ( ) . remove ( & id ) . unwrap ( ) } ;
scope . reset ( ) ;
2022-01-31 08:27:43 +00:00
self . free_scopes . borrow_mut ( ) . push ( scope ) ;
2021-12-21 03:33:13 +00:00
}
pub fn reserve_node < ' a > ( & self , node : & ' a VNode < ' a > ) -> ElementId {
let mut els = self . nodes . borrow_mut ( ) ;
let entry = els . vacant_entry ( ) ;
let key = entry . key ( ) ;
let id = ElementId ( key ) ;
let node = unsafe { extend_vnode ( node ) } ;
entry . insert ( node as * const _ ) ;
id
}
pub fn update_node < ' a > ( & self , node : & ' a VNode < ' a > , id : ElementId ) {
2022-01-31 17:29:11 +00:00
let node = unsafe { extend_vnode ( node ) } ;
* self . nodes . borrow_mut ( ) . get_mut ( id . 0 ) . unwrap ( ) = node ;
2021-12-21 03:33:13 +00:00
}
pub fn collect_garbage ( & self , id : ElementId ) {
2022-01-31 08:04:47 +00:00
self . nodes . borrow_mut ( ) . remove ( id . 0 ) ;
2021-12-21 03:33:13 +00:00
}
/// This method cleans up any references to data held within our hook list. This prevents mutable aliasing from
/// causing UB in our tree.
///
/// This works by cleaning up our references from the bottom of the tree to the top. The directed graph of components
/// essentially forms a dependency tree that we can traverse from the bottom to the top. As we traverse, we remove
/// any possible references to the data in the hook list.
///
/// References to hook data can only be stored in listeners and component props. During diffing, we make sure to log
/// all listeners and borrowed props so we can clear them here.
///
/// This also makes sure that drop order is consistent and predictable. All resources that rely on being dropped will
/// be dropped.
pub ( crate ) fn ensure_drop_safety ( & self , scope_id : ScopeId ) {
if let Some ( scope ) = self . get_scope ( scope_id ) {
let mut items = scope . items . borrow_mut ( ) ;
// make sure we drop all borrowed props manually to guarantee that their drop implementation is called before we
// run the hooks (which hold an &mut Reference)
// recursively call ensure_drop_safety on all children
items . borrowed_props . drain ( .. ) . for_each ( | comp | {
2022-01-14 21:40:01 +00:00
if let Some ( scope_id ) = comp . scope . get ( ) {
self . ensure_drop_safety ( scope_id ) ;
}
2021-12-21 04:31:33 +00:00
drop ( comp . props . take ( ) ) ;
2021-12-21 03:33:13 +00:00
} ) ;
// Now that all the references are gone, we can safely drop our own references in our listeners.
items
. listeners
. drain ( .. )
2022-01-07 16:51:25 +00:00
. for_each ( | listener | drop ( listener . callback . borrow_mut ( ) . take ( ) ) ) ;
2021-12-21 03:33:13 +00:00
}
}
pub ( crate ) fn run_scope ( & self , id : ScopeId ) {
// Cycle to the next frame and then reset it
// This breaks any latent references, invalidating every pointer referencing into it.
// Remove all the outdated listeners
self . ensure_drop_safety ( id ) ;
// todo: we *know* that this is aliased by the contents of the scope itself
let scope = unsafe { & mut * self . get_scope_raw ( id ) . expect ( " could not find scope " ) } ;
2022-01-31 08:27:43 +00:00
log ::trace! ( " running scope {:?} symbol: {:?} " , id , scope . fnptr ) ;
2022-01-30 19:08:03 +00:00
2021-12-21 03:33:13 +00:00
// Safety:
// - We dropped the listeners, so no more &mut T can be used while these are held
// - All children nodes that rely on &mut T are replaced with a new reference
scope . hook_idx . set ( 0 ) ;
{
// Safety:
// - We've dropped all references to the wip bump frame with "ensure_drop_safety"
unsafe { scope . reset_wip_frame ( ) } ;
2021-12-26 19:22:30 +00:00
let items = scope . items . borrow ( ) ;
2021-12-21 03:33:13 +00:00
// guarantee that we haven't screwed up - there should be no latent references anywhere
debug_assert! ( items . listeners . is_empty ( ) ) ;
debug_assert! ( items . borrowed_props . is_empty ( ) ) ;
}
/*
If the component returns None , then we fill in a placeholder node . This will wipe what was there .
An alternate approach is to leave the Real Dom the same , but that can lead to safety issues and a lot more checks .
Instead , we just treat the ` None ` as a shortcut to placeholder .
If the developer wants to prevent a scope from updating , they should control its memoization instead .
Also , the way we implement hooks allows us to cut rendering short before the next hook is recalled .
I ' m not sure if React lets you abort the component early , but we let you do that .
* /
2021-12-21 04:31:33 +00:00
let props = scope . props . borrow ( ) ;
let render = props . as_ref ( ) . unwrap ( ) ;
if let Some ( node ) = render . render ( scope ) {
2021-12-21 03:33:13 +00:00
let frame = scope . wip_frame ( ) ;
let node = frame . bump . alloc ( node ) ;
frame . node . set ( unsafe { extend_vnode ( node ) } ) ;
} else {
let frame = scope . wip_frame ( ) ;
2022-01-31 07:44:16 +00:00
let node = frame
. bump
. alloc ( VNode ::Placeholder ( frame . bump . alloc ( VPlaceholder {
2022-07-03 04:11:28 +00:00
id : Cell ::default ( ) ,
2022-01-31 07:44:16 +00:00
} ) ) ) ;
2021-12-21 03:33:13 +00:00
frame . node . set ( unsafe { extend_vnode ( node ) } ) ;
}
// make the "wip frame" contents the "finished frame"
// any future dipping into completed nodes after "render" will go through "fin head"
scope . cycle_frame ( ) ;
}
2022-09-30 19:03:06 +00:00
pub fn call_listener_with_bubbling ( & self , event : & UserEvent , element : GlobalNodeId ) {
2021-12-21 03:33:13 +00:00
let nodes = self . nodes . borrow ( ) ;
let mut cur_el = Some ( element ) ;
2022-01-03 07:06:42 +00:00
let state = Rc ::new ( BubbleState ::new ( ) ) ;
2021-12-21 03:33:13 +00:00
while let Some ( id ) = cur_el . take ( ) {
2022-09-30 19:03:06 +00:00
if state . canceled . get ( ) {
// stop bubbling if canceled
return ;
}
match id {
GlobalNodeId ::TemplateId {
template_ref_id ,
template_node_id ,
} = > {
log ::trace! (
" looking for listener in {:?} in node {:?} " ,
template_ref_id ,
template_node_id
) ;
if let Some ( template ) = nodes . get ( template_ref_id . 0 ) {
let template = unsafe { & * * template } ;
if let VNode ::TemplateRef ( template_ref ) = template {
let templates = self . templates . borrow ( ) ;
let template = templates . get ( & template_ref . template_id ) . unwrap ( ) ;
cur_el = template . borrow ( ) . with_node (
template_node_id ,
bubble_template ,
bubble_template ,
(
& nodes ,
& template_ref . dynamic_context ,
2022-10-02 21:12:24 +00:00
event ,
2022-09-30 19:03:06 +00:00
& state ,
template_ref_id ,
) ,
) ;
}
}
}
GlobalNodeId ::VNodeId ( id ) = > {
if let Some ( el ) = nodes . get ( id . 0 ) {
let real_el = unsafe { & * * el } ;
log ::trace! ( " looking for listener on {:?} " , real_el ) ;
if let VNode ::Element ( real_el ) = real_el {
for listener in real_el . listeners . iter ( ) {
if listener . event = = event . name {
log ::trace! ( " calling listener {:?} " , listener . event ) ;
let mut cb = listener . callback . borrow_mut ( ) ;
if let Some ( cb ) = cb . as_mut ( ) {
// todo: arcs are pretty heavy to clone
// we really want to convert arc to rc
// unfortunately, the SchedulerMsg must be send/sync to be sent across threads
// we could convert arc to rc internally or something
( cb ) ( AnyEvent {
bubble_state : state . clone ( ) ,
data : event . data . clone ( ) ,
} ) ;
}
break ;
}
2022-01-03 07:06:42 +00:00
}
2022-09-30 19:03:06 +00:00
cur_el = real_el . parent . get ( ) ;
}
}
}
}
if ! event . bubbles {
return ;
}
}
2022-06-22 18:57:05 +00:00
2022-09-30 19:03:06 +00:00
fn bubble_template < ' b , Attributes , V , Children , Listeners , TextSegments , Text > (
node : & TemplateNode < Attributes , V , Children , Listeners , TextSegments , Text > ,
ctx : (
& Ref < Slab < * const VNode > > ,
& TemplateContext < ' b > ,
& UserEvent ,
& Rc < BubbleState > ,
ElementId ,
) ,
) -> Option < GlobalNodeId >
where
Attributes : AsRef < [ TemplateAttribute < V > ] > ,
V : TemplateValue ,
Children : AsRef < [ TemplateNodeId ] > ,
Listeners : AsRef < [ usize ] > ,
TextSegments : AsRef < [ TextTemplateSegment < Text > ] > ,
Text : AsRef < str > ,
{
let ( vnodes , dynamic_context , event , state , template_ref_id ) = ctx ;
if let TemplateNodeType ::Element ( el ) = & node . node_type {
let TemplateElement { listeners , .. } = el ;
for listener_idx in listeners . as_ref ( ) {
let listener = dynamic_context . resolve_listener ( * listener_idx ) ;
if listener . event = = event . name {
log ::trace! ( " calling listener {:?} " , listener . event ) ;
let mut cb = listener . callback . borrow_mut ( ) ;
if let Some ( cb ) = cb . as_mut ( ) {
// todo: arcs are pretty heavy to clone
// we really want to convert arc to rc
// unfortunately, the SchedulerMsg must be send/sync to be sent across threads
// we could convert arc to rc internally or something
( cb ) ( AnyEvent {
bubble_state : state . clone ( ) ,
data : event . data . clone ( ) ,
} ) ;
2021-12-21 03:33:13 +00:00
}
2022-09-30 19:03:06 +00:00
break ;
2021-12-21 03:33:13 +00:00
}
2022-09-30 19:03:06 +00:00
}
2021-12-21 03:33:13 +00:00
2022-09-30 19:03:06 +00:00
if let Some ( id ) = el . parent {
Some ( GlobalNodeId ::TemplateId {
template_ref_id ,
template_node_id : id ,
} )
} else {
vnodes . get ( template_ref_id . 0 ) . and_then ( | el | {
let real_el = unsafe { & * * el } ;
if let VNode ::Element ( real_el ) = real_el {
real_el . parent . get ( )
} else {
None
}
} )
2021-12-21 03:33:13 +00:00
}
2022-09-30 19:03:06 +00:00
} else {
None
2021-12-21 03:33:13 +00:00
}
}
}
// The head of the bumpframe is the first linked NodeLink
pub fn wip_head ( & self , id : ScopeId ) -> & VNode {
let scope = self . get_scope ( id ) . unwrap ( ) ;
let frame = scope . wip_frame ( ) ;
let node = unsafe { & * frame . node . get ( ) } ;
unsafe { extend_vnode ( node ) }
}
// The head of the bumpframe is the first linked NodeLink
pub fn fin_head ( & self , id : ScopeId ) -> & VNode {
let scope = self . get_scope ( id ) . unwrap ( ) ;
let frame = scope . fin_frame ( ) ;
let node = unsafe { & * frame . node . get ( ) } ;
unsafe { extend_vnode ( node ) }
}
pub fn root_node ( & self , id : ScopeId ) -> & VNode {
self . fin_head ( id )
}
2022-01-02 07:15:04 +00:00
// this is totally okay since all our nodes are always in a valid state
pub fn get_element ( & self , id : ElementId ) -> Option < & VNode > {
2022-07-03 04:11:28 +00:00
self . nodes
. borrow ( )
. get ( id . 0 )
. copied ( )
. map ( | ptr | unsafe { extend_vnode ( & * ptr ) } )
2022-01-02 07:15:04 +00:00
}
2021-12-21 03:33:13 +00:00
}
2021-11-03 23:55:02 +00:00
/// Components in Dioxus use the "Context" object to interact with their lifecycle.
///
/// This lets components access props, schedule updates, integrate hooks, and expose shared state.
///
/// For the most part, the only method you should be using regularly is `render`.
///
/// ## Example
///
/// ```ignore
2021-11-05 20:28:08 +00:00
/// #[derive(Props)]
/// struct ExampleProps {
2021-11-03 23:55:02 +00:00
/// name: String
/// }
///
2021-12-19 04:03:59 +00:00
/// fn Example(cx: Scope<ExampleProps>) -> Element {
2021-12-15 20:56:53 +00:00
/// cx.render(rsx!{ div {"Hello, {cx.props.name}"} })
2021-11-03 23:55:02 +00:00
/// }
/// ```
2021-12-25 22:18:05 +00:00
pub struct Scope < ' a , P = ( ) > {
2022-02-20 22:37:46 +00:00
/// The internal ScopeState for this component
2021-12-14 07:27:59 +00:00
pub scope : & ' a ScopeState ,
2022-02-20 22:37:46 +00:00
/// The props for this component
2021-12-13 05:06:17 +00:00
pub props : & ' a P ,
}
2021-12-14 07:27:59 +00:00
2021-12-15 02:46:19 +00:00
impl < P > Copy for Scope < '_ , P > { }
2021-12-14 07:27:59 +00:00
impl < P > Clone for Scope < '_ , P > {
2021-12-13 05:06:17 +00:00
fn clone ( & self ) -> Self {
2022-01-31 07:44:16 +00:00
Self {
scope : self . scope ,
props : self . props ,
}
2021-12-13 05:06:17 +00:00
}
}
2021-12-15 02:46:19 +00:00
impl < ' a , P > std ::ops ::Deref for Scope < ' a , P > {
// rust will auto deref again to the original 'a lifetime at the call site
type Target = & ' a ScopeState ;
2021-12-13 05:06:17 +00:00
fn deref ( & self ) -> & Self ::Target {
& self . scope
}
}
2021-11-03 23:55:02 +00:00
2021-12-13 00:47:13 +00:00
/// A component's unique identifier.
///
2022-07-03 04:11:28 +00:00
/// `ScopeId` is a `usize` that is unique across the entire [`VirtualDom`] and across time. [`ScopeID`]s will never be reused
2021-12-13 00:47:13 +00:00
/// once a component has been unmounted.
#[ cfg_attr(feature = " serialize " , derive(serde::Serialize, serde::Deserialize)) ]
2022-03-04 18:08:25 +00:00
#[ derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord) ]
2021-12-13 00:47:13 +00:00
pub struct ScopeId ( pub usize ) ;
2021-12-26 19:22:30 +00:00
/// A task's unique identifier.
///
2022-07-03 04:11:28 +00:00
/// `TaskId` is a `usize` that is unique across the entire [`VirtualDom`] and across time. [`TaskID`]s will never be reused
2021-12-26 19:22:30 +00:00
/// once a Task has been completed.
#[ cfg_attr(feature = " serialize " , derive(serde::Serialize, serde::Deserialize)) ]
#[ derive(Copy, Clone, PartialEq, Eq, Hash, Debug) ]
2022-02-27 22:03:43 +00:00
pub struct TaskId {
/// The global ID of the task
pub id : usize ,
/// The original scope that this task was scheduled in
pub scope : ScopeId ,
}
2021-12-26 19:22:30 +00:00
2021-12-19 04:03:59 +00:00
/// Every component in Dioxus is represented by a `ScopeState`.
2021-07-09 05:42:26 +00:00
///
/// 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-07-15 08:09:28 +00:00
///
2022-07-03 04:11:28 +00:00
/// We expose the `Scope` type so downstream users can traverse the Dioxus [`VirtualDom`] for whatever
2021-10-24 17:30:36 +00:00
/// use case they might have.
2021-12-14 07:27:59 +00:00
pub struct ScopeState {
pub ( crate ) parent_scope : Option < * mut ScopeState > ,
2022-09-30 19:03:06 +00:00
pub ( crate ) container : GlobalNodeId ,
2021-07-15 07:38:09 +00:00
pub ( crate ) our_arena_idx : ScopeId ,
pub ( crate ) height : u32 ,
2022-01-31 07:40:12 +00:00
pub ( crate ) fnptr : ComponentPtr ,
2021-12-21 04:31:33 +00:00
pub ( crate ) props : RefCell < Option < Box < dyn AnyProps > > > ,
2021-11-08 03:36:57 +00:00
2021-12-21 03:33:13 +00:00
// nodes, items
2021-11-08 03:36:57 +00:00
pub ( crate ) frames : [ BumpFrame ; 2 ] ,
2021-12-21 03:33:13 +00:00
pub ( crate ) generation : Cell < u32 > ,
2021-11-05 20:28:08 +00:00
pub ( crate ) items : RefCell < SelfReferentialItems < 'static > > ,
2021-11-03 23:55:02 +00:00
2021-12-21 03:33:13 +00:00
// hooks
2021-11-28 21:25:42 +00:00
pub ( crate ) hook_arena : Bump ,
2021-12-19 04:03:59 +00:00
pub ( crate ) hook_vals : RefCell < Vec < * mut dyn Any > > ,
2021-11-28 21:25:42 +00:00
pub ( crate ) hook_idx : Cell < usize > ,
2021-10-25 19:05:17 +00:00
2021-12-21 03:33:13 +00:00
// shared state -> todo: move this out of scopestate
2022-03-02 22:57:57 +00:00
pub ( crate ) shared_contexts : RefCell < HashMap < TypeId , Box < dyn Any > > > ,
2021-12-26 19:22:30 +00:00
pub ( crate ) tasks : Rc < TaskQueue > ,
2022-09-30 19:03:06 +00:00
// templates
pub ( crate ) templates : Rc < RefCell < FxHashMap < TemplateId , Rc < RefCell < Template > > > > > ,
2021-07-09 05:42:26 +00:00
}
2021-11-05 20:28:08 +00:00
pub struct SelfReferentialItems < ' a > {
2021-11-07 06:01:22 +00:00
pub ( crate ) listeners : Vec < & ' a Listener < ' a > > ,
pub ( crate ) borrowed_props : Vec < & ' a VComponent < ' a > > ,
2021-09-13 22:55:43 +00:00
}
2021-11-07 06:01:22 +00:00
// Public methods exposed to libraries and components
2021-12-14 07:27:59 +00:00
impl ScopeState {
2021-11-05 20:28:08 +00:00
/// Get the height of this Scope - IE the number of scopes above it.
///
/// A Scope with a height of `0` is the root scope - there are no other scopes above it.
///
/// # Example
///
2021-11-12 02:34:20 +00:00
/// ```rust, ignore
2021-12-29 04:48:25 +00:00
/// let mut dom = VirtualDom::new(|cx| cx.render(rsx!{ div {} }));
2021-11-05 20:28:08 +00:00
/// dom.rebuild();
///
/// let base = dom.base_scope();
///
/// assert_eq!(base.height(), 0);
/// ```
pub fn height ( & self ) -> u32 {
self . height
}
2022-07-03 04:11:28 +00:00
/// Get the Parent of this [`Scope`] within this Dioxus [`VirtualDom`].
2021-11-05 20:28:08 +00:00
///
2022-07-03 04:11:28 +00:00
/// This ID is not unique across Dioxus [`VirtualDom`]s or across time. IDs will be reused when components are unmounted.
2021-11-05 20:28:08 +00:00
///
/// The base component will not have a parent, and will return `None`.
///
/// # Example
///
2021-11-12 02:34:20 +00:00
/// ```rust, ignore
2021-12-29 04:48:25 +00:00
/// let mut dom = VirtualDom::new(|cx| cx.render(rsx!{ div {} }));
2021-11-05 20:28:08 +00:00
/// dom.rebuild();
///
/// let base = dom.base_scope();
///
/// assert_eq!(base.parent(), None);
/// ```
pub fn parent ( & self ) -> Option < ScopeId > {
2021-11-28 21:25:42 +00:00
// safety: the pointer to our parent is *always* valid thanks to the bump arena
2021-11-12 02:34:20 +00:00
self . parent_scope . map ( | p | unsafe { & * p } . our_arena_idx )
2021-11-05 20:28:08 +00:00
}
2022-07-03 04:11:28 +00:00
/// Get the ID of this Scope within this Dioxus [`VirtualDom`].
2021-11-05 20:28:08 +00:00
///
2022-07-03 04:11:28 +00:00
/// This ID is not unique across Dioxus [`VirtualDom`]s or across time. IDs will be reused when components are unmounted.
2021-11-05 20:28:08 +00:00
///
/// # Example
///
2021-11-12 02:34:20 +00:00
/// ```rust, ignore
2021-12-29 04:48:25 +00:00
/// let mut dom = VirtualDom::new(|cx| cx.render(rsx!{ div {} }));
2021-11-05 20:28:08 +00:00
/// dom.rebuild();
/// let base = dom.base_scope();
///
/// assert_eq!(base.scope_id(), 0);
/// ```
pub fn scope_id ( & self ) -> ScopeId {
self . our_arena_idx
}
2022-02-23 18:47:38 +00:00
/// Get a handle to the raw update scheduler channel
pub fn scheduler_channel ( & self ) -> UnboundedSender < SchedulerMsg > {
self . tasks . sender . clone ( )
}
2021-11-03 23:55:02 +00:00
/// Create a subscription that schedules a future render for the reference component
///
2022-07-03 04:11:28 +00:00
/// ## Notice: you should prefer using [`schedule_update_any`] and [`scope_id`]
2022-02-23 19:01:09 +00:00
pub fn schedule_update ( & self ) -> Arc < dyn Fn ( ) + Send + Sync + 'static > {
2021-12-26 19:22:30 +00:00
let ( chan , id ) = ( self . tasks . sender . clone ( ) , self . scope_id ( ) ) ;
2022-07-03 04:11:28 +00:00
Arc ::new ( move | | drop ( chan . unbounded_send ( SchedulerMsg ::Immediate ( id ) ) ) )
2021-11-05 20:28:08 +00:00
}
2022-07-03 04:11:28 +00:00
/// Schedule an update for any component given its [`ScopeId`].
2021-11-05 20:28:08 +00:00
///
2022-07-03 04:11:28 +00:00
/// A component's [`ScopeId`] can be obtained from `use_hook` or the [`ScopeState::scope_id`] method.
2021-11-05 20:28:08 +00:00
///
/// This method should be used when you want to schedule an update for a component
2022-02-23 19:01:09 +00:00
pub fn schedule_update_any ( & self ) -> Arc < dyn Fn ( ScopeId ) + Send + Sync > {
2021-12-26 19:22:30 +00:00
let chan = self . tasks . sender . clone ( ) ;
2022-07-03 04:11:28 +00:00
Arc ::new ( move | id | drop ( chan . unbounded_send ( SchedulerMsg ::Immediate ( id ) ) ) )
2021-11-03 23:55:02 +00:00
}
/// Get the [`ScopeId`] of a mounted component.
///
2022-07-03 04:11:28 +00:00
/// `ScopeId` is not unique for the lifetime of the [`VirtualDom`] - a [`ScopeId`] will be reused if a component is unmounted.
2021-11-03 23:55:02 +00:00
pub fn needs_update ( & self ) {
2022-07-03 04:11:28 +00:00
self . needs_update_any ( self . scope_id ( ) ) ;
2021-11-03 23:55:02 +00:00
}
/// Get the [`ScopeId`] of a mounted component.
///
2022-07-03 04:11:28 +00:00
/// `ScopeId` is not unique for the lifetime of the [`VirtualDom`] - a [`ScopeId`] will be reused if a component is unmounted.
2021-11-03 23:55:02 +00:00
pub fn needs_update_any ( & self , id : ScopeId ) {
2022-07-03 04:11:28 +00:00
self . tasks
2021-12-26 19:22:30 +00:00
. sender
2022-07-03 04:11:28 +00:00
. unbounded_send ( SchedulerMsg ::Immediate ( id ) )
. expect ( " Scheduler to exist if scope exists " ) ;
2021-11-03 23:55:02 +00:00
}
2021-12-13 00:47:13 +00:00
/// Get the Root Node of this scope
pub fn root_node ( & self ) -> & VNode {
2021-12-21 03:33:13 +00:00
let node = unsafe { & * self . fin_frame ( ) . node . get ( ) } ;
2021-12-19 04:03:59 +00:00
unsafe { std ::mem ::transmute ( node ) }
2021-11-03 23:55:02 +00:00
}
2022-07-03 04:11:28 +00:00
/// This method enables the ability to expose state to children further down the [`VirtualDom`] Tree.
2021-11-03 23:55:02 +00:00
///
/// This is a "fundamental" operation and should only be called during initialization of a hook.
///
2021-12-29 04:20:01 +00:00
/// For a hook that provides the same functionality, use `use_provide_context` and `use_consume_context` instead.
2021-11-03 23:55:02 +00:00
///
/// When the component is dropped, so is the context. Be aware of this behavior when consuming
/// the context via Rc/Weak.
///
/// # Example
///
2021-11-12 02:34:20 +00:00
/// ```rust, ignore
2021-11-03 23:55:02 +00:00
/// struct SharedState(&'static str);
///
2021-12-29 04:48:25 +00:00
/// static App: Component = |cx| {
2022-07-11 19:50:56 +00:00
/// cx.use_hook(|| cx.provide_context(SharedState("world")));
2022-09-25 08:05:16 +00:00
/// render!(Child {})
2021-11-03 23:55:02 +00:00
/// }
///
2021-12-29 04:48:25 +00:00
/// static Child: Component = |cx| {
2021-11-03 23:55:02 +00:00
/// let state = cx.consume_state::<SharedState>();
2022-09-25 08:05:16 +00:00
/// render!(div { "hello {state.0}" })
2021-11-03 23:55:02 +00:00
/// }
/// ```
2022-03-02 22:57:57 +00:00
pub fn provide_context < T : 'static + Clone > ( & self , value : T ) -> T {
2021-11-03 23:55:02 +00:00
self . shared_contexts
. borrow_mut ( )
2022-03-02 22:57:57 +00:00
. insert ( TypeId ::of ::< T > ( ) , Box ::new ( value . clone ( ) ) )
2022-02-25 05:35:25 +00:00
. and_then ( | f | f . downcast ::< T > ( ) . ok ( ) ) ;
2022-01-05 05:27:22 +00:00
value
2021-11-03 23:55:02 +00:00
}
2022-01-21 05:37:52 +00:00
/// Provide a context for the root component from anywhere in your app.
///
///
/// # Example
///
/// ```rust, ignore
/// struct SharedState(&'static str);
///
/// static App: Component = |cx| {
2022-07-11 19:50:56 +00:00
/// cx.use_hook(|| cx.provide_root_context(SharedState("world")));
2022-09-25 08:05:16 +00:00
/// render!(Child {})
2022-01-21 05:37:52 +00:00
/// }
///
/// static Child: Component = |cx| {
/// let state = cx.consume_state::<SharedState>();
2022-09-25 08:05:16 +00:00
/// render!(div { "hello {state.0}" })
2022-01-21 05:37:52 +00:00
/// }
/// ```
2022-03-02 22:57:57 +00:00
pub fn provide_root_context < T : 'static + Clone > ( & self , value : T ) -> T {
2022-01-21 05:37:52 +00:00
// if we *are* the root component, then we can just provide the context directly
if self . scope_id ( ) = = ScopeId ( 0 ) {
self . shared_contexts
. borrow_mut ( )
2022-03-02 22:57:57 +00:00
. insert ( TypeId ::of ::< T > ( ) , Box ::new ( value . clone ( ) ) )
2022-02-25 05:35:25 +00:00
. and_then ( | f | f . downcast ::< T > ( ) . ok ( ) ) ;
2022-01-24 07:34:16 +00:00
return value ;
2022-01-21 05:37:52 +00:00
}
let mut search_parent = self . parent_scope ;
while let Some ( parent ) = search_parent . take ( ) {
let parent = unsafe { & * parent } ;
2022-01-21 14:29:41 +00:00
if parent . scope_id ( ) = = ScopeId ( 0 ) {
let exists = parent
. shared_contexts
. borrow_mut ( )
2022-03-02 22:57:57 +00:00
. insert ( TypeId ::of ::< T > ( ) , Box ::new ( value . clone ( ) ) ) ;
2022-01-21 14:29:41 +00:00
if exists . is_some ( ) {
log ::warn! ( " Context already provided to parent scope - replacing it " ) ;
}
return value ;
2022-01-21 05:37:52 +00:00
}
search_parent = parent . parent_scope ;
}
2022-01-21 14:29:41 +00:00
unreachable! ( " all apps have a root scope " )
2022-01-21 05:37:52 +00:00
}
2022-07-03 04:11:28 +00:00
/// Try to retrieve a shared state with type T from the any parent Scope.
2022-03-02 22:57:57 +00:00
pub fn consume_context < T : 'static + Clone > ( & self ) -> Option < T > {
2021-11-05 21:15:59 +00:00
if let Some ( shared ) = self . shared_contexts . borrow ( ) . get ( & TypeId ::of ::< T > ( ) ) {
2022-07-03 04:11:28 +00:00
Some (
( * shared
. downcast_ref ::< T > ( )
. expect ( " Context of type T should exist " ) )
. clone ( ) ,
)
2021-11-05 21:15:59 +00:00
} else {
let mut search_parent = self . parent_scope ;
while let Some ( parent_ptr ) = search_parent {
2021-12-10 02:19:31 +00:00
// safety: all parent pointers are valid thanks to the bump arena
2021-11-05 21:15:59 +00:00
let parent = unsafe { & * parent_ptr } ;
if let Some ( shared ) = parent . shared_contexts . borrow ( ) . get ( & TypeId ::of ::< T > ( ) ) {
2022-07-03 04:11:28 +00:00
return Some (
shared
. downcast_ref ::< T > ( )
. expect ( " Context of type T should exist " )
. clone ( ) ,
) ;
2021-11-05 21:15:59 +00:00
}
search_parent = parent . parent_scope ;
}
None
}
2021-11-03 23:55:02 +00:00
}
2021-11-28 21:25:42 +00:00
/// Pushes the future onto the poll queue to be polled after the component renders.
2021-12-26 19:22:30 +00:00
pub fn push_future ( & self , fut : impl Future < Output = ( ) > + 'static ) -> TaskId {
// wake up the scheduler if it is sleeping
self . tasks
. sender
2021-12-25 22:18:05 +00:00
. unbounded_send ( SchedulerMsg ::NewTask ( self . our_arena_idx ) )
2022-07-03 04:11:28 +00:00
. expect ( " Scheduler should exist " ) ;
2021-11-15 14:49:01 +00:00
2022-02-27 22:03:43 +00:00
self . tasks . spawn ( self . our_arena_idx , fut )
2021-12-26 19:22:30 +00:00
}
2022-07-03 04:11:28 +00:00
/// Spawns the future but does not return the [`TaskId`]
2022-01-24 20:21:56 +00:00
pub fn spawn ( & self , fut : impl Future < Output = ( ) > + 'static ) {
self . push_future ( fut ) ;
}
2022-02-27 22:22:21 +00:00
/// Spawn a future that Dioxus will never clean up
///
/// This is good for tasks that need to be run after the component has been dropped.
pub fn spawn_forever ( & self , fut : impl Future < Output = ( ) > + 'static ) -> TaskId {
// wake up the scheduler if it is sleeping
self . tasks
. sender
. unbounded_send ( SchedulerMsg ::NewTask ( self . our_arena_idx ) )
2022-07-03 04:11:28 +00:00
. expect ( " Scheduler should exist " ) ;
2022-02-27 22:22:21 +00:00
// The root scope will never be unmounted so we can just add the task at the top of the app
self . tasks . spawn ( ScopeId ( 0 ) , fut )
}
2022-02-20 22:37:46 +00:00
/// Informs the scheduler that this task is no longer needed and should be removed
/// on next poll.
2021-12-26 19:22:30 +00:00
pub fn remove_future ( & self , id : TaskId ) {
2022-02-27 22:03:43 +00:00
self . tasks . remove ( id ) ;
2021-11-15 14:49:01 +00:00
}
2022-07-03 04:11:28 +00:00
/// Take a lazy [`VNode`] structure and actually build it with the context of the Vdoms efficient [`VNode`] allocator.
2021-12-15 02:46:19 +00:00
///
/// ## Example
///
/// ```ignore
2021-12-15 20:56:53 +00:00
/// fn Component(cx: Scope<Props>) -> Element {
2021-12-15 02:46:19 +00:00
/// // Lazy assemble the VNode tree
/// let lazy_nodes = rsx!("hello world");
///
/// // Actually build the tree and allocate it
/// cx.render(lazy_tree)
/// }
///```
2021-12-25 22:18:05 +00:00
pub fn render < ' src > ( & ' src self , rsx : LazyNodes < ' src , '_ > ) -> Option < VNode < ' src > > {
2022-01-31 07:44:16 +00:00
Some ( rsx . call ( NodeFactory {
scope : self ,
bump : & self . wip_frame ( ) . bump ,
} ) )
2021-12-15 02:46:19 +00:00
}
2021-11-03 23:55:02 +00:00
2022-07-11 19:50:56 +00:00
/// Store a value between renders. The foundational hook for all other hooks.
2021-11-03 23:55:02 +00:00
///
2022-07-11 19:50:56 +00:00
/// Accepts an `initializer` closure, which is run on the first use of the hook (typically the initial render). The return value of this closure is stored for the lifetime of the component, and a mutable reference to it is provided on every render as the return value of `use_hook`.
2021-11-03 23:55:02 +00:00
///
2022-07-11 19:50:56 +00:00
/// When the component is unmounted (removed from the UI), the value is dropped. This means you can return a custom type and provide cleanup code by implementing the [`Drop`] trait
2021-11-03 23:55:02 +00:00
///
/// # Example
///
2022-07-11 19:50:56 +00:00
/// ```
/// use dioxus_core::ScopeState;
///
/// // prints a greeting on the initial render
/// pub fn use_hello_world(cx: &ScopeState) {
/// cx.use_hook(|| println!("Hello, world!"));
2021-11-03 23:55:02 +00:00
/// }
/// ```
2022-01-08 14:32:53 +00:00
#[ allow(clippy::mut_from_ref) ]
2022-07-11 19:50:56 +00:00
pub fn use_hook < State : 'static > ( & self , initializer : impl FnOnce ( ) -> State ) -> & mut State {
2021-11-28 21:25:42 +00:00
let mut vals = self . hook_vals . borrow_mut ( ) ;
2021-12-13 00:47:13 +00:00
2021-11-28 21:25:42 +00:00
let hook_len = vals . len ( ) ;
let cur_idx = self . hook_idx . get ( ) ;
if cur_idx > = hook_len {
2022-07-11 19:50:56 +00:00
vals . push ( self . hook_arena . alloc ( initializer ( ) ) ) ;
2021-11-03 23:55:02 +00:00
}
2022-01-02 07:15:04 +00:00
vals
2021-11-28 21:25:42 +00:00
. get ( cur_idx )
. and_then ( | inn | {
self . hook_idx . set ( cur_idx + 1 ) ;
let raw_box = unsafe { & mut * * inn } ;
raw_box . downcast_mut ::< State > ( )
} )
. expect (
r ###"
Unable to retrieve the hook that was initialized at this index .
Consult the ` rules of hooks ` to understand how to use hooks properly .
2021-11-03 23:55:02 +00:00
2021-11-28 21:25:42 +00:00
You likely used the hook in a conditional . Hooks rely on consistent ordering between renders .
Functions prefixed with " use " should never be called conditionally .
" ###,
2022-01-02 07:15:04 +00:00
)
2021-11-07 06:01:22 +00:00
}
/// The "work in progress frame" represents the frame that is currently being worked on.
2021-11-08 03:36:57 +00:00
pub ( crate ) fn wip_frame ( & self ) -> & BumpFrame {
2022-07-03 04:11:28 +00:00
match self . generation . get ( ) & 1 {
0 = > & self . frames [ 0 ] ,
_ = > & self . frames [ 1 ] ,
2021-11-08 03:36:57 +00:00
}
}
2021-11-10 22:09:52 +00:00
2021-12-13 00:47:13 +00:00
/// Mutable access to the "work in progress frame" - used to clear it
2021-11-12 02:34:20 +00:00
pub ( crate ) fn wip_frame_mut ( & mut self ) -> & mut BumpFrame {
2022-07-03 04:11:28 +00:00
match self . generation . get ( ) & 1 {
0 = > & mut self . frames [ 0 ] ,
_ = > & mut self . frames [ 1 ] ,
2021-11-12 02:34:20 +00:00
}
}
2021-12-13 00:47:13 +00:00
/// Access to the frame where finalized nodes existed
2021-11-08 03:36:57 +00:00
pub ( crate ) fn fin_frame ( & self ) -> & BumpFrame {
2022-07-03 04:11:28 +00:00
match self . generation . get ( ) & 1 {
1 = > & self . frames [ 0 ] ,
_ = > & self . frames [ 1 ] ,
2021-11-08 03:36:57 +00:00
}
2021-11-07 06:01:22 +00:00
}
2021-11-12 02:34:20 +00:00
/// Reset this component's frame
///
/// # Safety:
2021-12-13 00:47:13 +00:00
///
2022-07-03 04:11:28 +00:00
/// This method breaks every reference of every [`VNode`] in the current frame.
2021-12-13 00:47:13 +00:00
///
/// Calling reset itself is not usually a big deal, but we consider it important
/// due to the complex safety guarantees we need to uphold.
2021-11-12 02:34:20 +00:00
pub ( crate ) unsafe fn reset_wip_frame ( & mut self ) {
2021-12-13 00:47:13 +00:00
self . wip_frame_mut ( ) . bump . reset ( ) ;
2021-11-08 03:36:57 +00:00
}
2021-11-07 06:01:22 +00:00
2021-12-13 00:47:13 +00:00
/// Cycle to the next generation
2021-11-12 02:34:20 +00:00
pub ( crate ) fn cycle_frame ( & self ) {
2021-11-08 03:36:57 +00:00
self . generation . set ( self . generation . get ( ) + 1 ) ;
2021-11-07 06:01:22 +00:00
}
2021-12-21 03:33:13 +00:00
// todo: disable bookkeeping on drop (unncessary)
2021-12-19 04:03:59 +00:00
pub ( crate ) fn reset ( & mut self ) {
2021-12-21 03:33:13 +00:00
// first: book keaping
2021-12-19 04:03:59 +00:00
self . hook_idx . set ( 0 ) ;
self . parent_scope = None ;
self . generation . set ( 0 ) ;
2021-12-21 03:33:13 +00:00
// next: shared context data
self . shared_contexts . get_mut ( ) . clear ( ) ;
2021-12-19 04:03:59 +00:00
2021-12-21 03:33:13 +00:00
// next: reset the node data
2022-01-31 07:44:16 +00:00
let SelfReferentialItems {
borrowed_props ,
listeners ,
} = self . items . get_mut ( ) ;
2021-12-19 04:03:59 +00:00
borrowed_props . clear ( ) ;
listeners . clear ( ) ;
2021-12-21 03:33:13 +00:00
self . frames [ 0 ] . reset ( ) ;
self . frames [ 1 ] . reset ( ) ;
2021-12-19 04:03:59 +00:00
2022-01-24 17:21:24 +00:00
// Free up the hook values
2021-12-21 03:33:13 +00:00
self . hook_vals . get_mut ( ) . drain ( .. ) . for_each ( | state | {
let as_mut = unsafe { & mut * state } ;
let boxed = unsafe { bumpalo ::boxed ::Box ::from_raw ( as_mut ) } ;
drop ( boxed ) ;
} ) ;
2022-01-24 17:21:24 +00:00
// Finally, clear the hook arena
self . hook_arena . reset ( ) ;
2021-12-19 04:03:59 +00:00
}
2021-11-07 06:01:22 +00:00
}
2021-11-08 03:36:57 +00:00
2021-11-10 22:09:52 +00:00
pub ( crate ) struct BumpFrame {
2021-11-08 03:36:57 +00:00
pub bump : Bump ,
2021-12-19 04:03:59 +00:00
pub node : Cell < * const VNode < 'static > > ,
2021-11-08 03:36:57 +00:00
}
impl BumpFrame {
2021-12-13 00:47:13 +00:00
pub ( crate ) fn new ( capacity : usize ) -> Self {
2021-11-10 22:09:52 +00:00
let bump = Bump ::with_capacity ( capacity ) ;
2022-07-03 04:11:28 +00:00
let node = bump . alloc ( VText {
2021-12-19 04:03:59 +00:00
text : " placeholdertext " ,
2022-07-03 04:11:28 +00:00
id : Cell ::default ( ) ,
2021-11-08 03:36:57 +00:00
is_static : false ,
} ) ;
2022-07-03 04:11:28 +00:00
let node = bump . alloc ( VNode ::Text ( unsafe {
& * ( node as * mut VText as * const VText )
} ) ) ;
2021-12-15 02:46:19 +00:00
let nodes = Cell ::new ( node as * const _ ) ;
2021-12-19 04:03:59 +00:00
Self { bump , node : nodes }
}
pub ( crate ) fn reset ( & mut self ) {
self . bump . reset ( ) ;
let node = self . bump . alloc ( VText {
text : " placeholdertext " ,
2022-07-03 04:11:28 +00:00
id : Cell ::default ( ) ,
2021-12-19 04:03:59 +00:00
is_static : false ,
} ) ;
2022-07-03 04:11:28 +00:00
let node = self . bump . alloc ( VNode ::Text ( unsafe {
& * ( node as * mut VText as * const VText )
} ) ) ;
2021-12-19 04:03:59 +00:00
self . node . set ( node as * const _ ) ;
2021-11-08 03:36:57 +00:00
}
}
2021-11-09 17:10:11 +00:00
2021-12-26 19:22:30 +00:00
pub ( crate ) struct TaskQueue {
pub ( crate ) tasks : RefCell < FxHashMap < TaskId , InnerTask > > ,
2022-02-04 23:05:55 +00:00
pub ( crate ) task_map : RefCell < FxHashMap < ScopeId , HashSet < TaskId > > > ,
2021-12-26 19:22:30 +00:00
gen : Cell < usize > ,
sender : UnboundedSender < SchedulerMsg > ,
}
2022-02-04 23:05:55 +00:00
2021-12-26 19:22:30 +00:00
pub ( crate ) type InnerTask = Pin < Box < dyn Future < Output = ( ) > > > ;
impl TaskQueue {
2022-02-27 22:03:43 +00:00
fn spawn ( & self , scope : ScopeId , task : impl Future < Output = ( ) > + 'static ) -> TaskId {
2021-12-26 19:22:30 +00:00
let pinned = Box ::pin ( task ) ;
let id = self . gen . get ( ) ;
self . gen . set ( id + 1 ) ;
2022-02-27 22:03:43 +00:00
let tid = TaskId { id , scope } ;
2021-12-26 19:22:30 +00:00
self . tasks . borrow_mut ( ) . insert ( tid , pinned ) ;
2022-02-27 22:03:43 +00:00
// also add to the task map
// when the component is unmounted we know to remove it from the map
self . task_map
. borrow_mut ( )
. entry ( scope )
. or_default ( )
. insert ( tid ) ;
2021-12-26 19:22:30 +00:00
tid
}
2022-02-27 22:03:43 +00:00
fn remove ( & self , id : TaskId ) {
2021-12-26 19:22:30 +00:00
if let Ok ( mut tasks ) = self . tasks . try_borrow_mut ( ) {
2022-07-03 04:11:28 +00:00
tasks . remove ( & id ) ;
2022-06-13 10:42:24 +00:00
if let Some ( task_map ) = self . task_map . borrow_mut ( ) . get_mut ( & id . scope ) {
task_map . remove ( & id ) ;
}
2021-12-26 19:22:30 +00:00
}
2022-02-27 22:03:43 +00:00
// the task map is still around, but it'll be removed when the scope is unmounted
2021-12-26 19:22:30 +00:00
}
2022-02-27 22:03:43 +00:00
2021-12-26 19:22:30 +00:00
pub ( crate ) fn has_tasks ( & self ) -> bool {
! self . tasks . borrow ( ) . is_empty ( )
}
}
2021-11-09 17:10:11 +00:00
#[ test ]
fn sizeof ( ) {
2021-12-14 07:27:59 +00:00
dbg! ( std ::mem ::size_of ::< ScopeState > ( ) ) ;
2021-11-09 17:10:11 +00:00
}