1use std::{
4 any::{Any, TypeId},
5 cell::RefCell,
6 hash::{Hash, Hasher},
7 marker::PhantomData,
8 sync::Arc,
9 time::Duration,
10};
11
12use parking_lot::RwLock;
13use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
14use slotmap::{SlotMap, new_key_type};
15use smallvec::SmallVec;
16
17use crate::{
18 NodeId,
19 accessibility::{AccessibilityActionHandler, AccessibilityNode},
20 component_tree::ComponentTree,
21 execution_context::{OrderFrame, with_execution_context, with_execution_context_mut},
22 focus::{
23 FocusDirection, FocusGroupNode, FocusHandleId, FocusNode, FocusProperties,
24 FocusRegistration, FocusRegistrationKind, FocusRequester, FocusRequesterId,
25 FocusRevealRequest, FocusScopeNode, FocusState, FocusTraversalPolicy,
26 },
27 layout::{LayoutPolicyDyn, RenderPolicyDyn},
28 modifier::Modifier,
29 prop::{CallbackWith, ComponentReplayData, ErasedComponentRunner, Prop},
30 time::Instant,
31};
32
33#[derive(Clone, Copy)]
34enum OrderCounterKind {
35 Remember,
36 Functor,
37 Context,
38 FrameReceiver,
39}
40
41fn push_order_frame() {
42 with_execution_context_mut(|context| {
43 context.order_frame_stack.push(OrderFrame::default());
44 });
45}
46
47fn pop_order_frame(underflow_message: &str) {
48 with_execution_context_mut(|context| {
49 let popped = context.order_frame_stack.pop();
50 debug_assert!(popped.is_some(), "{underflow_message}");
51 });
52}
53
54fn next_order_counter(kind: OrderCounterKind, empty_message: &str) -> u64 {
55 with_execution_context_mut(|context| {
56 debug_assert!(!context.order_frame_stack.is_empty(), "{empty_message}");
57 let frame = context.order_frame_stack.last_mut().expect(empty_message);
58 match kind {
59 OrderCounterKind::Remember => {
60 let counter = frame.remember;
61 frame.remember = frame.remember.wrapping_add(1);
62 counter
63 }
64 OrderCounterKind::Functor => {
65 let counter = frame.functor;
66 frame.functor = frame.functor.wrapping_add(1);
67 counter
68 }
69 OrderCounterKind::Context => {
70 let counter = frame.context;
71 frame.context = frame.context.wrapping_add(1);
72 counter
73 }
74 OrderCounterKind::FrameReceiver => {
75 let counter = frame.frame_receiver;
76 frame.frame_receiver = frame.frame_receiver.wrapping_add(1);
77 counter
78 }
79 }
80 })
81}
82
83fn next_child_instance_call_index() -> u64 {
84 with_execution_context_mut(|context| {
85 let Some(frame) = context.order_frame_stack.last_mut() else {
86 return 0;
87 };
88 let index = frame.instance;
89 frame.instance = frame.instance.wrapping_add(1);
90 index
91 })
92}
93
94pub(crate) fn compute_context_slot_key() -> (u64, u64) {
95 let instance_logic_id = current_instance_logic_id();
96 let group_path_hash = current_group_path_hash();
97
98 let call_counter = next_order_counter(
99 OrderCounterKind::Context,
100 "ORDER_FRAME_STACK is empty; provide_context must be called inside a component",
101 );
102
103 let slot_hash = hash_components(&[&group_path_hash, &call_counter]);
104 (instance_logic_id, slot_hash)
105}
106
107#[derive(Hash, Eq, PartialEq, Clone, Copy)]
108struct SlotKey {
109 instance_logic_id: u64,
110 slot_hash: u64,
111 type_id: TypeId,
112}
113
114impl Default for SlotKey {
115 fn default() -> Self {
116 Self {
117 instance_logic_id: 0,
118 slot_hash: 0,
119 type_id: TypeId::of::<()>(),
120 }
121 }
122}
123
124new_key_type! {
125 struct SlotHandle;
126}
127
128#[derive(Default)]
129struct SlotEntry {
130 key: SlotKey,
131 generation: u64,
132 value: Option<Arc<dyn Any + Send + Sync>>,
133 last_alive_epoch: u64,
134 retained: bool,
135}
136
137#[derive(Default)]
138struct InstanceSlotCursor {
139 previous_order: SmallVec<[SlotHandle; 4]>,
140 current_order: SmallVec<[SlotHandle; 4]>,
141 cursor: usize,
142 epoch: u64,
143}
144
145impl InstanceSlotCursor {
146 fn begin_epoch(&mut self, epoch: u64) {
147 if self.epoch == epoch {
148 return;
149 }
150 self.previous_order = std::mem::take(&mut self.current_order);
151 self.cursor = 0;
152 self.epoch = epoch;
153 }
154
155 fn fast_candidate(&self) -> Option<SlotHandle> {
156 self.previous_order.get(self.cursor).copied()
157 }
158
159 fn record_fast_match(&mut self, slot: SlotHandle) {
160 self.cursor = self.cursor.saturating_add(1);
161 self.current_order.push(slot);
162 }
163
164 fn record_slow_match(&mut self, slot: SlotHandle) {
165 if self.cursor < self.previous_order.len()
166 && let Some(offset) = self.previous_order[self.cursor..]
167 .iter()
168 .position(|candidate| *candidate == slot)
169 {
170 self.cursor += offset + 1;
171 }
172 self.current_order.push(slot);
173 }
174}
175
176#[derive(Default)]
177struct SlotTable {
178 entries: SlotMap<SlotHandle, SlotEntry>,
179 key_to_slot: HashMap<SlotKey, SlotHandle>,
180 cursors_by_instance_logic_id: HashMap<u64, InstanceSlotCursor>,
181 epoch: u64,
182}
183
184impl SlotTable {
185 fn begin_epoch(&mut self) {
186 self.epoch = self.epoch.wrapping_add(1);
187 }
188
189 fn reset(&mut self) {
190 self.entries.clear();
191 self.key_to_slot.clear();
192 self.cursors_by_instance_logic_id.clear();
193 self.epoch = 0;
194 }
195
196 fn try_fast_slot_lookup(&mut self, key: SlotKey) -> Option<SlotHandle> {
197 let epoch = self.epoch;
198 let candidate = {
199 let cursor = self
200 .cursors_by_instance_logic_id
201 .entry(key.instance_logic_id)
202 .or_default();
203 cursor.begin_epoch(epoch);
204 cursor.fast_candidate()
205 }?;
206
207 let is_match = self
208 .entries
209 .get(candidate)
210 .is_some_and(|entry| entry.key == key);
211
212 if !is_match {
213 return None;
214 }
215
216 let cursor = self
217 .cursors_by_instance_logic_id
218 .get_mut(&key.instance_logic_id)
219 .expect("cursor entry should exist");
220 cursor.record_fast_match(candidate);
221 Some(candidate)
222 }
223
224 fn record_slot_usage_slow(&mut self, instance_logic_id: u64, slot: SlotHandle) {
225 let epoch = self.epoch;
226 let cursor = self
227 .cursors_by_instance_logic_id
228 .entry(instance_logic_id)
229 .or_default();
230 cursor.begin_epoch(epoch);
231 cursor.record_slow_match(slot);
232 }
233}
234
235#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
236struct PersistentFocusHandleKey {
237 instance_key: u64,
238 slot_hash: u64,
239}
240
241#[derive(Clone, Copy)]
242struct PersistentFocusHandleEntry<T> {
243 value: T,
244 missing_frames: u8,
245}
246
247impl<T: Copy> PersistentFocusHandleEntry<T> {
248 fn new(value: T) -> Self {
249 Self {
250 value,
251 missing_frames: 0,
252 }
253 }
254
255 fn mark_live(&mut self) -> T {
256 self.missing_frames = 0;
257 self.value
258 }
259
260 fn retain_for_frame(&mut self) -> bool {
261 if self.missing_frames == 0 {
262 self.missing_frames = 1;
263 true
264 } else {
265 false
266 }
267 }
268}
269
270#[derive(Default)]
271struct PersistentFocusHandleStore {
272 targets: HashMap<PersistentFocusHandleKey, PersistentFocusHandleEntry<FocusNode>>,
273 scopes: HashMap<PersistentFocusHandleKey, PersistentFocusHandleEntry<FocusScopeNode>>,
274 groups: HashMap<PersistentFocusHandleKey, PersistentFocusHandleEntry<FocusGroupNode>>,
275 requesters: HashMap<PersistentFocusHandleKey, PersistentFocusHandleEntry<FocusRequester>>,
276}
277
278#[derive(Default)]
279pub(crate) struct RemovedPersistentFocusHandles {
280 pub handle_ids: HashSet<FocusHandleId>,
281 pub requester_ids: HashSet<FocusRequesterId>,
282}
283
284impl PersistentFocusHandleStore {
285 fn retain_instance_keys(
286 &mut self,
287 live_instance_keys: &HashSet<u64>,
288 ) -> RemovedPersistentFocusHandles {
289 let mut removed = RemovedPersistentFocusHandles::default();
290 self.targets.retain(|key, handle| {
291 if !live_instance_keys.contains(&key.instance_key) {
292 if handle.retain_for_frame() {
293 true
294 } else {
295 removed.handle_ids.insert(handle.value.handle_id());
296 false
297 }
298 } else {
299 handle.mark_live();
300 true
301 }
302 });
303 self.scopes.retain(|key, scope| {
304 if !live_instance_keys.contains(&key.instance_key) {
305 if scope.retain_for_frame() {
306 true
307 } else {
308 removed.handle_ids.insert(scope.value.handle_id());
309 false
310 }
311 } else {
312 scope.mark_live();
313 true
314 }
315 });
316 self.groups.retain(|key, group| {
317 if !live_instance_keys.contains(&key.instance_key) {
318 if group.retain_for_frame() {
319 true
320 } else {
321 removed.handle_ids.insert(group.value.handle_id());
322 false
323 }
324 } else {
325 group.mark_live();
326 true
327 }
328 });
329 self.requesters.retain(|key, requester| {
330 if !live_instance_keys.contains(&key.instance_key) {
331 if requester.retain_for_frame() {
332 true
333 } else {
334 removed.requester_ids.insert(requester.value.requester_id());
335 false
336 }
337 } else {
338 requester.mark_live();
339 true
340 }
341 });
342 removed
343 }
344
345 fn contains_handle(&self, handle_id: FocusHandleId) -> bool {
346 self.targets
347 .values()
348 .any(|entry| entry.value.handle_id() == handle_id)
349 || self
350 .scopes
351 .values()
352 .any(|entry| entry.value.handle_id() == handle_id)
353 || self
354 .groups
355 .values()
356 .any(|entry| entry.value.handle_id() == handle_id)
357 }
358
359 fn clear(&mut self) {
360 self.targets.clear();
361 self.scopes.clear();
362 self.groups.clear();
363 self.requesters.clear();
364 }
365}
366
367fn with_slot_table<R>(f: impl FnOnce(&SlotTable) -> R) -> R {
368 RUNTIME_GLOBALS.with(|globals| f(&globals.slot_table.borrow()))
369}
370
371fn with_slot_table_mut<R>(f: impl FnOnce(&mut SlotTable) -> R) -> R {
372 RUNTIME_GLOBALS.with(|globals| f(&mut globals.slot_table.borrow_mut()))
373}
374
375#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
376pub(crate) struct FunctorHandle {
377 slot: SlotHandle,
378 generation: u64,
379}
380
381impl FunctorHandle {
382 fn new(slot: SlotHandle, generation: u64) -> Self {
383 Self { slot, generation }
384 }
385}
386
387struct CallbackCell {
388 current: RwLock<Arc<dyn Fn() + Send + Sync>>,
389}
390
391impl CallbackCell {
392 fn new(current: Arc<dyn Fn() + Send + Sync>) -> Self {
393 Self {
394 current: RwLock::new(current),
395 }
396 }
397
398 fn update(&self, next: Arc<dyn Fn() + Send + Sync>) {
399 *self.current.write() = next;
400 }
401
402 fn shared(&self) -> Arc<dyn Fn() + Send + Sync> {
403 Arc::clone(&self.current.read())
404 }
405}
406
407struct CallbackWithCell<T, R> {
408 current: RwLock<Arc<dyn Fn(T) -> R + Send + Sync>>,
409}
410
411impl<T, R> CallbackWithCell<T, R> {
412 fn new(current: Arc<dyn Fn(T) -> R + Send + Sync>) -> Self {
413 Self {
414 current: RwLock::new(current),
415 }
416 }
417
418 fn update(&self, next: Arc<dyn Fn(T) -> R + Send + Sync>) {
419 *self.current.write() = next;
420 }
421
422 fn shared(&self) -> Arc<dyn Fn(T) -> R + Send + Sync> {
423 Arc::clone(&self.current.read())
424 }
425}
426
427struct RenderSlotCell {
428 current: RwLock<Arc<dyn Fn() + Send + Sync>>,
429}
430
431impl RenderSlotCell {
432 fn new(current: Arc<dyn Fn() + Send + Sync>) -> Self {
433 Self {
434 current: RwLock::new(current),
435 }
436 }
437
438 fn update(&self, next: Arc<dyn Fn() + Send + Sync>) {
439 *self.current.write() = next;
440 }
441
442 fn shared(&self) -> Arc<dyn Fn() + Send + Sync> {
443 Arc::clone(&self.current.read())
444 }
445}
446
447struct RenderSlotWithCell<T> {
448 current: RwLock<Arc<dyn Fn(T) + Send + Sync>>,
449}
450
451impl<T> RenderSlotWithCell<T> {
452 fn new(current: Arc<dyn Fn(T) + Send + Sync>) -> Self {
453 Self {
454 current: RwLock::new(current),
455 }
456 }
457
458 fn update(&self, next: Arc<dyn Fn(T) + Send + Sync>) {
459 *self.current.write() = next;
460 }
461
462 fn shared(&self) -> Arc<dyn Fn(T) + Send + Sync> {
463 Arc::clone(&self.current.read())
464 }
465}
466
467#[derive(Default)]
468struct LayoutDirtyTracker {
469 previous_layout_inputs_by_node: HashMap<u64, LayoutInputSnapshot>,
470 frame_layout_inputs_by_node: HashMap<u64, LayoutInputSnapshot>,
471 pending_measure_self_dirty_nodes: HashSet<u64>,
472 ready_measure_self_dirty_nodes: HashSet<u64>,
473 pending_placement_self_dirty_nodes: HashSet<u64>,
474 ready_placement_self_dirty_nodes: HashSet<u64>,
475 previous_children_by_node: HashMap<u64, Vec<u64>>,
476}
477
478struct LayoutInputSnapshot {
479 policy: Box<dyn LayoutPolicyDyn>,
480 modifier: Modifier,
481}
482
483#[derive(Default)]
484pub(crate) struct LayoutDirtyNodes {
485 pub measure_self_nodes: HashSet<u64>,
486 pub placement_self_nodes: HashSet<u64>,
487}
488
489#[derive(Default)]
490pub(crate) struct StructureReconcileResult {
491 pub changed_nodes: HashSet<u64>,
492 pub removed_nodes: HashSet<u64>,
493}
494
495#[allow(dead_code)]
497#[derive(Clone)]
498pub(crate) struct ReplayNodeSnapshot {
499 pub instance_key: u64,
500 pub parent_instance_key: Option<u64>,
501 pub instance_logic_id: u64,
502 pub group_path: Vec<u64>,
503 pub instance_key_override: Option<u64>,
504 pub fn_name: String,
505 pub replay: ComponentReplayData,
506}
507
508#[derive(Default)]
509struct ComponentReplayTracker {
510 previous_nodes: HashMap<u64, ReplayNodeSnapshot>,
511 current_nodes: HashMap<u64, ReplayNodeSnapshot>,
512}
513
514fn with_component_replay_tracker<R>(f: impl FnOnce(&ComponentReplayTracker) -> R) -> R {
515 RUNTIME_GLOBALS.with(|globals| f(&globals.component_replay_tracker.borrow()))
516}
517
518fn with_component_replay_tracker_mut<R>(f: impl FnOnce(&mut ComponentReplayTracker) -> R) -> R {
519 RUNTIME_GLOBALS.with(|globals| f(&mut globals.component_replay_tracker.borrow_mut()))
520}
521
522pub(crate) fn begin_frame_component_replay_tracking() {
523 with_component_replay_tracker_mut(|tracker| tracker.current_nodes.clear());
524}
525
526pub(crate) fn finalize_frame_component_replay_tracking() {
527 with_component_replay_tracker_mut(|tracker| {
528 tracker.previous_nodes = std::mem::take(&mut tracker.current_nodes);
529 });
530}
531
532pub(crate) fn finalize_frame_component_replay_tracking_partial() {
533 with_component_replay_tracker_mut(|tracker| {
534 let current = std::mem::take(&mut tracker.current_nodes);
535 tracker.previous_nodes.extend(current);
536 });
537}
538
539pub(crate) fn reset_component_replay_tracking() {
540 with_component_replay_tracker_mut(|tracker| {
541 *tracker = ComponentReplayTracker::default();
542 });
543}
544
545pub(crate) fn previous_component_replay_nodes() -> HashMap<u64, ReplayNodeSnapshot> {
546 with_component_replay_tracker(|tracker| tracker.previous_nodes.clone())
547}
548
549pub(crate) fn remove_previous_component_replay_nodes(instance_keys: &HashSet<u64>) {
550 if instance_keys.is_empty() {
551 return;
552 }
553 with_component_replay_tracker_mut(|tracker| {
554 tracker
555 .previous_nodes
556 .retain(|instance_key, _| !instance_keys.contains(instance_key));
557 tracker
558 .current_nodes
559 .retain(|instance_key, _| !instance_keys.contains(instance_key));
560 });
561}
562
563#[derive(Default)]
564struct BuildInvalidationTracker {
565 dirty_instance_keys: HashSet<u64>,
566}
567
568#[derive(Default)]
569pub(crate) struct BuildInvalidationSet {
570 pub dirty_instance_keys: HashSet<u64>,
571}
572
573#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
574struct StateReadDependencyKey {
575 slot: SlotHandle,
578 generation: u64,
579}
580
581#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
582struct FocusReadDependencyKey {
583 kind: FocusReadDependencyKind,
584}
585
586#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
587enum FocusReadDependencyKind {
588 Handle(FocusHandleId),
589 Requester(FocusRequesterId),
590}
591
592#[derive(Default)]
593struct StateReadDependencyTracker {
594 readers_by_state: HashMap<StateReadDependencyKey, HashSet<u64>>,
595 states_by_reader: HashMap<u64, HashSet<StateReadDependencyKey>>,
596}
597
598#[derive(Default)]
599struct FocusReadDependencyTracker {
600 readers_by_focus: HashMap<FocusReadDependencyKey, HashSet<u64>>,
601 focus_by_reader: HashMap<u64, HashSet<FocusReadDependencyKey>>,
602}
603
604#[derive(Default)]
605struct RenderSlotReadDependencyTracker {
606 readers_by_slot: HashMap<FunctorHandle, HashSet<u64>>,
607 slots_by_reader: HashMap<u64, HashSet<FunctorHandle>>,
608}
609
610type RedrawWaker = Arc<dyn Fn() + Send + Sync + 'static>;
611
612fn with_build_invalidation_tracker<R>(f: impl FnOnce(&BuildInvalidationTracker) -> R) -> R {
613 RUNTIME_GLOBALS.with(|globals| f(&globals.build_invalidation_tracker.borrow()))
614}
615
616fn with_build_invalidation_tracker_mut<R>(f: impl FnOnce(&mut BuildInvalidationTracker) -> R) -> R {
617 RUNTIME_GLOBALS.with(|globals| f(&mut globals.build_invalidation_tracker.borrow_mut()))
618}
619
620fn with_state_read_dependency_tracker<R>(f: impl FnOnce(&StateReadDependencyTracker) -> R) -> R {
621 RUNTIME_GLOBALS.with(|globals| f(&globals.state_read_dependency_tracker.borrow()))
622}
623
624fn with_state_read_dependency_tracker_mut<R>(
625 f: impl FnOnce(&mut StateReadDependencyTracker) -> R,
626) -> R {
627 RUNTIME_GLOBALS.with(|globals| f(&mut globals.state_read_dependency_tracker.borrow_mut()))
628}
629
630fn with_focus_read_dependency_tracker<R>(f: impl FnOnce(&FocusReadDependencyTracker) -> R) -> R {
631 RUNTIME_GLOBALS.with(|globals| f(&globals.focus_read_dependency_tracker.borrow()))
632}
633
634fn with_focus_read_dependency_tracker_mut<R>(
635 f: impl FnOnce(&mut FocusReadDependencyTracker) -> R,
636) -> R {
637 RUNTIME_GLOBALS.with(|globals| f(&mut globals.focus_read_dependency_tracker.borrow_mut()))
638}
639
640fn with_render_slot_read_dependency_tracker<R>(
641 f: impl FnOnce(&RenderSlotReadDependencyTracker) -> R,
642) -> R {
643 RUNTIME_GLOBALS.with(|globals| f(&globals.render_slot_read_dependency_tracker.borrow()))
644}
645
646fn with_render_slot_read_dependency_tracker_mut<R>(
647 f: impl FnOnce(&mut RenderSlotReadDependencyTracker) -> R,
648) -> R {
649 RUNTIME_GLOBALS.with(|globals| f(&mut globals.render_slot_read_dependency_tracker.borrow_mut()))
650}
651
652fn with_redraw_waker<R>(f: impl FnOnce(&Option<RedrawWaker>) -> R) -> R {
653 RUNTIME_GLOBALS.with(|globals| f(&globals.redraw_waker.borrow()))
654}
655
656fn with_redraw_waker_mut<R>(f: impl FnOnce(&mut Option<RedrawWaker>) -> R) -> R {
657 RUNTIME_GLOBALS.with(|globals| f(&mut globals.redraw_waker.borrow_mut()))
658}
659
660fn schedule_runtime_redraw() {
661 let callback = with_redraw_waker(Clone::clone);
662 if let Some(callback) = callback {
663 callback();
664 }
665}
666
667pub(crate) fn install_redraw_waker(callback: RedrawWaker) {
668 with_redraw_waker_mut(|waker| *waker = Some(callback));
669}
670
671pub(crate) fn clear_redraw_waker() {
672 with_redraw_waker_mut(|waker| *waker = None);
673}
674
675pub(crate) fn current_replay_boundary_instance_key_from_scope() -> Option<u64> {
676 with_execution_context(|context| context.current_component_instance_stack.last().copied())
677}
678
679fn with_persistent_focus_handle_store<R>(f: impl FnOnce(&PersistentFocusHandleStore) -> R) -> R {
680 RUNTIME_GLOBALS.with(|globals| f(&globals.persistent_focus_handle_store.borrow()))
681}
682
683fn with_persistent_focus_handle_store_mut<R>(
684 f: impl FnOnce(&mut PersistentFocusHandleStore) -> R,
685) -> R {
686 RUNTIME_GLOBALS.with(|globals| f(&mut globals.persistent_focus_handle_store.borrow_mut()))
687}
688
689fn current_persistent_focus_handle_key<K: Hash>(slot_key: K) -> PersistentFocusHandleKey {
690 let Some(instance_key) = current_replay_boundary_instance_key_from_scope() else {
691 panic!("persistent focus handles must be requested during a component build");
692 };
693 let slot_hash = hash_components(&[&slot_key]);
694 PersistentFocusHandleKey {
695 instance_key,
696 slot_hash,
697 }
698}
699
700pub(crate) fn persistent_focus_target_for_current_instance<K: Hash>(slot_key: K) -> FocusNode {
701 let key = current_persistent_focus_handle_key(slot_key);
702 with_persistent_focus_handle_store_mut(|store| match store.targets.entry(key) {
703 std::collections::hash_map::Entry::Occupied(mut entry) => entry.get_mut().mark_live(),
704 std::collections::hash_map::Entry::Vacant(entry) => {
705 let value = FocusNode::new();
706 entry.insert(PersistentFocusHandleEntry::new(value));
707 value
708 }
709 })
710}
711
712pub(crate) fn persistent_focus_scope_for_current_instance<K: Hash>(slot_key: K) -> FocusScopeNode {
713 let key = current_persistent_focus_handle_key(slot_key);
714 with_persistent_focus_handle_store_mut(|store| match store.scopes.entry(key) {
715 std::collections::hash_map::Entry::Occupied(mut entry) => entry.get_mut().mark_live(),
716 std::collections::hash_map::Entry::Vacant(entry) => {
717 let value = FocusScopeNode::new();
718 entry.insert(PersistentFocusHandleEntry::new(value));
719 value
720 }
721 })
722}
723
724pub(crate) fn persistent_focus_group_for_current_instance<K: Hash>(slot_key: K) -> FocusGroupNode {
725 let key = current_persistent_focus_handle_key(slot_key);
726 with_persistent_focus_handle_store_mut(|store| match store.groups.entry(key) {
727 std::collections::hash_map::Entry::Occupied(mut entry) => entry.get_mut().mark_live(),
728 std::collections::hash_map::Entry::Vacant(entry) => {
729 let value = FocusGroupNode::new();
730 entry.insert(PersistentFocusHandleEntry::new(value));
731 value
732 }
733 })
734}
735
736pub(crate) fn has_persistent_focus_handle(handle_id: FocusHandleId) -> bool {
737 with_persistent_focus_handle_store(|store| store.contains_handle(handle_id))
738}
739
740pub(crate) fn retain_persistent_focus_handles(
741 live_instance_keys: &HashSet<u64>,
742) -> RemovedPersistentFocusHandles {
743 with_persistent_focus_handle_store_mut(|store| store.retain_instance_keys(live_instance_keys))
744}
745
746pub(crate) fn clear_persistent_focus_handles() {
747 with_persistent_focus_handle_store_mut(PersistentFocusHandleStore::clear);
748}
749
750fn take_next_node_instance_logic_id_override() -> Option<u64> {
751 with_execution_context_mut(|context| context.next_node_instance_logic_id_override.take())
752}
753
754pub(crate) fn with_replay_scope<R>(
762 instance_logic_id: u64,
763 group_path: &[u64],
764 instance_key_override: Option<u64>,
765 f: impl FnOnce() -> R,
766) -> R {
767 struct ReplayScopeGuard {
768 previous_group_path: Option<Vec<u64>>,
769 previous_instance_key_stack: Option<Vec<u64>>,
770 previous_instance_logic_id_override: Option<Option<u64>>,
771 }
772
773 impl Drop for ReplayScopeGuard {
774 fn drop(&mut self) {
775 if let Some(previous_group_path) = self.previous_group_path.take() {
776 with_execution_context_mut(|context| {
777 context.group_path_stack = previous_group_path;
778 });
779 }
780 if let Some(previous_instance_key_stack) = self.previous_instance_key_stack.take() {
781 with_execution_context_mut(|context| {
782 context.instance_key_stack = previous_instance_key_stack;
783 });
784 }
785 if let Some(previous_instance_logic_id_override) =
786 self.previous_instance_logic_id_override.take()
787 {
788 with_execution_context_mut(|context| {
789 context.next_node_instance_logic_id_override =
790 previous_instance_logic_id_override;
791 });
792 }
793 }
794 }
795
796 let previous_group_path = with_execution_context_mut(|context| {
797 std::mem::replace(&mut context.group_path_stack, group_path.to_vec())
798 });
799 let previous_instance_key_stack = with_execution_context_mut(|context| {
800 let next_stack = instance_key_override.into_iter().collect::<Vec<_>>();
801 std::mem::replace(&mut context.instance_key_stack, next_stack)
802 });
803 let previous_instance_logic_id_override = with_execution_context_mut(|context| {
804 context
805 .next_node_instance_logic_id_override
806 .replace(instance_logic_id)
807 });
808 let _guard = ReplayScopeGuard {
809 previous_group_path: Some(previous_group_path),
810 previous_instance_key_stack: Some(previous_instance_key_stack),
811 previous_instance_logic_id_override: Some(previous_instance_logic_id_override),
812 };
813
814 f()
815}
816
817pub(crate) fn with_build_dirty_instance_keys<R>(
818 dirty_instance_keys: &HashSet<u64>,
819 f: impl FnOnce() -> R,
820) -> R {
821 struct BuildDirtyScopeGuard {
822 popped: bool,
823 }
824
825 impl Drop for BuildDirtyScopeGuard {
826 fn drop(&mut self) {
827 if self.popped {
828 return;
829 }
830 with_execution_context_mut(|context| {
831 let popped = context.build_dirty_instance_keys_stack.pop();
832 debug_assert!(
833 popped.is_some(),
834 "BUILD_DIRTY_INSTANCE_KEYS_STACK underflow: attempted to pop from empty stack"
835 );
836 });
837 self.popped = true;
838 }
839 }
840
841 with_execution_context_mut(|context| {
842 context
843 .build_dirty_instance_keys_stack
844 .push(Arc::new(dirty_instance_keys.clone()));
845 });
846 let _guard = BuildDirtyScopeGuard { popped: false };
847 f()
848}
849
850pub(crate) fn is_instance_key_build_dirty(instance_key: u64) -> bool {
851 with_execution_context(|context| {
852 context
853 .build_dirty_instance_keys_stack
854 .last()
855 .is_some_and(|dirty_instance_keys| dirty_instance_keys.contains(&instance_key))
856 })
857}
858
859fn consume_pending_build_invalidation(instance_key: u64) -> bool {
860 with_build_invalidation_tracker_mut(|tracker| tracker.dirty_instance_keys.remove(&instance_key))
861}
862
863pub(crate) fn record_replay_boundary_invalidation_for_instance_key(instance_key: u64) {
864 let inserted = with_build_invalidation_tracker_mut(|tracker| {
865 tracker.dirty_instance_keys.insert(instance_key)
866 });
867 if inserted {
868 schedule_runtime_redraw();
869 }
870}
871
872fn track_state_read_dependency(slot: SlotHandle, generation: u64) {
873 if !matches!(current_phase(), Some(RuntimePhase::Build)) {
874 return;
875 }
876 let Some(reader_instance_key) = current_replay_boundary_instance_key_from_scope() else {
877 return;
878 };
879
880 let key = StateReadDependencyKey { slot, generation };
881 with_state_read_dependency_tracker_mut(|tracker| {
882 if tracker
883 .readers_by_state
884 .get(&key)
885 .is_some_and(|readers| readers.contains(&reader_instance_key))
886 {
887 return;
888 }
889 tracker
890 .readers_by_state
891 .entry(key)
892 .or_default()
893 .insert(reader_instance_key);
894 tracker
895 .states_by_reader
896 .entry(reader_instance_key)
897 .or_default()
898 .insert(key);
899 });
900}
901
902fn state_read_subscribers(slot: SlotHandle, generation: u64) -> Vec<u64> {
903 let key = StateReadDependencyKey { slot, generation };
904 with_state_read_dependency_tracker(|tracker| {
905 tracker
906 .readers_by_state
907 .get(&key)
908 .map(|readers| readers.iter().copied().collect())
909 .unwrap_or_default()
910 })
911}
912
913fn track_focus_dependency(kind: FocusReadDependencyKind) {
914 if !matches!(current_phase(), Some(RuntimePhase::Build)) {
915 return;
916 }
917 let Some(reader_instance_key) = current_replay_boundary_instance_key_from_scope() else {
918 return;
919 };
920
921 let key = FocusReadDependencyKey { kind };
922 with_focus_read_dependency_tracker_mut(|tracker| {
923 if tracker
924 .readers_by_focus
925 .get(&key)
926 .is_some_and(|readers| readers.contains(&reader_instance_key))
927 {
928 return;
929 }
930 tracker
931 .readers_by_focus
932 .entry(key)
933 .or_default()
934 .insert(reader_instance_key);
935 tracker
936 .focus_by_reader
937 .entry(reader_instance_key)
938 .or_default()
939 .insert(key);
940 });
941}
942
943fn focus_read_subscribers_by_kind(kind: FocusReadDependencyKind) -> Vec<u64> {
944 let key = FocusReadDependencyKey { kind };
945 with_focus_read_dependency_tracker(|tracker| {
946 tracker
947 .readers_by_focus
948 .get(&key)
949 .map(|readers| readers.iter().copied().collect())
950 .unwrap_or_default()
951 })
952}
953
954pub(crate) fn track_focus_read_dependency(handle_id: FocusHandleId) {
955 track_focus_dependency(FocusReadDependencyKind::Handle(handle_id));
956}
957
958pub(crate) fn track_focus_requester_read_dependency(requester_id: FocusRequesterId) {
959 track_focus_dependency(FocusReadDependencyKind::Requester(requester_id));
960}
961
962pub(crate) fn focus_read_subscribers(handle_id: FocusHandleId) -> Vec<u64> {
963 focus_read_subscribers_by_kind(FocusReadDependencyKind::Handle(handle_id))
964}
965
966pub(crate) fn focus_requester_read_subscribers(requester_id: FocusRequesterId) -> Vec<u64> {
967 focus_read_subscribers_by_kind(FocusReadDependencyKind::Requester(requester_id))
968}
969
970pub(crate) fn track_render_slot_read_dependency(handle: FunctorHandle) {
971 if !matches!(current_phase(), Some(RuntimePhase::Build)) {
972 return;
973 }
974 let Some(reader_instance_key) = current_replay_boundary_instance_key_from_scope() else {
975 return;
976 };
977
978 with_render_slot_read_dependency_tracker_mut(|tracker| {
979 if tracker
980 .readers_by_slot
981 .get(&handle)
982 .is_some_and(|readers| readers.contains(&reader_instance_key))
983 {
984 return;
985 }
986 tracker
987 .readers_by_slot
988 .entry(handle)
989 .or_default()
990 .insert(reader_instance_key);
991 tracker
992 .slots_by_reader
993 .entry(reader_instance_key)
994 .or_default()
995 .insert(handle);
996 });
997}
998
999fn render_slot_read_subscribers(handle: FunctorHandle) -> Vec<u64> {
1000 with_render_slot_read_dependency_tracker(|tracker| {
1001 tracker
1002 .readers_by_slot
1003 .get(&handle)
1004 .map(|readers| readers.iter().copied().collect())
1005 .unwrap_or_default()
1006 })
1007}
1008
1009pub(crate) fn remove_state_read_dependencies(instance_keys: &HashSet<u64>) {
1010 if instance_keys.is_empty() {
1011 return;
1012 }
1013 with_state_read_dependency_tracker_mut(|tracker| {
1014 for instance_key in instance_keys {
1015 let Some(state_keys) = tracker.states_by_reader.remove(instance_key) else {
1016 continue;
1017 };
1018 for state_key in state_keys {
1019 let mut remove_entry = false;
1020 if let Some(readers) = tracker.readers_by_state.get_mut(&state_key) {
1021 readers.remove(instance_key);
1022 remove_entry = readers.is_empty();
1023 }
1024 if remove_entry {
1025 tracker.readers_by_state.remove(&state_key);
1026 }
1027 }
1028 }
1029 });
1030}
1031
1032pub(crate) fn remove_focus_read_dependencies(instance_keys: &HashSet<u64>) {
1033 if instance_keys.is_empty() {
1034 return;
1035 }
1036 with_focus_read_dependency_tracker_mut(|tracker| {
1037 for instance_key in instance_keys {
1038 let Some(focus_keys) = tracker.focus_by_reader.remove(instance_key) else {
1039 continue;
1040 };
1041 for focus_key in focus_keys {
1042 let mut remove_entry = false;
1043 if let Some(readers) = tracker.readers_by_focus.get_mut(&focus_key) {
1044 readers.remove(instance_key);
1045 remove_entry = readers.is_empty();
1046 }
1047 if remove_entry {
1048 tracker.readers_by_focus.remove(&focus_key);
1049 }
1050 }
1051 }
1052 });
1053}
1054
1055pub(crate) fn remove_render_slot_read_dependencies(instance_keys: &HashSet<u64>) {
1056 if instance_keys.is_empty() {
1057 return;
1058 }
1059 with_render_slot_read_dependency_tracker_mut(|tracker| {
1060 for instance_key in instance_keys {
1061 let Some(slot_keys) = tracker.slots_by_reader.remove(instance_key) else {
1062 continue;
1063 };
1064 for slot_key in slot_keys {
1065 let mut remove_entry = false;
1066 if let Some(readers) = tracker.readers_by_slot.get_mut(&slot_key) {
1067 readers.remove(instance_key);
1068 remove_entry = readers.is_empty();
1069 }
1070 if remove_entry {
1071 tracker.readers_by_slot.remove(&slot_key);
1072 }
1073 }
1074 }
1075 });
1076}
1077
1078pub(crate) fn reset_state_read_dependencies() {
1079 with_state_read_dependency_tracker_mut(|tracker| {
1080 *tracker = StateReadDependencyTracker::default();
1081 });
1082}
1083
1084pub(crate) fn reset_focus_read_dependencies() {
1085 with_focus_read_dependency_tracker_mut(|tracker| {
1086 *tracker = FocusReadDependencyTracker::default();
1087 });
1088}
1089
1090pub(crate) fn reset_render_slot_read_dependencies() {
1091 with_render_slot_read_dependency_tracker_mut(|tracker| {
1092 *tracker = RenderSlotReadDependencyTracker::default();
1093 });
1094}
1095
1096pub(crate) fn take_build_invalidations() -> BuildInvalidationSet {
1097 with_build_invalidation_tracker_mut(|tracker| BuildInvalidationSet {
1098 dirty_instance_keys: std::mem::take(&mut tracker.dirty_instance_keys),
1099 })
1100}
1101
1102pub(crate) fn reset_build_invalidations() {
1103 with_build_invalidation_tracker_mut(|tracker| {
1104 *tracker = BuildInvalidationTracker::default();
1105 });
1106}
1107
1108pub(crate) fn remove_build_invalidations(instance_keys: &HashSet<u64>) {
1109 if instance_keys.is_empty() {
1110 return;
1111 }
1112 with_build_invalidation_tracker_mut(|tracker| {
1113 tracker
1114 .dirty_instance_keys
1115 .retain(|instance_key| !instance_keys.contains(instance_key));
1116 });
1117}
1118
1119pub(crate) fn has_pending_build_invalidations() -> bool {
1120 with_build_invalidation_tracker(|tracker| !tracker.dirty_instance_keys.is_empty())
1121}
1122
1123#[derive(Default)]
1124struct FrameClockTracker {
1125 frame_origin: Option<Instant>,
1126 current_frame_time: Option<Instant>,
1127 current_frame_nanos: u64,
1128 previous_frame_time: Option<Instant>,
1129 frame_delta: Duration,
1130 receivers: HashMap<FrameNanosReceiverKey, FrameNanosReceiver>,
1131}
1132
1133#[derive(Hash, Eq, PartialEq, Clone, Copy)]
1134struct FrameNanosReceiverKey {
1135 instance_logic_id: u64,
1136 receiver_hash: u64,
1137}
1138
1139#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1141pub enum FrameNanosControl {
1142 Continue,
1144 Stop,
1146}
1147
1148type FrameNanosReceiverCallback = Box<dyn FnMut(u64) -> FrameNanosControl + Send + 'static>;
1149
1150struct FrameNanosReceiver {
1151 owner_instance_key: u64,
1152 callback: FrameNanosReceiverCallback,
1153}
1154
1155fn with_frame_clock_tracker<R>(f: impl FnOnce(&FrameClockTracker) -> R) -> R {
1156 RUNTIME_GLOBALS.with(|globals| f(&globals.frame_clock_tracker.borrow()))
1157}
1158
1159fn with_frame_clock_tracker_mut<R>(f: impl FnOnce(&mut FrameClockTracker) -> R) -> R {
1160 RUNTIME_GLOBALS.with(|globals| f(&mut globals.frame_clock_tracker.borrow_mut()))
1161}
1162
1163pub(crate) fn begin_frame_clock(now: Instant) {
1164 with_frame_clock_tracker_mut(|tracker| {
1165 let frame_origin = *tracker.frame_origin.get_or_insert(now);
1166 tracker.previous_frame_time = tracker.current_frame_time;
1167 tracker.current_frame_time = Some(now);
1168 tracker.current_frame_nanos = now
1169 .saturating_duration_since(frame_origin)
1170 .as_nanos()
1171 .min(u64::MAX as u128) as u64;
1172 tracker.frame_delta = tracker
1173 .previous_frame_time
1174 .map(|previous| now.saturating_duration_since(previous))
1175 .unwrap_or_default();
1176 });
1177}
1178
1179pub(crate) fn reset_frame_clock() {
1180 with_frame_clock_tracker_mut(|tracker| *tracker = FrameClockTracker::default());
1181}
1182
1183pub(crate) fn has_pending_frame_nanos_receivers() -> bool {
1184 with_frame_clock_tracker(|tracker| !tracker.receivers.is_empty())
1185}
1186
1187pub(crate) fn tick_frame_nanos_receivers() {
1188 with_frame_clock_tracker_mut(|tracker| {
1189 let frame_nanos = tracker.current_frame_nanos;
1190 tracker.receivers.retain(|_, receiver| {
1191 matches!(
1192 (receiver.callback)(frame_nanos),
1193 FrameNanosControl::Continue
1194 )
1195 });
1196 });
1197}
1198
1199pub(crate) fn remove_frame_nanos_receivers(instance_keys: &HashSet<u64>) {
1200 if instance_keys.is_empty() {
1201 return;
1202 }
1203 with_frame_clock_tracker_mut(|tracker| {
1204 tracker
1205 .receivers
1206 .retain(|_, receiver| !instance_keys.contains(&receiver.owner_instance_key));
1207 });
1208}
1209
1210pub(crate) fn clear_frame_nanos_receivers() {
1211 with_frame_clock_tracker_mut(|tracker| tracker.receivers.clear());
1212}
1213
1214pub fn current_frame_time() -> Option<Instant> {
1218 with_frame_clock_tracker(|tracker| tracker.current_frame_time)
1219}
1220
1221pub fn current_frame_nanos() -> u64 {
1223 with_frame_clock_tracker(|tracker| tracker.current_frame_nanos)
1224}
1225
1226pub fn frame_delta() -> Duration {
1228 with_frame_clock_tracker(|tracker| tracker.frame_delta)
1229}
1230
1231fn ensure_frame_receive_phase() {
1232 match current_phase() {
1233 Some(RuntimePhase::Build) => {}
1234 Some(RuntimePhase::Measure) => {
1235 panic!("receive_frame_nanos must not be called inside measure")
1236 }
1237 Some(RuntimePhase::Input) => {
1238 panic!("receive_frame_nanos must be called inside a tessera component build")
1239 }
1240 None => panic!("receive_frame_nanos must be called inside a tessera component build"),
1241 }
1242}
1243
1244fn compute_frame_nanos_receiver_key() -> FrameNanosReceiverKey {
1245 let instance_logic_id = current_instance_logic_id();
1246 let group_path_hash = current_group_path_hash();
1247
1248 let call_counter = next_order_counter(
1249 OrderCounterKind::FrameReceiver,
1250 "ORDER_FRAME_STACK is empty; receive_frame_nanos must be called inside a component",
1251 );
1252
1253 let receiver_hash = hash_components(&[&group_path_hash, &call_counter]);
1254 FrameNanosReceiverKey {
1255 instance_logic_id,
1256 receiver_hash,
1257 }
1258}
1259
1260pub fn receive_frame_nanos<F>(callback: F)
1266where
1267 F: FnMut(u64) -> FrameNanosControl + Send + 'static,
1268{
1269 ensure_frame_receive_phase();
1270 let frame_nanos_state = remember(current_frame_nanos);
1271 let _ = frame_nanos_state.get();
1272
1273 let owner_instance_key = current_replay_boundary_instance_key_from_scope()
1274 .unwrap_or_else(|| panic!("receive_frame_nanos requires an active component node context"));
1275 let key = compute_frame_nanos_receiver_key();
1276
1277 with_frame_clock_tracker_mut(|tracker| {
1278 tracker.receivers.entry(key).or_insert_with(|| {
1279 let mut callback = callback;
1280 FrameNanosReceiver {
1281 owner_instance_key,
1282 callback: Box::new(move |frame_nanos| {
1283 if !frame_nanos_state.is_alive() {
1284 return FrameNanosControl::Stop;
1285 }
1286 frame_nanos_state.set(frame_nanos);
1287 callback(frame_nanos)
1288 }),
1289 }
1290 });
1291 });
1292}
1293
1294pub(crate) fn drop_slots_for_instance_logic_ids(instance_logic_ids: &HashSet<u64>) {
1295 if instance_logic_ids.is_empty() {
1296 return;
1297 }
1298
1299 with_slot_table_mut(|table| {
1300 let mut freed: Vec<(SlotHandle, SlotKey)> = Vec::new();
1301 for (slot, entry) in table.entries.iter() {
1302 if !instance_logic_ids.contains(&entry.key.instance_logic_id) {
1303 continue;
1304 }
1305 if entry.retained {
1306 continue;
1307 }
1308 freed.push((slot, entry.key));
1309 }
1310 for (slot, key) in freed {
1311 table.entries.remove(slot);
1312 table.key_to_slot.remove(&key);
1313 }
1314 for instance_logic_id in instance_logic_ids {
1315 table.cursors_by_instance_logic_id.remove(instance_logic_id);
1316 }
1317 });
1318}
1319
1320fn with_layout_dirty_tracker_mut<R>(f: impl FnOnce(&mut LayoutDirtyTracker) -> R) -> R {
1321 RUNTIME_GLOBALS.with(|globals| f(&mut globals.layout_dirty_tracker.borrow_mut()))
1322}
1323
1324fn record_layout_policy_dirty(
1325 instance_key: u64,
1326 layout_policy: &dyn LayoutPolicyDyn,
1327 modifier: &Modifier,
1328) {
1329 if current_phase() != Some(RuntimePhase::Build) {
1330 return;
1331 }
1332 with_layout_dirty_tracker_mut(|tracker| {
1333 let (measure_changed, placement_changed, next_layout_inputs) =
1334 match tracker.previous_layout_inputs_by_node.remove(&instance_key) {
1335 Some(previous) => {
1336 let measure_changed = !previous.policy.dyn_measure_eq(layout_policy)
1337 || !previous.modifier.layout_measure_eq(modifier);
1338 let placement_changed = !previous.policy.dyn_placement_eq(layout_policy)
1339 || !previous.modifier.layout_placement_eq(modifier);
1340 if !measure_changed && !placement_changed {
1341 (false, false, previous)
1342 } else {
1343 (
1344 measure_changed,
1345 placement_changed,
1346 LayoutInputSnapshot {
1347 policy: layout_policy.clone_box(),
1348 modifier: modifier.clone(),
1349 },
1350 )
1351 }
1352 }
1353 None => (
1354 true,
1355 true,
1356 LayoutInputSnapshot {
1357 policy: layout_policy.clone_box(),
1358 modifier: modifier.clone(),
1359 },
1360 ),
1361 };
1362 if measure_changed {
1363 tracker
1364 .pending_measure_self_dirty_nodes
1365 .insert(instance_key);
1366 } else if placement_changed {
1367 tracker
1368 .pending_placement_self_dirty_nodes
1369 .insert(instance_key);
1370 }
1371 tracker
1372 .frame_layout_inputs_by_node
1373 .insert(instance_key, next_layout_inputs);
1374 });
1375}
1376
1377pub(crate) fn begin_frame_layout_dirty_tracking() {
1378 with_layout_dirty_tracker_mut(|tracker| {
1379 tracker.frame_layout_inputs_by_node.clear();
1380 tracker.pending_measure_self_dirty_nodes.clear();
1381 tracker.pending_placement_self_dirty_nodes.clear();
1382 });
1383}
1384
1385pub(crate) fn finalize_frame_layout_dirty_tracking() {
1386 with_layout_dirty_tracker_mut(|tracker| {
1387 tracker.ready_measure_self_dirty_nodes =
1388 std::mem::take(&mut tracker.pending_measure_self_dirty_nodes);
1389 tracker.ready_placement_self_dirty_nodes =
1390 std::mem::take(&mut tracker.pending_placement_self_dirty_nodes);
1391 tracker.previous_layout_inputs_by_node =
1392 std::mem::take(&mut tracker.frame_layout_inputs_by_node);
1393 });
1394}
1395
1396pub(crate) fn take_layout_dirty_nodes() -> LayoutDirtyNodes {
1397 with_layout_dirty_tracker_mut(|tracker| LayoutDirtyNodes {
1398 measure_self_nodes: std::mem::take(&mut tracker.ready_measure_self_dirty_nodes),
1399 placement_self_nodes: std::mem::take(&mut tracker.ready_placement_self_dirty_nodes),
1400 })
1401}
1402
1403pub(crate) fn reset_layout_dirty_tracking() {
1404 with_layout_dirty_tracker_mut(|tracker| *tracker = LayoutDirtyTracker::default());
1405}
1406
1407fn record_component_replay_snapshot(runtime: &TesseraRuntime, node_id: NodeId) {
1408 let Some(node) = runtime.component_tree.get(node_id) else {
1409 return;
1410 };
1411 let Some(replay) = node.replay.clone() else {
1412 return;
1413 };
1414
1415 let tree = runtime.component_tree.tree();
1416 let parent_instance_key = tree
1417 .get(node_id)
1418 .and_then(|n| n.parent())
1419 .and_then(|parent_id| tree.get(parent_id))
1420 .map(|parent| parent.get().instance_key);
1421
1422 let snapshot = ReplayNodeSnapshot {
1423 instance_key: node.instance_key,
1424 parent_instance_key,
1425 instance_logic_id: node.instance_logic_id,
1426 group_path: current_group_path(),
1427 instance_key_override: current_instance_key_override(),
1428 fn_name: node.fn_name.clone(),
1429 replay,
1430 };
1431 with_component_replay_tracker_mut(|tracker| {
1432 tracker
1433 .current_nodes
1434 .insert(snapshot.instance_key, snapshot);
1435 });
1436}
1437
1438pub(crate) fn reconcile_layout_structure(
1439 current_children_by_node: &HashMap<u64, Vec<u64>>,
1440) -> StructureReconcileResult {
1441 with_layout_dirty_tracker_mut(|tracker| {
1442 let previous_children_by_node = &tracker.previous_children_by_node;
1443
1444 let mut changed_nodes = HashSet::default();
1445 let mut removed_nodes = HashSet::default();
1446
1447 for (node, current_children) in current_children_by_node {
1448 match previous_children_by_node.get(node) {
1449 Some(previous_children) if previous_children == current_children => {}
1450 _ => {
1451 changed_nodes.insert(*node);
1452 }
1453 }
1454 }
1455
1456 for node in previous_children_by_node.keys().copied() {
1457 if !current_children_by_node.contains_key(&node) {
1458 changed_nodes.insert(node);
1459 removed_nodes.insert(node);
1460 }
1461 }
1462
1463 tracker.previous_children_by_node = current_children_by_node.clone();
1464 StructureReconcileResult {
1465 changed_nodes,
1466 removed_nodes,
1467 }
1468 })
1469}
1470
1471pub struct State<T> {
1493 slot: SlotHandle,
1494 generation: u64,
1495 _marker: PhantomData<T>,
1496}
1497
1498impl<T> Copy for State<T> {}
1499
1500impl<T> Clone for State<T> {
1501 fn clone(&self) -> Self {
1502 *self
1503 }
1504}
1505
1506impl<T> PartialEq for State<T> {
1507 fn eq(&self, other: &Self) -> bool {
1508 self.slot == other.slot && self.generation == other.generation
1509 }
1510}
1511
1512impl<T> Eq for State<T> {}
1513
1514impl<T> Hash for State<T> {
1515 fn hash<H: Hasher>(&self, state: &mut H) {
1516 self.slot.hash(state);
1517 self.generation.hash(state);
1518 }
1519}
1520
1521impl<T> State<T> {
1522 fn new(slot: SlotHandle, generation: u64) -> Self {
1523 Self {
1524 slot,
1525 generation,
1526 _marker: PhantomData,
1527 }
1528 }
1529}
1530
1531impl<T> State<T>
1532where
1533 T: Send + Sync + 'static,
1534{
1535 fn is_alive(&self) -> bool {
1536 with_slot_table(|table| {
1537 let Some(entry) = table.entries.get(self.slot) else {
1538 return false;
1539 };
1540
1541 entry.generation == self.generation
1542 && entry.key.type_id == TypeId::of::<T>()
1543 && entry.value.is_some()
1544 })
1545 }
1546
1547 fn load_entry(&self) -> Arc<dyn Any + Send + Sync> {
1548 with_slot_table(|table| {
1549 let entry = table
1550 .entries
1551 .get(self.slot)
1552 .unwrap_or_else(|| panic!("State points to freed slot: {:?}", self.slot));
1553
1554 if entry.generation != self.generation {
1555 panic!(
1556 "State is stale (slot {:?}, generation {}, current generation {})",
1557 self.slot, self.generation, entry.generation
1558 );
1559 }
1560
1561 if entry.key.type_id != TypeId::of::<T>() {
1562 panic!(
1563 "State type mismatch for slot {:?}: expected {}, stored {:?}",
1564 self.slot,
1565 std::any::type_name::<T>(),
1566 entry.key.type_id
1567 );
1568 }
1569
1570 entry
1571 .value
1572 .as_ref()
1573 .unwrap_or_else(|| panic!("State slot {:?} has been cleared", self.slot))
1574 .clone()
1575 })
1576 }
1577
1578 fn load_lock(&self) -> Arc<RwLock<T>> {
1579 self.load_entry()
1580 .downcast::<RwLock<T>>()
1581 .unwrap_or_else(|_| panic!("State slot {:?} downcast failed", self.slot))
1582 }
1583
1584 pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
1586 track_state_read_dependency(self.slot, self.generation);
1587 let lock = self.load_lock();
1588 let guard = lock.read();
1589 f(&guard)
1590 }
1591
1592 #[track_caller]
1594 pub fn with_mut<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
1595 let lock = self.load_lock();
1596
1597 let result = {
1598 let mut guard = lock.write();
1599 f(&mut guard)
1600 };
1601
1602 let subscribers = state_read_subscribers(self.slot, self.generation);
1603 for instance_key in subscribers {
1604 record_replay_boundary_invalidation_for_instance_key(instance_key);
1605 }
1606 result
1607 }
1608
1609 pub fn get(&self) -> T
1611 where
1612 T: Clone,
1613 {
1614 self.with(Clone::clone)
1615 }
1616
1617 #[track_caller]
1619 pub fn set(&self, value: T) {
1620 self.with_mut(|slot| *slot = value);
1621 }
1622}
1623
1624struct RuntimeGlobals {
1625 slot_table: RefCell<SlotTable>,
1626 component_replay_tracker: RefCell<ComponentReplayTracker>,
1627 build_invalidation_tracker: RefCell<BuildInvalidationTracker>,
1628 state_read_dependency_tracker: RefCell<StateReadDependencyTracker>,
1629 focus_read_dependency_tracker: RefCell<FocusReadDependencyTracker>,
1630 render_slot_read_dependency_tracker: RefCell<RenderSlotReadDependencyTracker>,
1631 redraw_waker: RefCell<Option<RedrawWaker>>,
1632 persistent_focus_handle_store: RefCell<PersistentFocusHandleStore>,
1633 frame_clock_tracker: RefCell<FrameClockTracker>,
1634 layout_dirty_tracker: RefCell<LayoutDirtyTracker>,
1635 runtime: RefCell<TesseraRuntime>,
1636}
1637
1638impl RuntimeGlobals {
1639 fn new() -> Self {
1640 Self {
1641 slot_table: RefCell::new(SlotTable::default()),
1642 component_replay_tracker: RefCell::new(ComponentReplayTracker::default()),
1643 build_invalidation_tracker: RefCell::new(BuildInvalidationTracker::default()),
1644 state_read_dependency_tracker: RefCell::new(StateReadDependencyTracker::default()),
1645 focus_read_dependency_tracker: RefCell::new(FocusReadDependencyTracker::default()),
1646 render_slot_read_dependency_tracker: RefCell::new(
1647 RenderSlotReadDependencyTracker::default(),
1648 ),
1649 redraw_waker: RefCell::new(None),
1650 persistent_focus_handle_store: RefCell::new(PersistentFocusHandleStore::default()),
1651 frame_clock_tracker: RefCell::new(FrameClockTracker::default()),
1652 layout_dirty_tracker: RefCell::new(LayoutDirtyTracker::default()),
1653 runtime: RefCell::new(TesseraRuntime::default()),
1654 }
1655 }
1656}
1657
1658thread_local! {
1659 static RUNTIME_GLOBALS: RuntimeGlobals = RuntimeGlobals::new();
1660}
1661
1662#[derive(Default)]
1664pub struct TesseraRuntime {
1665 pub component_tree: ComponentTree,
1667 pub(crate) window_size: [u32; 2],
1669 pub cursor_icon_request: Option<winit::window::CursorIcon>,
1671 pub(crate) window_minimized: bool,
1673}
1674
1675impl TesseraRuntime {
1676 pub fn with<F, R>(f: F) -> R
1678 where
1679 F: FnOnce(&Self) -> R,
1680 {
1681 RUNTIME_GLOBALS.with(|globals| f(&globals.runtime.borrow()))
1682 }
1683
1684 pub fn with_mut<F, R>(f: F) -> R
1686 where
1687 F: FnOnce(&mut Self) -> R,
1688 {
1689 RUNTIME_GLOBALS.with(|globals| f(&mut globals.runtime.borrow_mut()))
1690 }
1691
1692 pub fn window_size(&self) -> [u32; 2] {
1694 self.window_size
1695 }
1696
1697 pub(crate) fn set_current_node_identity(&mut self, instance_key: u64, instance_logic_id: u64) {
1699 if let Some(node) = self.component_tree.current_node_mut() {
1700 node.instance_key = instance_key;
1701 node.instance_logic_id = instance_logic_id;
1702 } else {
1703 debug_assert!(
1704 false,
1705 "set_current_node_identity must be called inside a component build"
1706 );
1707 }
1708 }
1709
1710 pub(crate) fn set_current_component_replay<P>(
1712 &mut self,
1713 runner: Arc<dyn ErasedComponentRunner>,
1714 props: &P,
1715 ) -> bool
1716 where
1717 P: Prop,
1718 {
1719 let current_node_info = self
1720 .component_tree
1721 .current_node()
1722 .map(|node| (node.instance_key, node.instance_logic_id));
1723 let previous_replay = current_node_info.and_then(|(instance_key, instance_logic_id)| {
1724 with_component_replay_tracker(|tracker| {
1725 let previous = tracker.previous_nodes.get(&instance_key)?;
1726 if previous.instance_logic_id != instance_logic_id {
1727 return None;
1728 }
1729 if previous.replay.props.equals(props) {
1730 Some(previous.replay.clone())
1731 } else {
1732 None
1733 }
1734 })
1735 });
1736
1737 let pending_dirty = current_node_info
1738 .map(|(instance_key, _)| consume_pending_build_invalidation(instance_key))
1739 .unwrap_or(false);
1740
1741 if let Some((instance_key, instance_logic_id)) = current_node_info
1742 && let Some(replay) = previous_replay.clone()
1743 && !is_instance_key_build_dirty(instance_key)
1744 && !pending_dirty
1745 && self
1746 .component_tree
1747 .try_reuse_current_subtree(instance_key, instance_logic_id)
1748 {
1749 if let Some(node) = self.component_tree.current_node_mut() {
1750 node.replay = Some(replay);
1751 node.props_unchanged_from_previous = true;
1752 }
1753 return true;
1754 }
1755
1756 if let Some(node) = self.component_tree.current_node_mut() {
1757 if let Some(replay) = previous_replay {
1758 node.replay = Some(replay);
1759 node.props_unchanged_from_previous = true;
1760 } else {
1761 node.replay = Some(ComponentReplayData::new(runner, props));
1762 node.props_unchanged_from_previous = false;
1763 }
1764 } else {
1765 debug_assert!(
1766 false,
1767 "set_current_component_replay must be called inside a component build"
1768 );
1769 return false;
1770 }
1771 if let Some(node_id) = current_node_id() {
1772 record_component_replay_snapshot(self, node_id);
1773 }
1774 false
1775 }
1776
1777 pub(crate) fn set_current_layout_policy_boxed(&mut self, policy: Box<dyn LayoutPolicyDyn>) {
1779 if let Some(node) = self.component_tree.current_node_mut() {
1780 node.layout_policy = policy;
1781 } else {
1782 debug_assert!(
1783 false,
1784 "set_current_layout_policy_boxed must be called inside a component build"
1785 );
1786 }
1787 }
1788
1789 pub(crate) fn set_current_render_policy_boxed(&mut self, policy: Box<dyn RenderPolicyDyn>) {
1791 if let Some(node) = self.component_tree.current_node_mut() {
1792 node.render_policy = policy;
1793 } else {
1794 debug_assert!(
1795 false,
1796 "set_current_render_policy_boxed must be called inside a component build"
1797 );
1798 }
1799 }
1800
1801 pub(crate) fn append_current_modifier(&mut self, modifier: Modifier) {
1803 if let Some(node) = self.component_tree.current_node_mut() {
1804 node.modifier = node.modifier.clone().then(modifier);
1805 } else {
1806 debug_assert!(
1807 false,
1808 "append_current_modifier must be called inside a component build"
1809 );
1810 }
1811 }
1812
1813 pub(crate) fn set_current_accessibility(&mut self, accessibility: Option<AccessibilityNode>) {
1814 if let Some(node_id) = current_node_id()
1815 && let Some(metadata) = self.component_tree.metadatas_mut().get_mut(&node_id)
1816 {
1817 metadata.accessibility = accessibility;
1818 } else {
1819 debug_assert!(
1820 false,
1821 "set_current_accessibility must be called inside a component build"
1822 );
1823 }
1824 }
1825
1826 pub(crate) fn set_current_accessibility_action_handler(
1827 &mut self,
1828 handler: Option<AccessibilityActionHandler>,
1829 ) {
1830 if let Some(node_id) = current_node_id()
1831 && let Some(metadata) = self.component_tree.metadatas_mut().get_mut(&node_id)
1832 {
1833 metadata.accessibility_action_handler = handler;
1834 } else {
1835 debug_assert!(
1836 false,
1837 "set_current_accessibility_action_handler must be called inside a component build"
1838 );
1839 }
1840 }
1841
1842 pub(crate) fn bind_current_focus_requester(&mut self, requester: FocusRequester) {
1843 if let Some(current) = self.component_tree.current_node_mut() {
1844 current.focus_requester_binding = Some(requester);
1845 } else {
1846 debug_assert!(
1847 false,
1848 "bind_current_focus_requester must be called inside a component build"
1849 );
1850 }
1851 }
1852
1853 pub(crate) fn ensure_current_focus_target(&mut self, node: FocusNode) {
1854 if let Some(current) = self.component_tree.current_node_mut() {
1855 if current.focus_registration.is_none() {
1856 current.focus_registration = Some(FocusRegistration::target(node));
1857 }
1858 } else {
1859 debug_assert!(
1860 false,
1861 "ensure_current_focus_target must be called inside a component build"
1862 );
1863 }
1864 }
1865
1866 pub(crate) fn ensure_current_focus_scope(&mut self, scope: FocusScopeNode) {
1867 if let Some(current) = self.component_tree.current_node_mut() {
1868 if current.focus_registration.is_none() {
1869 current.focus_registration = Some(FocusRegistration::scope(scope));
1870 }
1871 } else {
1872 debug_assert!(
1873 false,
1874 "ensure_current_focus_scope must be called inside a component build"
1875 );
1876 }
1877 }
1878
1879 pub(crate) fn ensure_current_focus_group(&mut self, group: FocusGroupNode) {
1880 if let Some(current) = self.component_tree.current_node_mut() {
1881 if current.focus_registration.is_none() {
1882 current.focus_registration = Some(FocusRegistration::group(group));
1883 }
1884 } else {
1885 debug_assert!(
1886 false,
1887 "ensure_current_focus_group must be called inside a component build"
1888 );
1889 }
1890 }
1891
1892 pub(crate) fn current_focus_target_handle(&self) -> Option<FocusNode> {
1893 let registration = self.component_tree.current_node()?.focus_registration?;
1894 (registration.kind == FocusRegistrationKind::Target)
1895 .then(|| FocusNode::from_handle_id(registration.id))
1896 }
1897
1898 pub(crate) fn current_focus_scope_handle(&self) -> Option<FocusScopeNode> {
1899 let registration = self.component_tree.current_node()?.focus_registration?;
1900 (registration.kind == FocusRegistrationKind::Scope)
1901 .then(|| FocusScopeNode::from_handle_id(registration.id))
1902 }
1903
1904 pub(crate) fn current_focus_group_handle(&self) -> Option<FocusGroupNode> {
1905 let registration = self.component_tree.current_node()?.focus_registration?;
1906 (registration.kind == FocusRegistrationKind::Group)
1907 .then(|| FocusGroupNode::from_handle_id(registration.id))
1908 }
1909
1910 pub(crate) fn set_current_focus_properties(&mut self, properties: FocusProperties) {
1911 if let Some(current) = self.component_tree.current_node_mut() {
1912 if let Some(registration) = current.focus_registration.as_mut() {
1913 registration.properties = properties;
1914 } else {
1915 debug_assert!(
1916 false,
1917 "set_current_focus_properties requires focus_target, focus_scope, or focus_group first"
1918 );
1919 }
1920 } else {
1921 debug_assert!(
1922 false,
1923 "set_current_focus_properties must be called inside a component build"
1924 );
1925 }
1926 }
1927
1928 pub(crate) fn set_current_focus_traversal_policy(&mut self, policy: FocusTraversalPolicy) {
1929 if let Some(current) = self.component_tree.current_node_mut() {
1930 if current.focus_registration.is_some_and(|registration| {
1931 matches!(
1932 registration.kind,
1933 FocusRegistrationKind::Scope | FocusRegistrationKind::Group
1934 )
1935 }) {
1936 current.focus_traversal_policy = Some(policy);
1937 } else {
1938 debug_assert!(
1939 false,
1940 "set_current_focus_traversal_policy requires focus_scope or focus_group first"
1941 );
1942 }
1943 } else {
1944 debug_assert!(
1945 false,
1946 "set_current_focus_traversal_policy must be called inside a component build"
1947 );
1948 }
1949 }
1950
1951 pub(crate) fn set_current_focus_changed_handler(&mut self, handler: CallbackWith<FocusState>) {
1952 if let Some(current) = self.component_tree.current_node_mut() {
1953 current.focus_changed_handler = Some(handler);
1954 } else {
1955 debug_assert!(
1956 false,
1957 "set_current_focus_changed_handler must be called inside a component build"
1958 );
1959 }
1960 }
1961
1962 pub(crate) fn set_current_focus_event_handler(&mut self, handler: CallbackWith<FocusState>) {
1963 if let Some(current) = self.component_tree.current_node_mut() {
1964 current.focus_event_handler = Some(handler);
1965 } else {
1966 debug_assert!(
1967 false,
1968 "set_current_focus_event_handler must be called inside a component build"
1969 );
1970 }
1971 }
1972
1973 pub(crate) fn set_current_focus_beyond_bounds_handler(
1974 &mut self,
1975 handler: CallbackWith<FocusDirection, bool>,
1976 ) {
1977 if let Some(current) = self.component_tree.current_node_mut() {
1978 current.focus_beyond_bounds_handler = Some(handler);
1979 } else {
1980 debug_assert!(
1981 false,
1982 "set_current_focus_beyond_bounds_handler must be called inside a component build"
1983 );
1984 }
1985 }
1986
1987 pub(crate) fn set_current_focus_reveal_handler(
1988 &mut self,
1989 handler: CallbackWith<FocusRevealRequest, bool>,
1990 ) {
1991 if let Some(current) = self.component_tree.current_node_mut() {
1992 current.focus_reveal_handler = Some(handler);
1993 } else {
1994 debug_assert!(
1995 false,
1996 "set_current_focus_reveal_handler must be called inside a component build"
1997 );
1998 }
1999 }
2000
2001 pub(crate) fn set_current_focus_restorer_fallback(&mut self, fallback: FocusRequester) {
2002 if let Some(current) = self.component_tree.current_node_mut() {
2003 if current
2004 .focus_registration
2005 .is_some_and(|registration| registration.kind == FocusRegistrationKind::Scope)
2006 {
2007 current.focus_restorer_fallback = Some(fallback);
2008 } else {
2009 debug_assert!(
2010 false,
2011 "set_current_focus_restorer_fallback requires focus_scope or focus_restorer first"
2012 );
2013 }
2014 } else {
2015 debug_assert!(
2016 false,
2017 "set_current_focus_restorer_fallback must be called inside a component build"
2018 );
2019 }
2020 }
2021
2022 pub(crate) fn finalize_current_layout_policy_dirty(&mut self) {
2023 if let Some(node) = self.component_tree.current_node() {
2024 record_layout_policy_dirty(
2025 node.instance_key,
2026 node.layout_policy.as_ref(),
2027 &node.modifier,
2028 );
2029 } else {
2030 debug_assert!(
2031 false,
2032 "finalize_current_layout_policy_dirty must be called inside a component build"
2033 );
2034 }
2035 }
2036}
2037
2038pub struct NodeContextGuard {
2041 popped: bool,
2042 instance_logic_id_popped: bool,
2043 #[cfg(feature = "profiling")]
2044 profiling_guard: Option<crate::profiler::ScopeGuard>,
2045}
2046
2047pub struct CurrentComponentInstanceGuard {
2049 popped: bool,
2050}
2051
2052#[derive(Copy, Clone, Debug, Eq, PartialEq)]
2054pub enum RuntimePhase {
2055 Build,
2057 Measure,
2059 Input,
2061}
2062
2063pub struct PhaseGuard {
2065 popped: bool,
2066}
2067
2068impl PhaseGuard {
2069 pub fn pop(mut self) {
2071 if !self.popped {
2072 pop_phase();
2073 self.popped = true;
2074 }
2075 }
2076}
2077
2078impl Drop for PhaseGuard {
2079 fn drop(&mut self) {
2080 if !self.popped {
2081 pop_phase();
2082 self.popped = true;
2083 }
2084 }
2085}
2086
2087impl NodeContextGuard {
2088 pub fn pop(mut self) {
2090 if !self.popped {
2091 pop_current_node();
2092 self.popped = true;
2093 }
2094 if !self.instance_logic_id_popped {
2095 pop_instance_logic_id();
2096 self.instance_logic_id_popped = true;
2097 }
2098 }
2099}
2100
2101impl Drop for NodeContextGuard {
2102 fn drop(&mut self) {
2103 #[cfg(feature = "profiling")]
2104 {
2105 let _ = self.profiling_guard.take();
2106 }
2107 if !self.popped {
2108 pop_current_node();
2109 self.popped = true;
2110 }
2111 if !self.instance_logic_id_popped {
2112 pop_instance_logic_id();
2113 self.instance_logic_id_popped = true;
2114 }
2115 }
2116}
2117
2118impl CurrentComponentInstanceGuard {
2119 pub fn pop(mut self) {
2122 if !self.popped {
2123 pop_current_component_instance_key();
2124 self.popped = true;
2125 }
2126 }
2127}
2128
2129impl Drop for CurrentComponentInstanceGuard {
2130 fn drop(&mut self) {
2131 if !self.popped {
2132 pop_current_component_instance_key();
2133 self.popped = true;
2134 }
2135 }
2136}
2137
2138pub fn push_current_node(
2140 node_id: NodeId,
2141 component_type_id: u64,
2142 fn_name: &str,
2143) -> NodeContextGuard {
2144 #[cfg(not(feature = "profiling"))]
2145 let _ = fn_name;
2146 #[allow(unused_variables)]
2147 let parent_node_id = with_execution_context_mut(|context| {
2148 let parent = context.node_context_stack.last().copied();
2149 context.node_context_stack.push(node_id);
2150 parent
2151 });
2152
2153 let parent_call_index = next_child_instance_call_index();
2157 let parent_instance_logic_id = with_execution_context(|context| {
2158 context.instance_logic_id_stack.last().copied().unwrap_or(0)
2159 });
2160
2161 let group_path_hash = current_group_path_hash();
2162 let has_group_path = with_execution_context(|context| !context.group_path_stack.is_empty());
2163
2164 let instance_salt = if let Some(key_hash) = current_instance_key_override() {
2173 hash_components(&[&key_hash, &group_path_hash, &parent_call_index])
2174 } else if has_group_path {
2175 hash_components(&[&group_path_hash, &parent_call_index])
2176 } else {
2177 parent_call_index
2178 };
2179
2180 let instance_logic_id =
2181 if let Some(instance_logic_id_override) = take_next_node_instance_logic_id_override() {
2182 instance_logic_id_override
2183 } else if parent_call_index == 0
2184 && parent_instance_logic_id == 0
2185 && current_instance_key_override().is_none()
2186 && !has_group_path
2187 {
2188 component_type_id
2189 } else {
2190 hash_components(&[
2191 &component_type_id,
2192 &parent_instance_logic_id,
2193 &instance_salt,
2194 ])
2195 };
2196
2197 with_execution_context_mut(|context| {
2198 context.instance_logic_id_stack.push(instance_logic_id);
2199 });
2200
2201 push_order_frame();
2202
2203 #[cfg(feature = "profiling")]
2204 let profiling_guard = match current_phase() {
2205 Some(RuntimePhase::Build) => {
2206 crate::profiler::make_build_scope_guard(node_id, parent_node_id, fn_name)
2207 }
2208 _ => None,
2209 };
2210
2211 NodeContextGuard {
2212 popped: false,
2213 instance_logic_id_popped: false,
2214 #[cfg(feature = "profiling")]
2215 profiling_guard,
2216 }
2217}
2218
2219pub fn push_current_node_with_instance_logic_id(
2225 node_id: NodeId,
2226 instance_logic_id: u64,
2227 fn_name: &str,
2228) -> NodeContextGuard {
2229 #[cfg(not(feature = "profiling"))]
2230 let _ = fn_name;
2231 #[allow(unused_variables)]
2232 let parent_node_id = with_execution_context_mut(|context| {
2233 let parent = context.node_context_stack.last().copied();
2234 context.node_context_stack.push(node_id);
2235 parent
2236 });
2237
2238 let _ = next_child_instance_call_index();
2239
2240 with_execution_context_mut(|context| {
2241 context.instance_logic_id_stack.push(instance_logic_id);
2242 });
2243 push_order_frame();
2244
2245 #[cfg(feature = "profiling")]
2246 let profiling_guard = match current_phase() {
2247 Some(RuntimePhase::Build) => {
2248 crate::profiler::make_build_scope_guard(node_id, parent_node_id, fn_name)
2249 }
2250 _ => None,
2251 };
2252
2253 NodeContextGuard {
2254 popped: false,
2255 instance_logic_id_popped: false,
2256 #[cfg(feature = "profiling")]
2257 profiling_guard,
2258 }
2259}
2260
2261pub fn push_current_component_instance_key(instance_key: u64) -> CurrentComponentInstanceGuard {
2263 with_execution_context_mut(|context| {
2264 context.current_component_instance_stack.push(instance_key);
2265 });
2266 CurrentComponentInstanceGuard { popped: false }
2267}
2268
2269fn pop_current_component_instance_key() {
2270 with_execution_context_mut(|context| {
2271 let popped = context.current_component_instance_stack.pop();
2272 debug_assert!(
2273 popped.is_some(),
2274 "Attempted to pop current component instance key from an empty stack"
2275 );
2276 });
2277}
2278
2279fn pop_current_node() {
2280 with_execution_context_mut(|context| {
2281 let popped = context.node_context_stack.pop();
2282 debug_assert!(
2283 popped.is_some(),
2284 "Attempted to pop current node from an empty stack"
2285 );
2286 });
2287 pop_order_frame("ORDER_FRAME_STACK underflow: attempted to pop from empty stack");
2288}
2289
2290pub fn current_node_id() -> Option<NodeId> {
2292 with_execution_context(|context| context.node_context_stack.last().copied())
2293}
2294
2295fn current_instance_logic_id_opt() -> Option<u64> {
2296 with_execution_context(|context| context.instance_logic_id_stack.last().copied())
2297}
2298
2299pub(crate) fn current_instance_logic_id() -> u64 {
2301 current_instance_logic_id_opt()
2302 .expect("current_instance_logic_id must be called inside a component")
2303}
2304
2305pub(crate) fn current_instance_key() -> u64 {
2307 let instance_logic_id = current_instance_logic_id_opt()
2308 .expect("current_instance_key must be called inside a component");
2309 let group_path_hash = current_group_path_hash();
2310 hash_components(&[&instance_logic_id, &group_path_hash])
2311}
2312
2313fn pop_instance_logic_id() {
2314 with_execution_context_mut(|context| {
2315 let _ = context.instance_logic_id_stack.pop();
2316 });
2317}
2318
2319pub fn push_phase(phase: RuntimePhase) -> PhaseGuard {
2321 with_execution_context_mut(|context| {
2322 context.phase_stack.push(phase);
2323 });
2324 PhaseGuard { popped: false }
2325}
2326
2327fn pop_phase() {
2328 with_execution_context_mut(|context| {
2329 let popped = context.phase_stack.pop();
2330 debug_assert!(
2331 popped.is_some(),
2332 "Attempted to pop execution phase from an empty stack"
2333 );
2334 });
2335}
2336
2337pub(crate) fn current_phase() -> Option<RuntimePhase> {
2338 with_execution_context(|context| context.phase_stack.last().copied())
2339}
2340
2341pub(crate) fn push_group_id(group_id: u64) {
2343 with_execution_context_mut(|context| {
2344 context.group_path_stack.push(group_id);
2345 });
2346}
2347
2348pub(crate) fn pop_group_id(expected_group_id: u64) {
2350 with_execution_context_mut(|context| {
2351 if let Some(popped) = context.group_path_stack.pop() {
2352 debug_assert_eq!(
2353 popped, expected_group_id,
2354 "Unbalanced GroupGuard stack: expected {}, got {}",
2355 expected_group_id, popped
2356 );
2357 } else {
2358 debug_assert!(false, "Attempted to pop GroupGuard from an empty stack");
2359 }
2360 });
2361}
2362
2363fn current_group_path() -> Vec<u64> {
2365 with_execution_context(|context| context.group_path_stack.clone())
2366}
2367
2368fn current_group_path_hash() -> u64 {
2369 with_execution_context(|context| hash_components(&[&context.group_path_stack[..]]))
2370}
2371
2372fn current_instance_key_override() -> Option<u64> {
2373 with_execution_context(|context| context.instance_key_stack.last().copied())
2374}
2375
2376pub struct GroupGuard {
2381 group_id: u64,
2382}
2383
2384impl GroupGuard {
2385 pub fn new(group_id: u64) -> Self {
2387 push_group_id(group_id);
2388 push_order_frame();
2389 Self { group_id }
2390 }
2391}
2392
2393impl Drop for GroupGuard {
2394 fn drop(&mut self) {
2395 pop_order_frame("ORDER_FRAME_STACK underflow: attempted to pop GroupGuard frame");
2396 pop_group_id(self.group_id);
2397 }
2398}
2399
2400pub struct PathGroupGuard {
2407 group_id: u64,
2408}
2409
2410impl PathGroupGuard {
2411 pub fn new(group_id: u64) -> Self {
2414 push_group_id(group_id);
2415 Self { group_id }
2416 }
2417}
2418
2419impl Drop for PathGroupGuard {
2420 fn drop(&mut self) {
2421 pop_group_id(self.group_id);
2422 }
2423}
2424
2425pub struct InstanceKeyGuard {
2427 key_hash: u64,
2428}
2429
2430impl InstanceKeyGuard {
2431 pub fn new(key_hash: u64) -> Self {
2433 with_execution_context_mut(|context| {
2434 context.instance_key_stack.push(key_hash);
2435 });
2436 Self { key_hash }
2437 }
2438}
2439
2440impl Drop for InstanceKeyGuard {
2441 fn drop(&mut self) {
2442 with_execution_context_mut(|context| {
2443 let popped = context.instance_key_stack.pop();
2444 debug_assert_eq!(
2445 popped,
2446 Some(self.key_hash),
2447 "Unbalanced InstanceKeyGuard stack"
2448 );
2449 });
2450 }
2451}
2452
2453fn hash_components<H: Hash + ?Sized>(parts: &[&H]) -> u64 {
2454 let mut hasher = rustc_hash::FxHasher::default();
2455 for part in parts {
2456 part.hash(&mut hasher);
2457 }
2458 hasher.finish()
2459}
2460
2461fn compute_slot_key<K: Hash>(key: &K) -> (u64, u64) {
2462 let instance_logic_id = current_instance_logic_id();
2463 let group_path_hash = current_group_path_hash();
2464 let key_hash = hash_components(&[key]);
2465
2466 let call_counter = next_order_counter(
2471 OrderCounterKind::Remember,
2472 "ORDER_FRAME_STACK is empty; remember must be called inside a component",
2473 );
2474
2475 let slot_hash = hash_components(&[&group_path_hash, &key_hash, &call_counter]);
2476 (instance_logic_id, slot_hash)
2477}
2478
2479fn compute_functor_slot_key<K: Hash>(key: &K) -> (u64, u64) {
2480 let instance_logic_id = current_instance_logic_id();
2481 let group_path_hash = current_group_path_hash();
2482 let key_hash = hash_components(&[key]);
2483
2484 let call_counter = next_order_counter(
2485 OrderCounterKind::Functor,
2486 "ORDER_FRAME_STACK is empty; callback constructors must be called inside a component",
2487 );
2488
2489 let slot_hash = hash_components(&[&group_path_hash, &key_hash, &call_counter]);
2490 (instance_logic_id, slot_hash)
2491}
2492
2493pub(crate) fn ensure_build_phase() {
2494 match current_phase() {
2495 Some(RuntimePhase::Build) => {}
2496 Some(RuntimePhase::Measure) => {
2497 panic!("remember must not be called inside measure; move state to component render")
2498 }
2499 Some(RuntimePhase::Input) => {
2500 panic!(
2501 "remember must not be called inside typed input handlers; move state to component render"
2502 )
2503 }
2504 None => panic!(
2505 "remember must be called inside a tessera component. Ensure you're calling this from within a function annotated with #[tessera]."
2506 ),
2507 }
2508}
2509
2510fn remember_functor_cell_with_key<K, T, F>(key: K, init: F) -> (Arc<T>, FunctorHandle)
2511where
2512 K: Hash,
2513 T: Send + Sync + 'static,
2514 F: FnOnce() -> T,
2515{
2516 ensure_build_phase();
2517 let (instance_logic_id, slot_hash) = compute_functor_slot_key(&key);
2518 let slot_key = SlotKey {
2519 instance_logic_id,
2520 slot_hash,
2521 type_id: TypeId::of::<T>(),
2522 };
2523
2524 with_slot_table_mut(|table| {
2525 let mut init_opt = Some(init);
2526 if let Some(slot) = table.try_fast_slot_lookup(slot_key) {
2527 let epoch = table.epoch;
2528 let (generation, value): (u64, Arc<dyn Any + Send + Sync>) = {
2529 let entry = table
2530 .entries
2531 .get_mut(slot)
2532 .expect("functor slot entry should exist");
2533
2534 if entry.key.type_id != slot_key.type_id {
2535 panic!(
2536 "callback slot type mismatch: expected {}, found {:?}",
2537 std::any::type_name::<T>(),
2538 entry.key.type_id
2539 );
2540 }
2541
2542 entry.last_alive_epoch = epoch;
2543 if entry.value.is_none() {
2544 let init_fn = init_opt
2545 .take()
2546 .expect("callback slot init called more than once");
2547 entry.value = Some(Arc::new(init_fn()));
2548 entry.generation = entry.generation.wrapping_add(1);
2549 }
2550
2551 (
2552 entry.generation,
2553 entry
2554 .value
2555 .as_ref()
2556 .expect("callback slot must contain a value")
2557 .clone(),
2558 )
2559 };
2560
2561 (
2562 value
2563 .downcast::<T>()
2564 .unwrap_or_else(|_| panic!("callback slot {:?} downcast failed", slot)),
2565 FunctorHandle::new(slot, generation),
2566 )
2567 } else if let Some(slot) = table.key_to_slot.get(&slot_key).copied() {
2568 table.record_slot_usage_slow(instance_logic_id, slot);
2569 let epoch = table.epoch;
2570 let (generation, value): (u64, Arc<dyn Any + Send + Sync>) = {
2571 let entry = table
2572 .entries
2573 .get_mut(slot)
2574 .expect("functor slot entry should exist");
2575
2576 if entry.key.type_id != slot_key.type_id {
2577 panic!(
2578 "callback slot type mismatch: expected {}, found {:?}",
2579 std::any::type_name::<T>(),
2580 entry.key.type_id
2581 );
2582 }
2583
2584 entry.last_alive_epoch = epoch;
2585 if entry.value.is_none() {
2586 let init_fn = init_opt
2587 .take()
2588 .expect("callback slot init called more than once");
2589 entry.value = Some(Arc::new(init_fn()));
2590 entry.generation = entry.generation.wrapping_add(1);
2591 }
2592
2593 (
2594 entry.generation,
2595 entry
2596 .value
2597 .as_ref()
2598 .expect("callback slot must contain a value")
2599 .clone(),
2600 )
2601 };
2602
2603 (
2604 value
2605 .downcast::<T>()
2606 .unwrap_or_else(|_| panic!("callback slot {:?} downcast failed", slot)),
2607 FunctorHandle::new(slot, generation),
2608 )
2609 } else {
2610 let epoch = table.epoch;
2611 let init_fn = init_opt
2612 .take()
2613 .expect("callback slot init called more than once");
2614 let generation = 1u64;
2615 let slot = table.entries.insert(SlotEntry {
2616 key: slot_key,
2617 generation,
2618 value: Some(Arc::new(init_fn())),
2619 last_alive_epoch: epoch,
2620 retained: false,
2621 });
2622
2623 table.key_to_slot.insert(slot_key, slot);
2624 table.record_slot_usage_slow(instance_logic_id, slot);
2625
2626 let value = table
2627 .entries
2628 .get(slot)
2629 .expect("functor slot entry should exist")
2630 .value
2631 .as_ref()
2632 .expect("callback slot must contain a value")
2633 .clone()
2634 .downcast::<T>()
2635 .unwrap_or_else(|_| panic!("callback slot {:?} downcast failed", slot));
2636
2637 (value, FunctorHandle::new(slot, generation))
2638 }
2639 })
2640}
2641
2642fn load_functor_cell<T>(handle: FunctorHandle) -> Arc<T>
2643where
2644 T: Send + Sync + 'static,
2645{
2646 with_slot_table(|table| {
2647 let entry = table
2648 .entries
2649 .get(handle.slot)
2650 .unwrap_or_else(|| panic!("Callback points to freed slot: {:?}", handle.slot));
2651
2652 if entry.generation != handle.generation {
2653 panic!(
2654 "Callback is stale (slot {:?}, generation {}, current generation {})",
2655 handle.slot, handle.generation, entry.generation
2656 );
2657 }
2658
2659 if entry.key.type_id != TypeId::of::<T>() {
2660 panic!(
2661 "Callback type mismatch for slot {:?}: expected {}, stored {:?}",
2662 handle.slot,
2663 std::any::type_name::<T>(),
2664 entry.key.type_id
2665 );
2666 }
2667
2668 entry
2669 .value
2670 .as_ref()
2671 .unwrap_or_else(|| panic!("Callback slot {:?} has been cleared", handle.slot))
2672 .clone()
2673 .downcast::<T>()
2674 .unwrap_or_else(|_| panic!("Callback slot {:?} downcast failed", handle.slot))
2675 })
2676}
2677
2678pub(crate) fn remember_callback_handle<F>(handler: F) -> FunctorHandle
2679where
2680 F: Fn() + Send + Sync + 'static,
2681{
2682 let handler = Arc::new(handler) as Arc<dyn Fn() + Send + Sync>;
2683 let (cell, handle) = remember_functor_cell_with_key((), {
2684 let handler = Arc::clone(&handler);
2685 move || CallbackCell::new(handler)
2686 });
2687 cell.update(handler);
2688 handle
2689}
2690
2691pub(crate) fn invoke_callback_handle(handle: FunctorHandle) {
2692 let callback = load_functor_cell::<CallbackCell>(handle).shared();
2693 callback();
2694}
2695
2696pub(crate) fn remember_render_slot_handle<F>(render: F) -> FunctorHandle
2697where
2698 F: Fn() + Send + Sync + 'static,
2699{
2700 let render = Arc::new(render) as Arc<dyn Fn() + Send + Sync>;
2701 let creator_instance_key = current_replay_boundary_instance_key_from_scope()
2702 .unwrap_or_else(|| panic!("RenderSlot handles must be created during a component build"));
2703 let (cell, handle) = remember_functor_cell_with_key((), {
2704 let render = Arc::clone(&render);
2705 move || RenderSlotCell::new(render)
2706 });
2707 cell.update(render);
2708 for instance_key in render_slot_read_subscribers(handle) {
2709 if instance_key != creator_instance_key && !is_instance_key_build_dirty(instance_key) {
2710 record_replay_boundary_invalidation_for_instance_key(instance_key);
2711 }
2712 }
2713 handle
2714}
2715
2716pub(crate) fn invoke_render_slot_handle(handle: FunctorHandle) {
2717 let render = load_functor_cell::<RenderSlotCell>(handle).shared();
2718 render();
2719}
2720
2721pub(crate) fn remember_render_slot_with_handle<T, F>(render: F) -> FunctorHandle
2722where
2723 T: 'static,
2724 F: Fn(T) + Send + Sync + 'static,
2725{
2726 let render = Arc::new(render) as Arc<dyn Fn(T) + Send + Sync>;
2727 let creator_instance_key =
2728 current_replay_boundary_instance_key_from_scope().unwrap_or_else(|| {
2729 panic!("RenderSlotWith handles must be created during a component build")
2730 });
2731 let (cell, handle) = remember_functor_cell_with_key((), {
2732 let render = Arc::clone(&render);
2733 move || RenderSlotWithCell::new(render)
2734 });
2735 cell.update(render);
2736 for instance_key in render_slot_read_subscribers(handle) {
2737 if instance_key != creator_instance_key && !is_instance_key_build_dirty(instance_key) {
2738 record_replay_boundary_invalidation_for_instance_key(instance_key);
2739 }
2740 }
2741 handle
2742}
2743
2744pub(crate) fn invoke_render_slot_with_handle<T>(handle: FunctorHandle, value: T)
2745where
2746 T: 'static,
2747{
2748 let render = load_functor_cell::<RenderSlotWithCell<T>>(handle).shared();
2749 render(value)
2750}
2751
2752pub(crate) fn remember_callback_with_handle<T, R, F>(handler: F) -> FunctorHandle
2753where
2754 T: 'static,
2755 R: 'static,
2756 F: Fn(T) -> R + Send + Sync + 'static,
2757{
2758 let handler = Arc::new(handler) as Arc<dyn Fn(T) -> R + Send + Sync>;
2759 let (cell, handle) = remember_functor_cell_with_key((), {
2760 let handler = Arc::clone(&handler);
2761 move || CallbackWithCell::new(handler)
2762 });
2763 cell.update(handler);
2764 handle
2765}
2766
2767pub(crate) fn invoke_callback_with_handle<T, R>(handle: FunctorHandle, value: T) -> R
2768where
2769 T: 'static,
2770 R: 'static,
2771{
2772 let callback = load_functor_cell::<CallbackWithCell<T, R>>(handle).shared();
2773 callback(value)
2774}
2775
2776pub fn begin_recompose_slot_epoch() {
2778 with_slot_table_mut(SlotTable::begin_epoch);
2779}
2780
2781pub fn reset_slots() {
2783 with_slot_table_mut(SlotTable::reset);
2784}
2785
2786pub(crate) fn recycle_recomposed_slots_for_instance_logic_ids(instance_logic_ids: &HashSet<u64>) {
2787 if instance_logic_ids.is_empty() {
2788 return;
2789 }
2790
2791 with_slot_table_mut(|table| {
2792 let epoch = table.epoch;
2793 let mut freed: Vec<(SlotHandle, SlotKey)> = Vec::new();
2794
2795 for (slot, entry) in table.entries.iter() {
2796 if !instance_logic_ids.contains(&entry.key.instance_logic_id) {
2797 continue;
2798 }
2799 if entry.last_alive_epoch == epoch || entry.retained {
2800 continue;
2801 }
2802 freed.push((slot, entry.key));
2803 }
2804
2805 for (slot, key) in freed {
2806 table.entries.remove(slot);
2807 table.key_to_slot.remove(&key);
2808 }
2809 });
2810}
2811
2812pub(crate) fn live_slot_instance_logic_ids() -> HashSet<u64> {
2813 with_slot_table(|table| {
2814 table
2815 .entries
2816 .iter()
2817 .map(|(_, entry)| entry.key.instance_logic_id)
2818 .collect()
2819 })
2820}
2821
2822pub fn remember_with_key<K, F, T>(key: K, init: F) -> State<T>
2850where
2851 K: Hash,
2852 F: FnOnce() -> T,
2853 T: Send + Sync + 'static,
2854{
2855 ensure_build_phase();
2856 let (instance_logic_id, slot_hash) = compute_slot_key(&key);
2857 let type_id = TypeId::of::<T>();
2858 let slot_key = SlotKey {
2859 instance_logic_id,
2860 slot_hash,
2861 type_id,
2862 };
2863
2864 with_slot_table_mut(|table| {
2865 let mut init_opt = Some(init);
2866 if let Some(slot) = table.try_fast_slot_lookup(slot_key) {
2867 let epoch = table.epoch;
2868 let generation = {
2869 let entry = table
2870 .entries
2871 .get_mut(slot)
2872 .expect("slot entry should exist");
2873
2874 if entry.key.type_id != slot_key.type_id {
2875 panic!(
2876 "remember_with_key type mismatch: expected {}, found {:?}",
2877 std::any::type_name::<T>(),
2878 entry.key.type_id
2879 );
2880 }
2881
2882 entry.last_alive_epoch = epoch;
2883 if entry.value.is_none() {
2884 let init_fn = init_opt
2885 .take()
2886 .expect("remember_with_key init called more than once");
2887 entry.value = Some(Arc::new(RwLock::new(init_fn())));
2888 entry.generation = entry.generation.wrapping_add(1);
2889 }
2890 entry.generation
2891 };
2892
2893 State::new(slot, generation)
2894 } else if let Some(slot) = table.key_to_slot.get(&slot_key).copied() {
2895 table.record_slot_usage_slow(instance_logic_id, slot);
2896 let epoch = table.epoch;
2897 let generation = {
2898 let entry = table
2899 .entries
2900 .get_mut(slot)
2901 .expect("slot entry should exist");
2902
2903 if entry.key.type_id != slot_key.type_id {
2904 panic!(
2905 "remember_with_key type mismatch: expected {}, found {:?}",
2906 std::any::type_name::<T>(),
2907 entry.key.type_id
2908 );
2909 }
2910
2911 entry.last_alive_epoch = epoch;
2912 if entry.value.is_none() {
2913 let init_fn = init_opt
2914 .take()
2915 .expect("remember_with_key init called more than once");
2916 entry.value = Some(Arc::new(RwLock::new(init_fn())));
2917 entry.generation = entry.generation.wrapping_add(1);
2918 }
2919 entry.generation
2920 };
2921
2922 State::new(slot, generation)
2923 } else {
2924 let epoch = table.epoch;
2925 let init_fn = init_opt
2926 .take()
2927 .expect("remember_with_key init called more than once");
2928 let generation = 1u64;
2929 let slot = table.entries.insert(SlotEntry {
2930 key: slot_key,
2931 generation,
2932 value: Some(Arc::new(RwLock::new(init_fn()))),
2933 last_alive_epoch: epoch,
2934 retained: false,
2935 });
2936
2937 table.key_to_slot.insert(slot_key, slot);
2938 table.record_slot_usage_slow(instance_logic_id, slot);
2939 State::new(slot, generation)
2940 }
2941 })
2942}
2943
2944pub fn remember<F, T>(init: F) -> State<T>
2971where
2972 F: FnOnce() -> T,
2973 T: Send + Sync + 'static,
2974{
2975 remember_with_key((), init)
2976}
2977
2978pub fn retain_with_key<K, F, T>(key: K, init: F) -> State<T>
3015where
3016 K: Hash,
3017 F: FnOnce() -> T,
3018 T: Send + Sync + 'static,
3019{
3020 ensure_build_phase();
3021 let (instance_logic_id, slot_hash) = compute_slot_key(&key);
3022 let type_id = TypeId::of::<T>();
3023 let slot_key = SlotKey {
3024 instance_logic_id,
3025 slot_hash,
3026 type_id,
3027 };
3028
3029 with_slot_table_mut(|table| {
3030 let mut init_opt = Some(init);
3031 if let Some(slot) = table.try_fast_slot_lookup(slot_key) {
3032 let epoch = table.epoch;
3033 let generation = {
3034 let entry = table
3035 .entries
3036 .get_mut(slot)
3037 .expect("slot entry should exist");
3038
3039 if entry.key.type_id != slot_key.type_id {
3040 panic!(
3041 "retain_with_key type mismatch: expected {}, found {:?}",
3042 std::any::type_name::<T>(),
3043 entry.key.type_id
3044 );
3045 }
3046
3047 entry.last_alive_epoch = epoch;
3048 entry.retained = true;
3049 if entry.value.is_none() {
3050 let init_fn = init_opt
3051 .take()
3052 .expect("retain_with_key init called more than once");
3053 entry.value = Some(Arc::new(RwLock::new(init_fn())));
3054 entry.generation = entry.generation.wrapping_add(1);
3055 }
3056
3057 entry.generation
3058 };
3059
3060 State::new(slot, generation)
3061 } else if let Some(slot) = table.key_to_slot.get(&slot_key).copied() {
3062 table.record_slot_usage_slow(instance_logic_id, slot);
3063 let epoch = table.epoch;
3064 let generation = {
3065 let entry = table
3066 .entries
3067 .get_mut(slot)
3068 .expect("slot entry should exist");
3069
3070 if entry.key.type_id != slot_key.type_id {
3071 panic!(
3072 "retain_with_key type mismatch: expected {}, found {:?}",
3073 std::any::type_name::<T>(),
3074 entry.key.type_id
3075 );
3076 }
3077
3078 entry.last_alive_epoch = epoch;
3079 entry.retained = true;
3080 if entry.value.is_none() {
3081 let init_fn = init_opt
3082 .take()
3083 .expect("retain_with_key init called more than once");
3084 entry.value = Some(Arc::new(RwLock::new(init_fn())));
3085 entry.generation = entry.generation.wrapping_add(1);
3086 }
3087
3088 entry.generation
3089 };
3090
3091 State::new(slot, generation)
3092 } else {
3093 let epoch = table.epoch;
3094 let init_fn = init_opt
3095 .take()
3096 .expect("retain_with_key init called more than once");
3097 let generation = 1u64;
3098 let slot = table.entries.insert(SlotEntry {
3099 key: slot_key,
3100 generation,
3101 value: Some(Arc::new(RwLock::new(init_fn()))),
3102 last_alive_epoch: epoch,
3103 retained: true,
3104 });
3105
3106 table.key_to_slot.insert(slot_key, slot);
3107 table.record_slot_usage_slow(instance_logic_id, slot);
3108 State::new(slot, generation)
3109 }
3110 })
3111}
3112
3113pub fn retain<F, T>(init: F) -> State<T>
3149where
3150 F: FnOnce() -> T,
3151 T: Send + Sync + 'static,
3152{
3153 retain_with_key((), init)
3154}
3155
3156pub fn key<K, F, R>(key: K, block: F) -> R
3176where
3177 K: Hash,
3178 F: FnOnce() -> R,
3179{
3180 let key_hash = hash_components(&[&key]);
3181 let _group_guard = GroupGuard::new(key_hash);
3182 let _instance_guard = InstanceKeyGuard::new(key_hash);
3183 block()
3184}
3185
3186#[cfg(test)]
3187mod tests {
3188 use std::sync::{
3189 Arc,
3190 atomic::{AtomicU64, AtomicUsize, Ordering},
3191 };
3192
3193 use super::*;
3194 use crate::execution_context::{
3195 reset_execution_context, with_execution_context, with_execution_context_mut,
3196 };
3197 use crate::layout::{LayoutPolicy, MeasureScope};
3198 use crate::modifier::{LayoutModifierChild, LayoutModifierInput, LayoutModifierNode};
3199 use crate::prop::{
3200 Callback, CallbackWith, ComponentReplayData, RenderSlot, make_component_runner,
3201 };
3202 use crate::tessera;
3203
3204 #[tessera(crate)]
3205 fn required_value_component(value: Option<usize>, on_value: Option<CallbackWith<usize>>) {
3206 let value = value.expect("missing required prop `value` in `required_value_component`");
3207 if let Some(on_value) = on_value {
3208 on_value.call(value);
3209 }
3210 }
3211
3212 #[tessera(crate)]
3213 fn optional_value_component(
3214 value: Option<usize>,
3215 on_value: Option<CallbackWith<Option<usize>>>,
3216 ) {
3217 if let Some(on_value) = on_value {
3218 on_value.call(value);
3219 }
3220 }
3221
3222 #[tessera(crate)]
3223 fn defaulted_value_component(value: Option<usize>, on_value: Option<CallbackWith<usize>>) {
3224 let value = value.unwrap_or(7);
3225 if let Some(on_value) = on_value {
3226 on_value.call(value);
3227 }
3228 }
3229
3230 fn panic_message(err: Box<dyn std::any::Any + Send>) -> String {
3231 match err.downcast::<String>() {
3232 Ok(message) => *message,
3233 Err(err) => match err.downcast::<&'static str>() {
3234 Ok(message) => (*message).to_string(),
3235 Err(_) => "<non-string panic>".to_string(),
3236 },
3237 }
3238 }
3239
3240 fn seed_previous_replay_snapshot(instance_key: u64) {
3241 let replay = ComponentReplayData::new(make_component_runner::<()>(|_| {}), &());
3242 with_component_replay_tracker_mut(|tracker| {
3243 tracker.previous_nodes.insert(
3244 instance_key,
3245 ReplayNodeSnapshot {
3246 instance_key,
3247 parent_instance_key: None,
3248 instance_logic_id: 0,
3249 group_path: Vec::new(),
3250 instance_key_override: None,
3251 fn_name: "test_component".to_string(),
3252 replay,
3253 },
3254 );
3255 });
3256 }
3257
3258 fn with_test_component_scope<R>(component_type_id: u64, f: impl FnOnce() -> R) -> R {
3259 reset_execution_context();
3260 let mut arena = crate::Arena::<()>::new();
3261 let node_id = arena.new_node(());
3262 let _phase_guard = push_phase(RuntimePhase::Build);
3263 let _node_guard = push_current_node(node_id, component_type_id, "test_component");
3264 let _instance_guard = push_current_component_instance_key(current_instance_key());
3265 f()
3266 }
3267
3268 #[test]
3269 fn frame_receiver_uses_component_scope_instance_key() {
3270 let _instance_guard = push_current_component_instance_key(7);
3271 assert_eq!(current_replay_boundary_instance_key_from_scope(), Some(7));
3272 }
3273
3274 #[test]
3275 fn receive_frame_nanos_panics_without_component_scope() {
3276 reset_frame_clock();
3277 begin_frame_clock(Instant::now());
3278
3279 let result = std::panic::catch_unwind(|| {
3280 receive_frame_nanos(|_| FrameNanosControl::Continue);
3281 });
3282 assert!(result.is_err());
3283 }
3284
3285 #[test]
3286 fn receive_frame_nanos_panics_in_input_phase() {
3287 let _phase_guard = push_phase(RuntimePhase::Input);
3288 let result = std::panic::catch_unwind(|| {
3289 receive_frame_nanos(|_| FrameNanosControl::Continue);
3290 });
3291 assert!(result.is_err());
3292 }
3293
3294 #[test]
3295 fn tick_frame_nanos_receivers_removes_stopped_receivers() {
3296 reset_frame_clock();
3297 begin_frame_clock(Instant::now());
3298
3299 with_frame_clock_tracker_mut(|tracker| {
3300 tracker.receivers.insert(
3301 FrameNanosReceiverKey {
3302 instance_logic_id: 1,
3303 receiver_hash: 1,
3304 },
3305 FrameNanosReceiver {
3306 owner_instance_key: 123,
3307 callback: Box::new(|_| FrameNanosControl::Stop),
3308 },
3309 );
3310 });
3311
3312 tick_frame_nanos_receivers();
3313 assert!(with_frame_clock_tracker(|tracker| tracker
3314 .receivers
3315 .is_empty()));
3316 }
3317
3318 #[test]
3319 fn with_build_dirty_instance_keys_marks_current_scope() {
3320 let mut outer = HashSet::default();
3321 outer.insert(7);
3322
3323 assert!(!is_instance_key_build_dirty(7));
3324 with_build_dirty_instance_keys(&outer, || {
3325 assert!(is_instance_key_build_dirty(7));
3326 assert!(!is_instance_key_build_dirty(8));
3327
3328 let mut inner = HashSet::default();
3329 inner.insert(8);
3330 with_build_dirty_instance_keys(&inner, || {
3331 assert!(!is_instance_key_build_dirty(7));
3332 assert!(is_instance_key_build_dirty(8));
3333 });
3334
3335 assert!(is_instance_key_build_dirty(7));
3336 assert!(!is_instance_key_build_dirty(8));
3337 });
3338 assert!(!is_instance_key_build_dirty(7));
3339 }
3340
3341 #[test]
3342 fn with_build_dirty_instance_keys_restores_on_panic() {
3343 let mut dirty = HashSet::default();
3344 dirty.insert(11);
3345
3346 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3347 with_build_dirty_instance_keys(&dirty, || {
3348 assert!(is_instance_key_build_dirty(11));
3349 panic!("expected panic");
3350 });
3351 }));
3352 assert!(result.is_err());
3353 assert!(!is_instance_key_build_dirty(11));
3354 }
3355
3356 #[derive(Clone, PartialEq)]
3357 struct DirtySplitPolicy {
3358 measure_key: u32,
3359 placement_key: u32,
3360 }
3361
3362 impl LayoutPolicy for DirtySplitPolicy {
3363 fn measure(
3364 &self,
3365 _input: &MeasureScope<'_>,
3366 ) -> Result<crate::LayoutResult, crate::MeasurementError> {
3367 Ok(crate::LayoutResult::new(crate::ComputedData::ZERO))
3368 }
3369
3370 fn measure_eq(&self, other: &Self) -> bool {
3371 self.measure_key == other.measure_key
3372 }
3373
3374 fn placement_eq(&self, other: &Self) -> bool {
3375 self.placement_key == other.placement_key
3376 }
3377 }
3378
3379 #[derive(Clone, Copy, PartialEq)]
3380 struct DirtyMeasureModifierNode {
3381 width: u32,
3382 }
3383
3384 impl LayoutModifierNode for DirtyMeasureModifierNode {
3385 fn measure(
3386 &self,
3387 _input: &LayoutModifierInput<'_>,
3388 child: &mut dyn LayoutModifierChild,
3389 ) -> Result<crate::LayoutModifierOutput, crate::MeasurementError> {
3390 let size = child.measure(&crate::Constraint::exact(
3391 crate::Px::new(self.width as i32),
3392 crate::Px::new(10),
3393 ))?;
3394 child.place(crate::PxPosition::ZERO);
3395 Ok(crate::LayoutModifierOutput { size })
3396 }
3397 }
3398
3399 #[test]
3400 fn layout_dirty_tracking_separates_measure_and_placement_changes() {
3401 reset_layout_dirty_tracking();
3402
3403 begin_frame_layout_dirty_tracking();
3404 {
3405 let _phase_guard = push_phase(RuntimePhase::Build);
3406 record_layout_policy_dirty(
3407 1,
3408 &DirtySplitPolicy {
3409 measure_key: 0,
3410 placement_key: 0,
3411 },
3412 &Modifier::new(),
3413 );
3414 }
3415 finalize_frame_layout_dirty_tracking();
3416 let dirty = take_layout_dirty_nodes();
3417 assert!(dirty.measure_self_nodes.contains(&1));
3418 assert!(dirty.placement_self_nodes.is_empty());
3419
3420 begin_frame_layout_dirty_tracking();
3421 {
3422 let _phase_guard = push_phase(RuntimePhase::Build);
3423 record_layout_policy_dirty(
3424 1,
3425 &DirtySplitPolicy {
3426 measure_key: 0,
3427 placement_key: 1,
3428 },
3429 &Modifier::new(),
3430 );
3431 }
3432 finalize_frame_layout_dirty_tracking();
3433 let dirty = take_layout_dirty_nodes();
3434 assert!(!dirty.measure_self_nodes.contains(&1));
3435 assert!(dirty.placement_self_nodes.contains(&1));
3436
3437 begin_frame_layout_dirty_tracking();
3438 {
3439 let _phase_guard = push_phase(RuntimePhase::Build);
3440 record_layout_policy_dirty(
3441 1,
3442 &DirtySplitPolicy {
3443 measure_key: 1,
3444 placement_key: 1,
3445 },
3446 &Modifier::new(),
3447 );
3448 }
3449 finalize_frame_layout_dirty_tracking();
3450 let dirty = take_layout_dirty_nodes();
3451 assert!(dirty.measure_self_nodes.contains(&1));
3452 assert!(!dirty.placement_self_nodes.contains(&1));
3453 }
3454
3455 #[test]
3456 fn layout_dirty_tracking_marks_measure_when_layout_modifier_changes() {
3457 reset_layout_dirty_tracking();
3458
3459 begin_frame_layout_dirty_tracking();
3460 {
3461 let _phase_guard = push_phase(RuntimePhase::Build);
3462 let modifier = Modifier::new().push_layout(DirtyMeasureModifierNode { width: 10 });
3463 record_layout_policy_dirty(
3464 1,
3465 &DirtySplitPolicy {
3466 measure_key: 0,
3467 placement_key: 0,
3468 },
3469 &modifier,
3470 );
3471 }
3472 finalize_frame_layout_dirty_tracking();
3473 let _ = take_layout_dirty_nodes();
3474
3475 begin_frame_layout_dirty_tracking();
3476 {
3477 let _phase_guard = push_phase(RuntimePhase::Build);
3478 let modifier = Modifier::new().push_layout(DirtyMeasureModifierNode { width: 20 });
3479 record_layout_policy_dirty(
3480 1,
3481 &DirtySplitPolicy {
3482 measure_key: 0,
3483 placement_key: 0,
3484 },
3485 &modifier,
3486 );
3487 }
3488 finalize_frame_layout_dirty_tracking();
3489 let dirty = take_layout_dirty_nodes();
3490 assert!(dirty.measure_self_nodes.contains(&1));
3491 assert!(!dirty.placement_self_nodes.contains(&1));
3492 }
3493
3494 #[test]
3495 fn with_replay_scope_restores_group_path_and_override() {
3496 with_execution_context_mut(|context| {
3497 context.group_path_stack = vec![1, 2, 3];
3498 });
3499 with_execution_context_mut(|context| {
3500 context.instance_key_stack = vec![5];
3501 });
3502 with_execution_context_mut(|context| {
3503 context.next_node_instance_logic_id_override = Some(9);
3504 });
3505
3506 with_replay_scope(42, &[7, 8], Some(11), || {
3507 assert_eq!(current_group_path(), vec![7, 8]);
3508 assert_eq!(current_instance_key_override(), Some(11));
3509 assert_eq!(take_next_node_instance_logic_id_override(), Some(42));
3510 assert_eq!(take_next_node_instance_logic_id_override(), None);
3511 });
3512
3513 assert_eq!(current_group_path(), vec![1, 2, 3]);
3514 assert_eq!(current_instance_key_override(), Some(5));
3515 let restored_override =
3516 with_execution_context(|context| context.next_node_instance_logic_id_override);
3517 assert_eq!(restored_override, Some(9));
3518 }
3519
3520 #[test]
3521 fn with_replay_scope_restores_on_panic() {
3522 with_execution_context_mut(|context| {
3523 context.group_path_stack = vec![5];
3524 });
3525 with_execution_context_mut(|context| {
3526 context.instance_key_stack = vec![13];
3527 });
3528 with_execution_context_mut(|context| {
3529 context.next_node_instance_logic_id_override = None;
3530 });
3531
3532 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3533 with_replay_scope(77, &[10], Some(17), || {
3534 assert_eq!(current_group_path(), vec![10]);
3535 assert_eq!(current_instance_key_override(), Some(17));
3536 panic!("expected panic");
3537 });
3538 }));
3539 assert!(result.is_err());
3540
3541 assert_eq!(current_group_path(), vec![5]);
3542 assert_eq!(current_instance_key_override(), Some(13));
3543 let restored_override =
3544 with_execution_context(|context| context.next_node_instance_logic_id_override);
3545 assert_eq!(restored_override, None);
3546 }
3547
3548 #[test]
3549 fn group_local_remember_does_not_shift_following_slots() {
3550 reset_slots();
3551
3552 begin_recompose_slot_epoch();
3553 with_test_component_scope(1001, || {
3554 let stable_state = remember(|| 1usize);
3555 stable_state.set(41);
3556 });
3557
3558 begin_recompose_slot_epoch();
3559 with_test_component_scope(1001, || {
3560 {
3561 let _group_guard = GroupGuard::new(7);
3562 let _branch_state = remember(|| 10usize);
3563 }
3564 let stable_state = remember(|| 1usize);
3565 assert_eq!(stable_state.get(), 41);
3566 });
3567 }
3568
3569 #[test]
3570 fn conditional_frame_receiver_does_not_shift_following_remember_slots() {
3571 reset_slots();
3572 reset_frame_clock();
3573 begin_frame_clock(Instant::now());
3574
3575 begin_recompose_slot_epoch();
3576 with_test_component_scope(1002, || {
3577 let stable_state = remember(|| 1usize);
3578 stable_state.set(99);
3579 });
3580
3581 begin_recompose_slot_epoch();
3582 with_test_component_scope(1002, || {
3583 {
3584 let _group_guard = GroupGuard::new(9);
3585 receive_frame_nanos(|_| FrameNanosControl::Stop);
3586 }
3587 let stable_state = remember(|| 1usize);
3588 assert_eq!(stable_state.get(), 99);
3589 });
3590 }
3591
3592 #[test]
3593 fn callback_handle_stays_stable_and_invokes_latest_closure() {
3594 reset_slots();
3595
3596 let calls = Arc::new(AtomicUsize::new(0));
3597
3598 begin_recompose_slot_epoch();
3599 let first = with_test_component_scope(11001, || {
3600 let calls = Arc::clone(&calls);
3601 Callback::new(move || {
3602 calls.store(1, Ordering::SeqCst);
3603 })
3604 });
3605
3606 begin_recompose_slot_epoch();
3607 let second = with_test_component_scope(11001, || {
3608 let calls = Arc::clone(&calls);
3609 Callback::new(move || {
3610 calls.store(2, Ordering::SeqCst);
3611 })
3612 });
3613
3614 assert!(first == second);
3615 first.call();
3616 assert_eq!(calls.load(Ordering::SeqCst), 2);
3617 }
3618
3619 #[test]
3620 fn tessera_panics_when_required_prop_is_missing() {
3621 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3622 with_test_component_scope(11013, || {
3623 required_value_component();
3624 });
3625 }));
3626
3627 let message = panic_message(result.expect_err("missing required prop should panic"));
3628 assert!(message.contains("missing required prop `value`"));
3629 assert!(message.contains("required_value_component"));
3630 }
3631
3632 #[test]
3633 fn tessera_uses_none_for_omitted_option_props() {
3634 let observed = Arc::new(AtomicUsize::new(99));
3635
3636 with_test_component_scope(11014, || {
3637 let observed = Arc::clone(&observed);
3638 optional_value_component().on_value(move |value: Option<usize>| {
3639 observed.store(
3640 value.map_or(0, |next| next.saturating_add(1)),
3641 Ordering::SeqCst,
3642 );
3643 });
3644 });
3645
3646 assert_eq!(observed.load(Ordering::SeqCst), 0);
3647 }
3648
3649 #[test]
3650 fn tessera_uses_function_body_default_for_omitted_option_props() {
3651 let observed = Arc::new(AtomicUsize::new(0));
3652
3653 with_test_component_scope(11015, || {
3654 let observed = Arc::clone(&observed);
3655 defaulted_value_component().on_value(move |value: usize| {
3656 observed.store(value, Ordering::SeqCst);
3657 });
3658 });
3659
3660 assert_eq!(observed.load(Ordering::SeqCst), 7);
3661 }
3662
3663 #[test]
3664 fn tessera_accepts_non_default_required_props_when_provided() {
3665 let observed = Arc::new(AtomicUsize::new(0));
3666
3667 with_test_component_scope(11016, || {
3668 let observed = Arc::clone(&observed);
3669 required_value_component()
3670 .value(13)
3671 .on_value(move |value: usize| {
3672 observed.store(value, Ordering::SeqCst);
3673 });
3674 });
3675
3676 assert_eq!(observed.load(Ordering::SeqCst), 13);
3677 }
3678
3679 #[test]
3680 fn render_slot_update_invalidates_reader_instance() {
3681 reset_slots();
3682 reset_component_replay_tracking();
3683 reset_render_slot_read_dependencies();
3684 reset_build_invalidations();
3685
3686 let reader_boundary_key = Arc::new(AtomicU64::new(0));
3687
3688 begin_recompose_slot_epoch();
3689 let first = with_test_component_scope(11002, || {
3690 let reader_boundary_key = Arc::clone(&reader_boundary_key);
3691 RenderSlot::new(move || {
3692 let instance_key = current_replay_boundary_instance_key_from_scope()
3693 .expect("slot render must run inside replay boundary");
3694 reader_boundary_key.store(instance_key, Ordering::SeqCst);
3695 })
3696 });
3697
3698 with_test_component_scope(11003, || {
3699 first.render();
3700 });
3701 let reader_instance_key = reader_boundary_key.load(Ordering::SeqCst);
3702 assert!(reader_instance_key != 0);
3703 seed_previous_replay_snapshot(reader_instance_key);
3704
3705 assert!(!has_pending_build_invalidations());
3706
3707 begin_recompose_slot_epoch();
3708 let second = with_test_component_scope(11002, || RenderSlot::new(|| {}));
3709
3710 assert!(first == second);
3711 assert!(has_pending_build_invalidations());
3712
3713 let invalidations = take_build_invalidations();
3714 let mut expected = HashSet::default();
3715 expected.insert(reader_instance_key);
3716 assert_eq!(invalidations.dirty_instance_keys, expected);
3717 }
3718
3719 #[test]
3720 fn render_slot_update_skips_reader_already_dirty_in_current_build() {
3721 reset_slots();
3722 reset_component_replay_tracking();
3723 reset_render_slot_read_dependencies();
3724 reset_build_invalidations();
3725 reset_execution_context();
3726
3727 let reader_boundary_key = Arc::new(AtomicU64::new(0));
3728
3729 begin_recompose_slot_epoch();
3730 let first = with_test_component_scope(12002, || {
3731 let reader_boundary_key = Arc::clone(&reader_boundary_key);
3732 RenderSlot::new(move || {
3733 let instance_key = current_replay_boundary_instance_key_from_scope()
3734 .expect("slot render must run inside replay boundary");
3735 reader_boundary_key.store(instance_key, Ordering::SeqCst);
3736 })
3737 });
3738
3739 with_test_component_scope(12003, || {
3740 first.render();
3741 });
3742 let reader_instance_key = reader_boundary_key.load(Ordering::SeqCst);
3743 assert!(reader_instance_key != 0);
3744 seed_previous_replay_snapshot(reader_instance_key);
3745
3746 assert!(!has_pending_build_invalidations());
3747
3748 begin_recompose_slot_epoch();
3749 let mut dirty = HashSet::default();
3750 dirty.insert(reader_instance_key);
3751 with_build_dirty_instance_keys(&dirty, || {
3752 let mut arena = crate::Arena::<()>::new();
3753 let node_id = arena.new_node(());
3754 let _phase_guard = push_phase(RuntimePhase::Build);
3755 let _node_guard = push_current_node(node_id, 12002, "test_component");
3756 let _instance_guard = push_current_component_instance_key(current_instance_key());
3757 let second = RenderSlot::new(|| {});
3758 assert!(first == second);
3759 });
3760
3761 assert!(!has_pending_build_invalidations());
3762 }
3763
3764 #[test]
3765 fn group_local_child_identity_does_not_shift_following_siblings() {
3766 fn stable_child_instance_logic_id(with_group_child: bool) -> u64 {
3767 let mut arena = crate::Arena::<()>::new();
3768 let root_node = arena.new_node(());
3769 let stable_child_node = arena.new_node(());
3770 let group_child_node = arena.new_node(());
3771
3772 let _phase_guard = push_phase(RuntimePhase::Build);
3773 let _root_guard = push_current_node(root_node, 2001, "root_component");
3774 let _root_instance_guard = push_current_component_instance_key(current_instance_key());
3775
3776 if with_group_child {
3777 let _group_guard = GroupGuard::new(5);
3778 let _group_child_guard =
3779 push_current_node(group_child_node, 2002, "group_child_component");
3780 let _group_child_instance_guard =
3781 push_current_component_instance_key(current_instance_key());
3782 let _ = current_instance_logic_id();
3783 }
3784
3785 let _stable_child_guard =
3786 push_current_node(stable_child_node, 2003, "stable_child_component");
3787 current_instance_logic_id()
3788 }
3789
3790 assert_eq!(
3791 stable_child_instance_logic_id(false),
3792 stable_child_instance_logic_id(true)
3793 );
3794 }
3795
3796 #[test]
3797 fn child_components_in_different_groups_get_distinct_instance_logic_ids() {
3798 let mut arena = crate::Arena::<()>::new();
3799 let root_node = arena.new_node(());
3800 let first_child_node = arena.new_node(());
3801 let second_child_node = arena.new_node(());
3802
3803 let _phase_guard = push_phase(RuntimePhase::Build);
3804 let _root_guard = push_current_node(root_node, 3001, "root_component");
3805 let _root_instance_guard = push_current_component_instance_key(current_instance_key());
3806
3807 let first_id = {
3808 let _group_guard = GroupGuard::new(11);
3809 let _child_guard = push_current_node(first_child_node, 3002, "grouped_child");
3810 current_instance_logic_id()
3811 };
3812
3813 let second_id = {
3814 let _group_guard = GroupGuard::new(12);
3815 let _child_guard = push_current_node(second_child_node, 3002, "grouped_child");
3816 current_instance_logic_id()
3817 };
3818
3819 assert_ne!(first_id, second_id);
3820 }
3821
3822 #[test]
3823 fn child_components_in_repeated_path_groups_keep_distinct_instance_logic_ids() {
3824 let mut arena = crate::Arena::<()>::new();
3825 let root_node = arena.new_node(());
3826 let first_child_node = arena.new_node(());
3827 let second_child_node = arena.new_node(());
3828
3829 let _phase_guard = push_phase(RuntimePhase::Build);
3830 let _root_guard = push_current_node(root_node, 4001, "root_component");
3831 let _root_instance_guard = push_current_component_instance_key(current_instance_key());
3832
3833 let first_id = {
3834 let _group_guard = PathGroupGuard::new(21);
3835 let _child_guard = push_current_node(first_child_node, 4002, "loop_child");
3836 current_instance_logic_id()
3837 };
3838
3839 let second_id = {
3840 let _group_guard = PathGroupGuard::new(21);
3841 let _child_guard = push_current_node(second_child_node, 4002, "loop_child");
3842 current_instance_logic_id()
3843 };
3844
3845 assert_ne!(first_id, second_id);
3846 }
3847
3848 #[test]
3849 fn drop_slots_for_instance_logic_ids_keeps_retained_entries() {
3850 let mut table = SlotTable::default();
3851 let keep_key = SlotKey {
3852 instance_logic_id: 7,
3853 slot_hash: 11,
3854 type_id: TypeId::of::<i32>(),
3855 };
3856 let drop_key = SlotKey {
3857 instance_logic_id: 7,
3858 slot_hash: 12,
3859 type_id: TypeId::of::<i32>(),
3860 };
3861
3862 let keep_slot = table.entries.insert(SlotEntry {
3863 key: keep_key,
3864 generation: 1,
3865 value: Some(Arc::new(RwLock::new(10_i32))),
3866 last_alive_epoch: 0,
3867 retained: true,
3868 });
3869 let drop_slot = table.entries.insert(SlotEntry {
3870 key: drop_key,
3871 generation: 1,
3872 value: Some(Arc::new(RwLock::new(20_i32))),
3873 last_alive_epoch: 0,
3874 retained: false,
3875 });
3876 table.key_to_slot.insert(keep_key, keep_slot);
3877 table.key_to_slot.insert(drop_key, drop_slot);
3878 with_slot_table_mut(|slot_table| *slot_table = table);
3879
3880 let mut stale = HashSet::default();
3881 stale.insert(7_u64);
3882 drop_slots_for_instance_logic_ids(&stale);
3883
3884 with_slot_table(|table| {
3885 assert!(table.entries.get(keep_slot).is_some());
3886 assert!(table.key_to_slot.contains_key(&keep_key));
3887 assert!(table.entries.get(drop_slot).is_none());
3888 assert!(!table.key_to_slot.contains_key(&drop_key));
3889 });
3890 }
3891}