1use std::any::Any;
2use std::cell::RefCell;
3use std::collections::BTreeMap;
4use std::path::PathBuf;
5use std::rc::Rc;
6
7use crate::cycle::GcEdge;
8use crate::runtime::{IdCounter, IdExhausted, ScopeId, TaskLocalValue, Trace};
9use crate::Value;
10
11#[derive(Clone, Debug)]
12struct Scoped<T> {
13 id: Option<ScopeId>,
14 value: T,
15}
16
17impl<T> Scoped<T> {
18 fn inherited(value: T) -> Self {
19 Self { id: None, value }
20 }
21
22 fn owned(id: ScopeId, value: T) -> Self {
23 Self {
24 id: Some(id),
25 value,
26 }
27 }
28}
29
30#[derive(Clone)]
37pub(crate) struct DynamicStackEntryId(Rc<()>);
38
39impl DynamicStackEntryId {
40 fn fresh() -> Self {
41 Self(Rc::new(()))
42 }
43}
44
45impl std::fmt::Debug for DynamicStackEntryId {
46 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47 f.write_str("DynamicStackEntryId(..)")
48 }
49}
50
51impl PartialEq for DynamicStackEntryId {
52 fn eq(&self, other: &Self) -> bool {
53 Rc::ptr_eq(&self.0, &other.0)
54 }
55}
56
57impl Eq for DynamicStackEntryId {}
58
59#[derive(Clone, Debug, Default)]
64pub(crate) struct DynamicStackIdentities {
65 entries: BTreeMap<Value, Vec<DynamicStackEntryId>>,
66}
67
68impl DynamicStackIdentities {
69 pub(crate) fn from_stacks(stacks: &BTreeMap<Value, Vec<Value>>) -> Self {
70 Self {
71 entries: stacks
72 .iter()
73 .map(|(key, values)| {
74 (
75 key.clone(),
76 values
77 .iter()
78 .map(|_| DynamicStackEntryId::fresh())
79 .collect(),
80 )
81 })
82 .collect(),
83 }
84 }
85
86 pub(crate) fn matches_stacks(&self, stacks: &BTreeMap<Value, Vec<Value>>) -> bool {
87 self.entries.len() == stacks.len()
88 && stacks.iter().all(|(key, values)| {
89 self.entries
90 .get(key)
91 .is_some_and(|identities| identities.len() == values.len())
92 })
93 }
94
95 pub(crate) fn push(&mut self, key: Value) {
96 self.entries
97 .entry(key)
98 .or_default()
99 .push(DynamicStackEntryId::fresh());
100 }
101
102 pub(crate) fn pop(&mut self, key: &Value) -> bool {
103 let Some(entries) = self.entries.get_mut(key) else {
104 return false;
105 };
106 if entries.pop().is_none() {
107 return false;
108 }
109 if entries.is_empty() {
110 self.entries.remove(key);
111 }
112 true
113 }
114
115 pub(crate) fn remove(&mut self, key: &Value) {
116 self.entries.remove(key);
117 }
118
119 fn entries_for(&self, key: &Value, len: usize) -> Vec<DynamicStackEntryId> {
120 let identities = self.entries.get(key).map(Vec::as_slice).unwrap_or(&[]);
121 assert_eq!(
122 identities.len(),
123 len,
124 "dynamic stack identities must match their value entries"
125 );
126 identities.to_vec()
127 }
128
129 fn push_exact(&mut self, key: Value, identity: DynamicStackEntryId) {
130 self.entries.entry(key).or_default().push(identity);
131 }
132
133 fn pop_exact(&mut self, key: &Value, expected: &DynamicStackEntryId) -> bool {
134 let Some(entries) = self.entries.get_mut(key) else {
135 return false;
136 };
137 if entries.last() != Some(expected) {
138 return false;
139 }
140 entries.pop();
141 if entries.is_empty() {
142 self.entries.remove(key);
143 }
144 true
145 }
146}
147
148#[derive(Clone, Debug)]
149struct DynamicStackEntry {
150 scope: Option<ScopeId>,
151 identity: DynamicStackEntryId,
152 value: Value,
153}
154
155impl DynamicStackEntry {
156 fn inherited(identity: DynamicStackEntryId, value: Value) -> Self {
157 Self {
158 scope: None,
159 identity,
160 value,
161 }
162 }
163
164 fn owned(scope: ScopeId, value: Value) -> Self {
165 Self {
166 scope: Some(scope),
167 identity: DynamicStackEntryId::fresh(),
168 value,
169 }
170 }
171}
172
173#[derive(Clone, Debug, Default)]
174struct ModuleState {
175 scope_ids: IdCounter<ScopeId>,
176 current_files: Vec<Scoped<PathBuf>>,
177 loading: Vec<Scoped<PathBuf>>,
178 exports: Vec<Scoped<Option<Vec<String>>>>,
179}
180
181#[derive(Debug)]
182pub struct ModuleTaskState {
183 inner: RefCell<ModuleState>,
184}
185
186impl Default for ModuleTaskState {
187 fn default() -> Self {
188 Self {
189 inner: RefCell::new(ModuleState::default()),
190 }
191 }
192}
193
194impl ModuleTaskState {
195 pub fn from_snapshot(
196 current_files: Vec<PathBuf>,
197 loading: Vec<PathBuf>,
198 exports: Vec<Option<Vec<String>>>,
199 ) -> Self {
200 Self {
201 inner: RefCell::new(ModuleState {
202 scope_ids: IdCounter::new(),
203 current_files: current_files.into_iter().map(Scoped::inherited).collect(),
204 loading: loading.into_iter().map(Scoped::inherited).collect(),
205 exports: exports.into_iter().map(Scoped::inherited).collect(),
206 }),
207 }
208 }
209
210 pub fn current_file(&self) -> Option<PathBuf> {
211 self.inner
212 .borrow()
213 .current_files
214 .last()
215 .map(|entry| entry.value.clone())
216 }
217
218 pub fn push_current_file(&self, path: PathBuf) -> Result<ScopeId, IdExhausted> {
219 let mut inner = self.inner.borrow_mut();
220 let id = inner.scope_ids.allocate()?;
221 inner.current_files.push(Scoped::owned(id, path));
222 Ok(id)
223 }
224
225 pub fn remove_current_file(&self, id: ScopeId) -> bool {
226 remove_scope(&mut self.inner.borrow_mut().current_files, id).is_some()
227 }
228
229 pub fn pop_current_file(&self) -> Option<PathBuf> {
230 self.inner
231 .borrow_mut()
232 .current_files
233 .pop()
234 .map(|entry| entry.value)
235 }
236
237 pub fn loading(&self) -> Vec<PathBuf> {
238 self.inner
239 .borrow()
240 .loading
241 .iter()
242 .map(|entry| entry.value.clone())
243 .collect()
244 }
245
246 pub fn push_loading(&self, path: PathBuf) -> Result<ScopeId, IdExhausted> {
247 let mut inner = self.inner.borrow_mut();
248 let id = inner.scope_ids.allocate()?;
249 inner.loading.push(Scoped::owned(id, path));
250 Ok(id)
251 }
252
253 pub fn remove_loading(&self, id: ScopeId) -> bool {
254 remove_scope(&mut self.inner.borrow_mut().loading, id).is_some()
255 }
256
257 pub fn exports(&self) -> Vec<Option<Vec<String>>> {
258 self.inner
259 .borrow()
260 .exports
261 .iter()
262 .map(|entry| entry.value.clone())
263 .collect()
264 }
265
266 pub fn push_exports(&self, exports: Option<Vec<String>>) -> Result<ScopeId, IdExhausted> {
267 let mut inner = self.inner.borrow_mut();
268 let id = inner.scope_ids.allocate()?;
269 inner.exports.push(Scoped::owned(id, exports));
270 Ok(id)
271 }
272
273 pub fn set_current_exports(&self, exports: Vec<String>) -> bool {
274 let mut inner = self.inner.borrow_mut();
275 let Some(current) = inner.exports.last_mut() else {
276 return false;
277 };
278 current.value = Some(exports);
279 true
280 }
281
282 pub fn take_exports(&self, id: ScopeId) -> Option<Option<Vec<String>>> {
283 remove_scope(&mut self.inner.borrow_mut().exports, id)
284 }
285
286 pub fn remove_exports(&self, id: ScopeId) -> bool {
287 self.take_exports(id).is_some()
288 }
289
290 pub fn pop_exports(&self) -> Option<Option<Vec<String>>> {
291 self.inner
292 .borrow_mut()
293 .exports
294 .pop()
295 .map(|entry| entry.value)
296 }
297}
298
299impl Trace for ModuleTaskState {
300 fn trace(&self, _sink: &mut dyn FnMut(GcEdge<'_>)) -> bool {
301 self.inner.try_borrow().is_ok()
302 }
303}
304
305impl TaskLocalValue for ModuleTaskState {
306 fn inherit(&self) -> Rc<dyn TaskLocalValue> {
307 let mut inner = self.inner.borrow().clone();
308 strip_scope_ids(&mut inner.current_files);
309 strip_scope_ids(&mut inner.loading);
310 strip_scope_ids(&mut inner.exports);
311 Rc::new(Self {
312 inner: RefCell::new(inner),
313 })
314 }
315
316 fn as_any(&self) -> &dyn Any {
317 self
318 }
319}
320
321#[derive(Clone, Debug, Eq, PartialEq)]
322pub(crate) enum DynamicMutation {
323 UserSet(Value, Value),
324 UserRemove(Value),
325 UserClear,
326 HiddenSet(Value, Value),
327 StackPush {
328 key: Value,
329 value: Value,
330 scope: ScopeId,
331 identity: DynamicStackEntryId,
332 },
333 StackPop {
336 key: Value,
337 expected: DynamicStackEntryId,
338 },
339}
340
341#[derive(Clone, Debug)]
342struct DynamicState {
343 scope_ids: IdCounter<ScopeId>,
344 user_frames: Vec<Scoped<BTreeMap<Value, Value>>>,
345 hidden_frames: Vec<Scoped<BTreeMap<Value, Value>>>,
346 stacks: BTreeMap<Value, Vec<DynamicStackEntry>>,
347 mutations: Option<Vec<DynamicMutation>>,
348}
349
350#[derive(Debug)]
351pub struct DynamicTaskState {
352 inner: RefCell<DynamicState>,
353}
354
355impl DynamicTaskState {
356 pub fn root(
357 user_frames: Vec<BTreeMap<Value, Value>>,
358 hidden_frames: Vec<BTreeMap<Value, Value>>,
359 stacks: BTreeMap<Value, Vec<Value>>,
360 ) -> Self {
361 let identities = DynamicStackIdentities::from_stacks(&stacks);
362 Self::root_with_stack_identities(user_frames, hidden_frames, stacks, &identities)
363 }
364
365 pub(crate) fn root_with_stack_identities(
366 mut user_frames: Vec<BTreeMap<Value, Value>>,
367 mut hidden_frames: Vec<BTreeMap<Value, Value>>,
368 stacks: BTreeMap<Value, Vec<Value>>,
369 identities: &DynamicStackIdentities,
370 ) -> Self {
371 if user_frames.is_empty() {
372 user_frames.push(BTreeMap::new());
373 }
374 if hidden_frames.is_empty() {
375 hidden_frames.push(BTreeMap::new());
376 }
377 Self {
378 inner: RefCell::new(DynamicState {
379 scope_ids: IdCounter::new(),
380 user_frames: user_frames.into_iter().map(Scoped::inherited).collect(),
381 hidden_frames: hidden_frames.into_iter().map(Scoped::inherited).collect(),
382 stacks: stacks
383 .into_iter()
384 .map(|(key, values)| {
385 let entries = identities
386 .entries_for(&key, values.len())
387 .into_iter()
388 .zip(values)
389 .map(|(identity, value)| DynamicStackEntry::inherited(identity, value))
390 .collect();
391 (key, entries)
392 })
393 .collect(),
394 mutations: Some(Vec::new()),
395 }),
396 }
397 }
398
399 pub fn user_get(&self, key: &Value) -> Option<Value> {
400 self.inner
401 .borrow()
402 .user_frames
403 .iter()
404 .rev()
405 .find_map(|frame| frame.value.get(key).cloned())
406 }
407
408 pub fn user_all(&self) -> BTreeMap<Value, Value> {
409 self.inner
410 .borrow()
411 .user_frames
412 .iter()
413 .flat_map(|frame| frame.value.iter())
414 .map(|(key, value)| (key.clone(), value.clone()))
415 .collect()
416 }
417
418 pub fn user_set(&self, key: Value, value: Value) {
419 let mut inner = self.inner.borrow_mut();
420 let publishes = inner
421 .user_frames
422 .last()
423 .is_some_and(|frame| frame.id.is_none());
424 if let Some(frame) = inner.user_frames.last_mut() {
425 frame.value.insert(key.clone(), value.clone());
426 }
427 if publishes {
428 record(&mut inner, DynamicMutation::UserSet(key, value));
429 }
430 }
431
432 pub fn user_remove(&self, key: &Value) -> Option<Value> {
433 let mut inner = self.inner.borrow_mut();
434 let publishes = inner
435 .user_frames
436 .iter()
437 .any(|frame| frame.id.is_none() && frame.value.contains_key(key));
438 let mut removed = None;
439 for frame in inner.user_frames.iter_mut().rev() {
440 if let Some(value) = frame.value.remove(key) {
441 if removed.is_none() {
442 removed = Some(value);
443 }
444 }
445 }
446 if publishes {
447 record(&mut inner, DynamicMutation::UserRemove(key.clone()));
448 }
449 removed
450 }
451
452 pub fn user_clear(&self) {
453 let mut inner = self.inner.borrow_mut();
454 inner.user_frames.clear();
455 inner.user_frames.push(Scoped::inherited(BTreeMap::new()));
456 record(&mut inner, DynamicMutation::UserClear);
457 }
458
459 pub fn push_user_frame(
460 &self,
461 bindings: BTreeMap<Value, Value>,
462 ) -> Result<ScopeId, IdExhausted> {
463 let mut inner = self.inner.borrow_mut();
464 let id = inner.scope_ids.allocate()?;
465 inner.user_frames.push(Scoped::owned(id, bindings));
466 Ok(id)
467 }
468
469 pub fn remove_user_frame(&self, id: ScopeId) -> bool {
470 remove_scope(&mut self.inner.borrow_mut().user_frames, id).is_some()
471 }
472
473 pub fn pop_user_frame(&self) -> bool {
474 let mut inner = self.inner.borrow_mut();
475 if inner.user_frames.len() <= 1 {
476 return false;
477 }
478 inner.user_frames.pop();
479 true
480 }
481
482 pub fn hidden_get(&self, key: &Value) -> Option<Value> {
483 self.inner
484 .borrow()
485 .hidden_frames
486 .iter()
487 .rev()
488 .find_map(|frame| frame.value.get(key).cloned())
489 }
490
491 pub fn hidden_set(&self, key: Value, value: Value) {
492 let mut inner = self.inner.borrow_mut();
493 let publishes = inner
494 .hidden_frames
495 .last()
496 .is_some_and(|frame| frame.id.is_none());
497 if let Some(frame) = inner.hidden_frames.last_mut() {
498 frame.value.insert(key.clone(), value.clone());
499 }
500 if publishes {
501 record(&mut inner, DynamicMutation::HiddenSet(key, value));
502 }
503 }
504
505 pub fn push_hidden_frame(
506 &self,
507 bindings: BTreeMap<Value, Value>,
508 ) -> Result<ScopeId, IdExhausted> {
509 let mut inner = self.inner.borrow_mut();
510 let id = inner.scope_ids.allocate()?;
511 inner.hidden_frames.push(Scoped::owned(id, bindings));
512 Ok(id)
513 }
514
515 pub fn remove_hidden_frame(&self, id: ScopeId) -> bool {
516 remove_scope(&mut self.inner.borrow_mut().hidden_frames, id).is_some()
517 }
518
519 pub fn pop_hidden_frame(&self) -> bool {
520 let mut inner = self.inner.borrow_mut();
521 if inner.hidden_frames.len() <= 1 {
522 return false;
523 }
524 inner.hidden_frames.pop();
525 true
526 }
527
528 pub fn stack_get(&self, key: &Value) -> Vec<Value> {
529 self.inner
530 .borrow()
531 .stacks
532 .get(key)
533 .map(|values| values.iter().map(|entry| entry.value.clone()).collect())
534 .unwrap_or_default()
535 }
536
537 pub fn stack_push(&self, key: Value, value: Value) -> Result<ScopeId, IdExhausted> {
538 let mut inner = self.inner.borrow_mut();
539 let id = inner.scope_ids.allocate()?;
540 let entry = DynamicStackEntry::owned(id, value.clone());
541 let identity = entry.identity.clone();
542 inner.stacks.entry(key.clone()).or_default().push(entry);
543 record(
544 &mut inner,
545 DynamicMutation::StackPush {
546 key,
547 value,
548 scope: id,
549 identity,
550 },
551 );
552 Ok(id)
553 }
554
555 pub fn stack_pop(&self, key: &Value) -> Option<Value> {
556 let mut inner = self.inner.borrow_mut();
557 let entry = inner.stacks.get_mut(key)?.pop()?;
558 if inner.stacks.get(key).is_some_and(Vec::is_empty) {
559 inner.stacks.remove(key);
560 }
561 if entry
562 .scope
563 .is_some_and(|scope| cancel_pending_stack_push(&mut inner, scope))
564 {
565 return Some(entry.value);
566 }
567 record(
568 &mut inner,
569 DynamicMutation::StackPop {
570 key: key.clone(),
571 expected: entry.identity,
572 },
573 );
574 Some(entry.value)
575 }
576
577 pub fn remove_stack_value(&self, key: &Value, id: ScopeId) -> Option<Value> {
578 let mut inner = self.inner.borrow_mut();
579 let stack = inner.stacks.get_mut(key)?;
580 let index = stack.iter().position(|entry| entry.scope == Some(id))?;
581 let was_top = index + 1 == stack.len();
582 let removed = stack.remove(index);
583 if stack.is_empty() {
584 inner.stacks.remove(key);
585 }
586 if !cancel_pending_stack_push(&mut inner, id) && was_top {
587 record(
588 &mut inner,
589 DynamicMutation::StackPop {
590 key: key.clone(),
591 expected: removed.identity.clone(),
592 },
593 );
594 }
595 Some(removed.value)
596 }
597
598 pub(crate) fn drain_mutations(&self) -> Option<Vec<DynamicMutation>> {
599 self.inner
600 .borrow_mut()
601 .mutations
602 .as_mut()
603 .map(std::mem::take)
604 }
605}
606
607impl Trace for DynamicTaskState {
608 fn trace(&self, sink: &mut dyn FnMut(GcEdge<'_>)) -> bool {
609 let Ok(inner) = self.inner.try_borrow() else {
610 return false;
611 };
612 for frame in inner.user_frames.iter().chain(&inner.hidden_frames) {
613 for (key, value) in &frame.value {
614 sink(GcEdge::Value(key));
615 sink(GcEdge::Value(value));
616 }
617 }
618 for (key, values) in &inner.stacks {
619 sink(GcEdge::Value(key));
620 for value in values {
621 sink(GcEdge::Value(&value.value));
622 }
623 }
624 if let Some(mutations) = &inner.mutations {
625 for mutation in mutations {
626 trace_mutation(mutation, sink);
627 }
628 }
629 true
630 }
631}
632
633impl TaskLocalValue for DynamicTaskState {
634 fn inherit(&self) -> Rc<dyn TaskLocalValue> {
635 let mut inner = self.inner.borrow().clone();
636 strip_scope_ids(&mut inner.user_frames);
637 strip_scope_ids(&mut inner.hidden_frames);
638 for stack in inner.stacks.values_mut() {
639 for entry in stack {
640 entry.scope = None;
641 }
642 }
643 inner.mutations = None;
644 Rc::new(Self {
645 inner: RefCell::new(inner),
646 })
647 }
648
649 fn as_any(&self) -> &dyn Any {
650 self
651 }
652}
653
654fn remove_scope<T>(entries: &mut Vec<Scoped<T>>, id: ScopeId) -> Option<T> {
655 entries
656 .iter()
657 .position(|entry| entry.id == Some(id))
658 .map(|index| entries.remove(index).value)
659}
660
661fn strip_scope_ids<T>(entries: &mut [Scoped<T>]) {
662 for entry in entries {
663 entry.id = None;
664 }
665}
666
667fn record(inner: &mut DynamicState, mutation: DynamicMutation) {
668 if let Some(mutations) = &mut inner.mutations {
669 mutations.push(mutation);
670 }
671}
672
673fn cancel_pending_stack_push(inner: &mut DynamicState, scope: ScopeId) -> bool {
674 let Some(mutations) = inner.mutations.as_mut() else {
675 return false;
676 };
677 let Some(index) = mutations.iter().rposition(
678 |mutation| matches!(mutation, DynamicMutation::StackPush { scope: candidate, .. } if *candidate == scope),
679 ) else {
680 return false;
681 };
682 mutations.remove(index);
683 true
684}
685
686pub(crate) fn apply_dynamic_mutations(
688 user_frames: &mut Vec<BTreeMap<Value, Value>>,
689 hidden_frames: &mut Vec<BTreeMap<Value, Value>>,
690 stacks: &mut BTreeMap<Value, Vec<Value>>,
691 stack_identities: &mut DynamicStackIdentities,
692 mutations: &[DynamicMutation],
693) {
694 assert!(
695 stack_identities.matches_stacks(stacks),
696 "dynamic stack identities must match their value entries"
697 );
698 if user_frames.is_empty() {
699 user_frames.push(BTreeMap::new());
700 }
701 if hidden_frames.is_empty() {
702 hidden_frames.push(BTreeMap::new());
703 }
704 for mutation in mutations {
705 match mutation {
706 DynamicMutation::UserSet(key, value) => {
707 user_frames
708 .last_mut()
709 .expect("user context is normalized above")
710 .insert(key.clone(), value.clone());
711 }
712 DynamicMutation::UserRemove(key) => {
713 for frame in user_frames.iter_mut().rev() {
714 frame.remove(key);
715 }
716 }
717 DynamicMutation::UserClear => {
718 user_frames.clear();
719 user_frames.push(BTreeMap::new());
720 }
721 DynamicMutation::HiddenSet(key, value) => {
722 hidden_frames
723 .last_mut()
724 .expect("hidden context is normalized above")
725 .insert(key.clone(), value.clone());
726 }
727 DynamicMutation::StackPush {
728 key,
729 value,
730 identity,
731 ..
732 } => {
733 stacks.entry(key.clone()).or_default().push(value.clone());
734 stack_identities.push_exact(key.clone(), identity.clone());
735 }
736 DynamicMutation::StackPop { key, expected } => {
737 if stack_identities.pop_exact(key, expected) {
738 let stack = stacks
739 .get_mut(key)
740 .expect("stack identity must have a matching value stack");
741 stack
742 .pop()
743 .expect("stack identity must have a matching value entry");
744 if stack.is_empty() {
745 stacks.remove(key);
746 }
747 }
748 }
749 }
750 }
751}
752
753fn trace_mutation(mutation: &DynamicMutation, sink: &mut dyn FnMut(GcEdge<'_>)) {
754 match mutation {
755 DynamicMutation::UserSet(key, value)
756 | DynamicMutation::HiddenSet(key, value)
757 | DynamicMutation::StackPush { key, value, .. } => {
758 sink(GcEdge::Value(key));
759 sink(GcEdge::Value(value));
760 }
761 DynamicMutation::UserRemove(key) => {
762 sink(GcEdge::Value(key));
763 }
764 DynamicMutation::StackPop { key, .. } => {
765 sink(GcEdge::Value(key));
766 }
767 DynamicMutation::UserClear => {}
768 }
769}
770
771#[cfg(test)]
772mod tests {
773 use std::collections::BTreeMap;
774 use std::path::PathBuf;
775 use std::rc::Rc;
776
777 use crate::cycle::GcEdge;
778 use crate::runtime::{TaskContext, Trace};
779 use crate::Value;
780
781 use super::{
782 apply_dynamic_mutations, DynamicMutation, DynamicStackIdentities, DynamicTaskState,
783 ModuleTaskState,
784 };
785
786 #[test]
787 fn module_scopes_remove_the_exact_current_file_token_once() {
788 let state = ModuleTaskState::default();
789 let first = state
790 .push_current_file(PathBuf::from("first.sema"))
791 .expect("scope ID available");
792 let second = state
793 .push_current_file(PathBuf::from("second.sema"))
794 .expect("scope ID available");
795
796 assert!(state.remove_current_file(first));
797 assert!(!state.remove_current_file(first));
798 assert_eq!(state.current_file(), Some(PathBuf::from("second.sema")));
799 assert!(state.remove_current_file(second));
800 assert_eq!(state.current_file(), None);
801 }
802
803 #[test]
804 fn module_scopes_remove_the_exact_loading_token_once() {
805 let state = ModuleTaskState::default();
806 let first = state
807 .push_loading(PathBuf::from("first.sema"))
808 .expect("scope ID available");
809 let second = state
810 .push_loading(PathBuf::from("second.sema"))
811 .expect("scope ID available");
812
813 assert!(state.remove_loading(first));
814 assert!(!state.remove_loading(first));
815 assert_eq!(state.loading(), vec![PathBuf::from("second.sema")]);
816 assert!(state.remove_loading(second));
817 assert!(state.loading().is_empty());
818 }
819
820 #[test]
821 fn module_scopes_remove_the_exact_export_token_once() {
822 let state = ModuleTaskState::default();
823 let first = state
824 .push_exports(Some(vec!["first".to_owned()]))
825 .expect("scope ID available");
826 let second = state
827 .push_exports(Some(vec!["second".to_owned()]))
828 .expect("scope ID available");
829
830 assert!(state.remove_exports(first));
831 assert!(!state.remove_exports(first));
832 assert_eq!(state.exports(), vec![Some(vec!["second".to_owned()])]);
833 assert!(state.remove_exports(second));
834 assert!(state.exports().is_empty());
835 }
836
837 #[test]
838 fn module_exports_update_and_take_the_exact_scope() {
839 let state = ModuleTaskState::default();
840 let outer = state.push_exports(None).expect("scope ID available");
841 let inner = state.push_exports(None).expect("scope ID available");
842
843 assert!(state.set_current_exports(vec!["inner".to_owned()]));
844 assert_eq!(
845 state.take_exports(outer),
846 Some(None),
847 "taking an outer scope must not consume the current scope"
848 );
849 assert_eq!(
850 state.take_exports(inner),
851 Some(Some(vec!["inner".to_owned()]))
852 );
853 assert_eq!(state.take_exports(inner), None);
854 }
855
856 #[test]
857 fn module_child_snapshot_is_independent() {
858 let parent = Rc::new(ModuleTaskState::from_snapshot(
859 vec![PathBuf::from("parent.sema")],
860 vec![PathBuf::from("loading.sema")],
861 vec![Some(vec!["parent-export".to_owned()])],
862 ));
863 let mut context = TaskContext::default();
864 context.insert(Rc::clone(&parent));
865
866 let child = context.inherit_for_child();
867 let child = child
868 .get_rc::<ModuleTaskState>()
869 .expect("module state inherited");
870 let child_file = child
871 .push_current_file(PathBuf::from("child.sema"))
872 .expect("scope ID available");
873 let child_loading = child
874 .push_loading(PathBuf::from("child-loading.sema"))
875 .expect("scope ID available");
876 let child_exports = child.push_exports(None).expect("scope ID available");
877
878 assert_eq!(parent.current_file(), Some(PathBuf::from("parent.sema")));
879 assert_eq!(parent.loading(), vec![PathBuf::from("loading.sema")]);
880 assert_eq!(
881 parent.exports(),
882 vec![Some(vec!["parent-export".to_owned()])]
883 );
884 assert!(child.remove_current_file(child_file));
885 assert!(child.remove_loading(child_loading));
886 assert!(child.remove_exports(child_exports));
887 }
888
889 #[test]
890 fn module_child_strips_parent_scope_tokens() {
891 let parent = Rc::new(ModuleTaskState::default());
892 let file = parent
893 .push_current_file(PathBuf::from("parent.sema"))
894 .expect("scope ID available");
895 let loading = parent
896 .push_loading(PathBuf::from("loading.sema"))
897 .expect("scope ID available");
898 let exports = parent
899 .push_exports(Some(vec!["parent-export".to_owned()]))
900 .expect("scope ID available");
901 let mut context = TaskContext::default();
902 context.insert(Rc::clone(&parent));
903
904 let child = context
905 .inherit_for_child()
906 .get_rc::<ModuleTaskState>()
907 .expect("module state inherited");
908
909 assert!(!child.remove_current_file(file));
910 assert!(!child.remove_loading(loading));
911 assert!(!child.remove_exports(exports));
912 assert_eq!(child.current_file(), Some(PathBuf::from("parent.sema")));
913 assert_eq!(child.loading(), vec![PathBuf::from("loading.sema")]);
914 assert_eq!(
915 child.exports(),
916 vec![Some(vec!["parent-export".to_owned()])]
917 );
918 let child_scope = child
919 .push_current_file(PathBuf::from("child.sema"))
920 .expect("scope ID available");
921 assert!(child_scope > exports);
922 }
923
924 #[test]
925 fn dynamic_scope_cleanup_is_exact_and_idempotent() {
926 let state = DynamicTaskState::root(
927 vec![BTreeMap::new()],
928 vec![BTreeMap::new()],
929 BTreeMap::new(),
930 );
931 let outer_user = state
932 .push_user_frame(BTreeMap::from([(
933 Value::keyword("frame"),
934 Value::string("outer"),
935 )]))
936 .expect("scope ID available");
937 let inner_user = state
938 .push_user_frame(BTreeMap::from([(
939 Value::keyword("frame"),
940 Value::string("inner"),
941 )]))
942 .expect("scope ID available");
943 let outer_hidden = state
944 .push_hidden_frame(BTreeMap::new())
945 .expect("scope ID available");
946 let inner_hidden = state
947 .push_hidden_frame(BTreeMap::new())
948 .expect("scope ID available");
949 let stack_key = Value::keyword("stack");
950 let outer_stack = state
951 .stack_push(stack_key.clone(), Value::string("outer"))
952 .expect("scope ID available");
953 let inner_stack = state
954 .stack_push(stack_key.clone(), Value::string("inner"))
955 .expect("scope ID available");
956
957 assert!(state.remove_user_frame(outer_user));
958 assert!(!state.remove_user_frame(outer_user));
959 assert_eq!(
960 state.user_get(&Value::keyword("frame")),
961 Some(Value::string("inner"))
962 );
963 assert!(state.remove_hidden_frame(outer_hidden));
964 assert!(!state.remove_hidden_frame(outer_hidden));
965 assert_eq!(
966 state.remove_stack_value(&stack_key, outer_stack),
967 Some(Value::string("outer"))
968 );
969 assert_eq!(state.remove_stack_value(&stack_key, outer_stack), None);
970 assert_eq!(state.stack_get(&stack_key), vec![Value::string("inner")]);
971
972 assert!(state.remove_user_frame(inner_user));
973 assert!(state.remove_hidden_frame(inner_hidden));
974 assert_eq!(
975 state.remove_stack_value(&stack_key, inner_stack),
976 Some(Value::string("inner"))
977 );
978
979 state
980 .push_user_frame(BTreeMap::new())
981 .expect("scope ID available");
982 state
983 .push_hidden_frame(BTreeMap::new())
984 .expect("scope ID available");
985 assert!(state.pop_user_frame());
986 assert!(state.pop_hidden_frame());
987 assert!(!state.pop_user_frame());
988 assert!(!state.pop_hidden_frame());
989 }
990
991 #[test]
992 fn root_dynamic_state_records_publishable_mutations_in_order() {
993 let state = DynamicTaskState::root(
994 vec![BTreeMap::new()],
995 vec![BTreeMap::new()],
996 BTreeMap::new(),
997 );
998 let user_key = Value::keyword("user");
999 let hidden_key = Value::keyword("hidden");
1000 let stack_key = Value::keyword("stack");
1001
1002 state.user_set(user_key.clone(), Value::int(1));
1003 assert_eq!(state.user_remove(&user_key), Some(Value::int(1)));
1004 state.user_clear();
1005 state.hidden_set(hidden_key.clone(), Value::int(2));
1006 state
1007 .stack_push(stack_key.clone(), Value::int(3))
1008 .expect("scope ID available");
1009 assert_eq!(state.stack_pop(&stack_key), Some(Value::int(3)));
1010
1011 assert_eq!(
1012 state.drain_mutations(),
1013 Some(vec![
1014 DynamicMutation::UserSet(user_key.clone(), Value::int(1)),
1015 DynamicMutation::UserRemove(user_key),
1016 DynamicMutation::UserClear,
1017 DynamicMutation::HiddenSet(hidden_key, Value::int(2)),
1018 ])
1019 );
1020 assert_eq!(state.drain_mutations(), Some(Vec::new()));
1021 }
1022
1023 #[test]
1024 fn inherited_stack_pop_cannot_remove_another_roots_equal_later_push() {
1025 let key = Value::keyword("stack");
1026 let initial = BTreeMap::from([(key.clone(), vec![Value::keyword("a")])]);
1027 let mut identities = DynamicStackIdentities::from_stacks(&initial);
1028 let first = DynamicTaskState::root_with_stack_identities(
1029 vec![BTreeMap::new()],
1030 vec![BTreeMap::new()],
1031 initial.clone(),
1032 &identities,
1033 );
1034 let second = DynamicTaskState::root_with_stack_identities(
1035 vec![BTreeMap::new()],
1036 vec![BTreeMap::new()],
1037 initial.clone(),
1038 &identities,
1039 );
1040
1041 first
1042 .stack_push(key.clone(), Value::keyword("a"))
1043 .expect("scope ID available");
1044 assert_eq!(second.stack_pop(&key), Some(Value::keyword("a")));
1045
1046 let mut user = vec![BTreeMap::new()];
1047 let mut hidden = vec![BTreeMap::new()];
1048 let mut published = initial;
1049 apply_dynamic_mutations(
1050 &mut user,
1051 &mut hidden,
1052 &mut published,
1053 &mut identities,
1054 &first.drain_mutations().expect("root journal"),
1055 );
1056 apply_dynamic_mutations(
1057 &mut user,
1058 &mut hidden,
1059 &mut published,
1060 &mut identities,
1061 &second.drain_mutations().expect("root journal"),
1062 );
1063
1064 assert_eq!(
1065 published.get(&key),
1066 Some(&vec![Value::keyword("a"), Value::keyword("a")])
1067 );
1068 }
1069
1070 #[test]
1071 fn stale_pop_cannot_remove_an_equal_entry_recreated_by_a_later_root() {
1072 let key = Value::keyword("stack");
1073 let value = Value::keyword("same");
1074 let initial = BTreeMap::from([(key.clone(), vec![value.clone()])]);
1075 let mut identities = DynamicStackIdentities::from_stacks(&initial);
1076 let stale = DynamicTaskState::root_with_stack_identities(
1077 vec![BTreeMap::new()],
1078 vec![BTreeMap::new()],
1079 initial.clone(),
1080 &identities,
1081 );
1082 let recreating = DynamicTaskState::root_with_stack_identities(
1083 vec![BTreeMap::new()],
1084 vec![BTreeMap::new()],
1085 initial.clone(),
1086 &identities,
1087 );
1088
1089 assert_eq!(stale.stack_pop(&key), Some(value.clone()));
1090 assert_eq!(recreating.stack_pop(&key), Some(value.clone()));
1091 recreating
1092 .stack_push(key.clone(), value.clone())
1093 .expect("scope ID available");
1094
1095 let mut user = vec![BTreeMap::new()];
1096 let mut hidden = vec![BTreeMap::new()];
1097 let mut published = initial;
1098 apply_dynamic_mutations(
1099 &mut user,
1100 &mut hidden,
1101 &mut published,
1102 &mut identities,
1103 &recreating.drain_mutations().expect("root journal"),
1104 );
1105 apply_dynamic_mutations(
1106 &mut user,
1107 &mut hidden,
1108 &mut published,
1109 &mut identities,
1110 &stale.drain_mutations().expect("root journal"),
1111 );
1112
1113 assert_eq!(published.get(&key), Some(&vec![value]));
1114 }
1115
1116 #[test]
1117 fn one_root_can_replay_multiple_inherited_pops_in_order() {
1118 let key = Value::keyword("stack");
1119 let initial = BTreeMap::from([(
1120 key.clone(),
1121 vec![Value::keyword("bottom"), Value::keyword("top")],
1122 )]);
1123 let mut identities = DynamicStackIdentities::from_stacks(&initial);
1124 let root = DynamicTaskState::root_with_stack_identities(
1125 vec![BTreeMap::new()],
1126 vec![BTreeMap::new()],
1127 initial.clone(),
1128 &identities,
1129 );
1130
1131 assert_eq!(root.stack_pop(&key), Some(Value::keyword("top")));
1132 assert_eq!(root.stack_pop(&key), Some(Value::keyword("bottom")));
1133
1134 let mut user = vec![BTreeMap::new()];
1135 let mut hidden = vec![BTreeMap::new()];
1136 let mut published = initial;
1137 apply_dynamic_mutations(
1138 &mut user,
1139 &mut hidden,
1140 &mut published,
1141 &mut identities,
1142 &root.drain_mutations().expect("root journal"),
1143 );
1144
1145 assert!(!published.contains_key(&key));
1146 }
1147
1148 #[test]
1149 fn owned_stack_cleanup_cancels_only_its_exact_push_with_duplicate_values() {
1150 let key = Value::keyword("stack");
1151 let state = DynamicTaskState::root(
1152 vec![BTreeMap::new()],
1153 vec![BTreeMap::new()],
1154 BTreeMap::new(),
1155 );
1156 let first = state
1157 .stack_push(key.clone(), Value::keyword("same"))
1158 .expect("scope ID available");
1159 let second = state
1160 .stack_push(key.clone(), Value::keyword("same"))
1161 .expect("scope ID available");
1162
1163 assert_eq!(
1164 state.remove_stack_value(&key, first),
1165 Some(Value::keyword("same"))
1166 );
1167 let mutations = state.drain_mutations().expect("root journal");
1168 assert!(matches!(
1169 mutations.as_slice(),
1170 [DynamicMutation::StackPush {
1171 key: mutation_key,
1172 value,
1173 scope,
1174 ..
1175 }] if mutation_key == &key && value == &Value::keyword("same") && *scope == second
1176 ));
1177
1178 let mut user = vec![BTreeMap::new()];
1179 let mut hidden = vec![BTreeMap::new()];
1180 let mut published = BTreeMap::new();
1181 let mut identities = DynamicStackIdentities::default();
1182 apply_dynamic_mutations(
1183 &mut user,
1184 &mut hidden,
1185 &mut published,
1186 &mut identities,
1187 &mutations,
1188 );
1189 assert_eq!(published.get(&key), Some(&vec![Value::keyword("same")]));
1190 }
1191
1192 #[test]
1193 fn dynamic_user_all_merges_frames_from_outer_to_inner() {
1194 let outer_only = Value::keyword("outer-only");
1195 let shadowed = Value::keyword("shadowed");
1196 let inner_only = Value::keyword("inner-only");
1197 let state = DynamicTaskState::root(
1198 vec![
1199 BTreeMap::from([
1200 (outer_only.clone(), Value::int(1)),
1201 (shadowed.clone(), Value::int(2)),
1202 ]),
1203 BTreeMap::from([
1204 (shadowed.clone(), Value::int(3)),
1205 (inner_only.clone(), Value::int(4)),
1206 ]),
1207 ],
1208 vec![BTreeMap::new()],
1209 BTreeMap::new(),
1210 );
1211
1212 assert_eq!(
1213 state.user_all(),
1214 BTreeMap::from([
1215 (outer_only, Value::int(1)),
1216 (shadowed, Value::int(3)),
1217 (inner_only, Value::int(4)),
1218 ])
1219 );
1220 }
1221
1222 #[test]
1223 fn inherited_top_frames_record_user_and_hidden_sets_for_publication() {
1224 let user_key = Value::keyword("user");
1225 let hidden_key = Value::keyword("hidden");
1226 let state = DynamicTaskState::root(
1227 vec![
1228 BTreeMap::from([(user_key.clone(), Value::int(1))]),
1229 BTreeMap::new(),
1230 ],
1231 vec![
1232 BTreeMap::from([(hidden_key.clone(), Value::int(2))]),
1233 BTreeMap::new(),
1234 ],
1235 BTreeMap::new(),
1236 );
1237
1238 state.user_set(user_key.clone(), Value::int(10));
1239 state.hidden_set(hidden_key.clone(), Value::int(20));
1240
1241 assert_eq!(
1242 state.drain_mutations(),
1243 Some(vec![
1244 DynamicMutation::UserSet(user_key, Value::int(10)),
1245 DynamicMutation::HiddenSet(hidden_key, Value::int(20)),
1246 ])
1247 );
1248 }
1249
1250 #[test]
1251 fn removing_a_key_from_any_inherited_frame_records_publication() {
1252 let key = Value::keyword("removed");
1253 let state = DynamicTaskState::root(
1254 vec![
1255 BTreeMap::new(),
1256 BTreeMap::from([(key.clone(), Value::int(1))]),
1257 ],
1258 vec![BTreeMap::new()],
1259 BTreeMap::new(),
1260 );
1261
1262 assert_eq!(state.user_remove(&key), Some(Value::int(1)));
1263 assert_eq!(
1264 state.drain_mutations(),
1265 Some(vec![DynamicMutation::UserRemove(key)])
1266 );
1267 }
1268
1269 #[test]
1270 fn dynamic_child_deep_clones_state_without_publication_authority() {
1271 let user_key = Value::keyword("user");
1272 let hidden_key = Value::keyword("hidden");
1273 let stack_key = Value::keyword("stack");
1274 let parent = Rc::new(DynamicTaskState::root(
1275 vec![BTreeMap::from([(user_key.clone(), Value::int(1))])],
1276 vec![BTreeMap::from([(hidden_key.clone(), Value::int(2))])],
1277 BTreeMap::from([(stack_key.clone(), vec![Value::int(3)])]),
1278 ));
1279 let mut context = TaskContext::default();
1280 context.insert(Rc::clone(&parent));
1281
1282 let child = context.inherit_for_child();
1283 let child = child
1284 .get_rc::<DynamicTaskState>()
1285 .expect("dynamic state inherited");
1286 child.user_set(user_key.clone(), Value::int(10));
1287 child.hidden_set(hidden_key.clone(), Value::int(20));
1288 child
1289 .stack_push(stack_key.clone(), Value::int(30))
1290 .expect("scope ID available");
1291
1292 assert_eq!(parent.user_get(&user_key), Some(Value::int(1)));
1293 assert_eq!(parent.hidden_get(&hidden_key), Some(Value::int(2)));
1294 assert_eq!(parent.stack_get(&stack_key), vec![Value::int(3)]);
1295 assert_eq!(child.user_get(&user_key), Some(Value::int(10)));
1296 assert_eq!(child.hidden_get(&hidden_key), Some(Value::int(20)));
1297 assert_eq!(
1298 child.stack_get(&stack_key),
1299 vec![Value::int(3), Value::int(30)]
1300 );
1301 assert_eq!(child.drain_mutations(), None);
1302 assert_eq!(parent.drain_mutations(), Some(Vec::new()));
1303 }
1304
1305 #[test]
1306 fn dynamic_child_strips_parent_scope_tokens() {
1307 let parent = Rc::new(DynamicTaskState::root(
1308 vec![BTreeMap::new()],
1309 vec![BTreeMap::new()],
1310 BTreeMap::new(),
1311 ));
1312 let user = parent
1313 .push_user_frame(BTreeMap::from([(Value::keyword("user"), Value::int(1))]))
1314 .expect("scope ID available");
1315 let hidden = parent
1316 .push_hidden_frame(BTreeMap::from([(Value::keyword("hidden"), Value::int(2))]))
1317 .expect("scope ID available");
1318 let stack_key = Value::keyword("stack");
1319 let stack = parent
1320 .stack_push(stack_key.clone(), Value::int(3))
1321 .expect("scope ID available");
1322 let mut context = TaskContext::default();
1323 context.insert(Rc::clone(&parent));
1324
1325 let child = context
1326 .inherit_for_child()
1327 .get_rc::<DynamicTaskState>()
1328 .expect("dynamic state inherited");
1329
1330 assert!(!child.remove_user_frame(user));
1331 assert!(!child.remove_hidden_frame(hidden));
1332 assert_eq!(child.remove_stack_value(&stack_key, stack), None);
1333 assert_eq!(child.user_get(&Value::keyword("user")), Some(Value::int(1)));
1334 assert_eq!(
1335 child.hidden_get(&Value::keyword("hidden")),
1336 Some(Value::int(2))
1337 );
1338 assert_eq!(child.stack_get(&stack_key), vec![Value::int(3)]);
1339 let child_scope = child
1340 .push_user_frame(BTreeMap::new())
1341 .expect("scope ID available");
1342 assert!(child_scope > stack);
1343 }
1344
1345 #[test]
1346 fn dynamic_child_preserves_inherited_stack_entry_identity() {
1347 let key = Value::keyword("stack");
1348 let parent = Rc::new(DynamicTaskState::root(
1349 vec![BTreeMap::new()],
1350 vec![BTreeMap::new()],
1351 BTreeMap::from([(key.clone(), vec![Value::keyword("value")])]),
1352 ));
1353 let mut context = TaskContext::default();
1354 context.insert(Rc::clone(&parent));
1355
1356 let child = context
1357 .inherit_for_child()
1358 .get_rc::<DynamicTaskState>()
1359 .expect("dynamic state inherited");
1360 let parent_identity = parent.inner.borrow().stacks[&key][0].identity.clone();
1361 let child_entry = child.inner.borrow().stacks[&key][0].clone();
1362
1363 assert_eq!(child_entry.identity, parent_identity);
1364 assert_eq!(child_entry.scope, None);
1365 }
1366
1367 #[test]
1368 fn conditional_stack_pop_traces_its_key() {
1369 let key = Value::keyword("stack");
1370 let state = DynamicTaskState::root(
1371 vec![BTreeMap::new()],
1372 vec![BTreeMap::new()],
1373 BTreeMap::from([(key.clone(), vec![Value::int(1)])]),
1374 );
1375 assert_eq!(state.stack_pop(&key), Some(Value::int(1)));
1376
1377 let mut edges = 0;
1378 assert!(state.trace(&mut |edge| {
1379 assert!(matches!(edge, GcEdge::Value(_)));
1380 edges += 1;
1381 }));
1382 assert_eq!(edges, 1);
1383 }
1384
1385 #[test]
1386 fn dynamic_trace_reports_every_stored_value_edge_exactly_once() {
1387 let state = DynamicTaskState::root(
1388 vec![BTreeMap::from([(Value::keyword("user"), Value::int(1))])],
1389 vec![BTreeMap::from([(Value::keyword("hidden"), Value::int(2))])],
1390 BTreeMap::from([(Value::keyword("stack"), vec![Value::int(3), Value::int(4)])]),
1391 );
1392 state.user_set(Value::keyword("published"), Value::int(5));
1393 state.hidden_set(Value::keyword("private"), Value::int(6));
1394 state
1395 .stack_push(Value::keyword("other-stack"), Value::int(7))
1396 .expect("scope ID available");
1397 state.user_remove(&Value::keyword("published"));
1398
1399 let mut edges = 0;
1400 assert!(state.trace(&mut |edge| {
1401 assert!(matches!(edge, GcEdge::Value(_)));
1402 edges += 1;
1403 }));
1404
1405 assert_eq!(edges, 18);
1407 }
1408}