Skip to main content

hermes_support/
persistent_scoped_map.rs

1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8//! Port of `hermes::PersistentScopedMap`
9//! (`include/hermes/ADT/PersistentScopedMap.h`): a scoped hash table similar
10//! to `hermes::ScopedHashTable`, but scopes can be retained and reactivated
11//! after they have been popped from the table.
12//!
13//! The type [`ScopePtr`], which in C++ is an intrusive reference-counting
14//! smart pointer, is used to retain ownership of a scope. The pointer can be
15//! used to reactivate the scope in the table using
16//! [`PersistentScopedMap::activate_scope`].
17//!
18//! Scopes can also be re-activated even if they are currently active but are
19//! not the current scope. Note however that if there are active scopes in
20//! the stack, in the end we must restore the state — the top-most scope in
21//! the stack must be active.
22//!
23//! Example (mirrors the C++ doc comment):
24//! ```
25//! use hermes_support::persistent_scoped_map::{PersistentScopedMap, Scope, ScopePtr};
26//!
27//! let table: PersistentScopedMap<&str, &str> = PersistentScopedMap::new();
28//! let mut ptr: ScopePtr<&str, &str> = ScopePtr::default();
29//! let a = Scope::new(&table);
30//! let b = Scope::new(&table);
31//! // At this point, A and B are active scopes in the table.
32//! // A(active)->B(active)
33//! // We can reactivate A:
34//! table.activate_scope(&a.ptr());
35//! // Now the state is the same as it was when A was active initially.
36//! table.activate_scope(&b.ptr());
37//! // The state has been restored to "normal".
38//! {
39//!     let c = Scope::new(&table);
40//!     // A(active)->B(active)->C(active)
41//!     // Save C for later.
42//!     ptr = c.ptr();
43//! }
44//! // A(active)->B(active)
45//! let d = Scope::new(&table);
46//! // A(active)->B(active)->D(active).
47//! // Activate C again.
48//! table.activate_scope(&ptr);
49//! // A(active)->B(active)->C(active)
50//! // Restore normal state.
51//! table.activate_scope(&d.ptr());
52//! ptr.reset();
53//! drop(d);
54//! drop(b);
55//! drop(a);
56//! ```
57//!
58//! ## Deviations from the C++ implementation
59//!
60//! `support` is `#![forbid(unsafe_code)]`, so this port cannot reproduce the
61//! C++ web of raw pointers and an intrusive, manually-maintained reference
62//! count (`PersistentScopedMapScopeData::addRef`/`decRef`). Instead:
63//!
64//! - A scope's data (`ScopeData<K, V>`, the port of
65//!   `detail::PersistentScopedMapScopeData`) is held behind an `Rc`.
66//!   [`ScopePtr`] is `Option<ScopeRef<K, V>>`; `Rc`'s own strong count
67//!   replaces the manual `refCount_`, and `Clone`/`Drop` on `Rc` replace
68//!   `addRef`/`decRef` — there is nothing left to implement by hand.
69//! - The C++ `Node` intrusive linked lists (`nextInScope_` links every node
70//!   created in a scope; `nextShadowed_` links a node to the same-key node it
71//!   shadows in an ancestor scope) become, per scope, a `Vec<Entry<K, V>>` in
72//!   insertion order (`ScopeData::entries`) plus a `shadowed: Option<Slot<K,
73//!   V>>` field on `Entry` (`Slot<K, V> = (Rc<ScopeData<K, V>>, usize)`) that
74//!   identifies the shadowed entry by `(scope, index)` instead of by raw
75//!   pointer. The map from key to innermost definition (C++ `map_:
76//!   DenseMap<K, Node *>`) becomes `map: RefCell<HashMap<K, Slot<K, V>>>`.
77//! - Because each key is inserted at most once per scope (`try_emplace`
78//!   refuses a second insertion into the same scope), the *order* in which a
79//!   scope's own entries are popped/pushed does not affect observable
80//!   behavior. This port walks `entries` forward (insertion order); the C++
81//!   walks `head_`/`nextInScope_`, which is reverse insertion order. Only the
82//!   internal traversal order differs — not behavior.
83//! - `lookup`, `find`, `find_with_depth`, and `find_in_current_scope` return
84//!   an owned `Option<V>` (hence the `V: Clone` bound) rather than C++'s
85//!   `V*` / default-constructed `V` (for `lookup`). Interior mutability
86//!   (`RefCell`) means we cannot hand back a reference into the map that
87//!   outlives the call, so callers get a clone instead. `count` still
88//!   returns `u32` (0 or 1), matching `DenseMap::count`'s semantics for a
89//!   unique key.
90//! - All `PersistentScopedMap` methods take `&self`: the map's mutable state
91//!   lives in `RefCell`s so it can be shared behind a plain reference, which
92//!   is what a scope-retaining API needs (a [`Scope`] borrows the map for
93//!   its whole lifetime while [`ScopePtr`]s to older scopes may outlive it).
94//!
95//! Every method below keeps the corresponding C++ comment (adapted) and the
96//! same assertions, expressed as `debug_assert!`/`assert!`.
97
98use std::cell::{Cell, RefCell};
99use std::collections::HashMap;
100use std::hash::Hash;
101use std::rc::Rc;
102
103/// A reference-counted handle to a scope's data. `Rc`'s strong count is the
104/// port of the C++ intrusive `refCount_`; see the module documentation.
105type ScopeRef<K, V> = Rc<ScopeData<K, V>>;
106
107/// Identifies one entry by the scope that owns it and its index within that
108/// scope's `entries`. The port of a raw `Node *`.
109type Slot<K, V> = (ScopeRef<K, V>, usize);
110
111/// A key/value pair declared in a scope. Analogous to C++
112/// `detail::PersistentScopedMapNode`, minus the intrusive `nextInScope_`
113/// pointer (this port keeps entries in a `Vec` on the owning scope instead).
114struct Entry<K, V> {
115    /// The declared key. Stored so that popping/pushing a scope can find the
116    /// key's slot in the map without also storing the value there.
117    key: K,
118    /// The declared value. Overwritten in place by `put`/`put_in_scope`.
119    value: V,
120    /// The `(scope, index)` this entry shadows in an ancestor scope, if any.
121    /// The port of `nextShadowed_`. Recomputed every time the owning scope
122    /// is pushed (initial insertion, or reactivation via `activate_scope`),
123    /// exactly like the C++ `pushEntry` overwriting `nextShadowed_`.
124    shadowed: Option<Slot<K, V>>,
125}
126
127/// This is the data for a scope. It is reference counted (via `Rc`). It
128/// contains the entries declared in the scope, and a pointer to the parent
129/// scope. Port of `detail::PersistentScopedMapScopeData`.
130struct ScopeData<K, V> {
131    /// Entries declared in this scope, in insertion order. Owned by the
132    /// scope (the port of the `head_`/`nextInScope_` linked list).
133    entries: RefCell<Vec<Entry<K, V>>>,
134    /// The scope we're shadowing.
135    parent: Option<ScopeRef<K, V>>,
136    /// Scope depth, starting from 0 for the outermost one.
137    depth: u32,
138    /// Whether this scope is active in the map or it has been popped.
139    active: Cell<bool>,
140}
141
142impl<K, V> Drop for ScopeData<K, V> {
143    fn drop(&mut self) {
144        // The C++ destructor also asserts `refCount_ == 0`, but with `Rc`
145        // that is guaranteed by construction: this destructor only runs
146        // once the strong count reaches zero.
147        debug_assert!(!self.active.get(), "Cannot destroy an active scope");
148    }
149}
150
151/// Smart pointer retaining ownership of a scope so it can be reactivated
152/// after it has been popped from the table. Port of
153/// `PersistentScopedMapScopePtr`; `Rc`'s automatic reference counting
154/// replaces the manual `addRef`/`decRef` pair.
155pub struct ScopePtr<K, V>(Option<ScopeRef<K, V>>);
156
157impl<K, V> ScopePtr<K, V> {
158    fn new(scope: ScopeRef<K, V>) -> Self {
159        ScopePtr(Some(scope))
160    }
161
162    /// Return true if this pointer is null.
163    pub fn is_null(&self) -> bool {
164        self.0.is_none()
165    }
166
167    /// Free the scope reference and set the pointer to null.
168    pub fn reset(&mut self) {
169        self.0 = None;
170    }
171
172    fn get(&self) -> Option<&ScopeRef<K, V>> {
173        self.0.as_ref()
174    }
175}
176
177impl<K, V> Clone for ScopePtr<K, V> {
178    fn clone(&self) -> Self {
179        // Not `#[derive(Clone)]`: derive would require `K: Clone, V: Clone`
180        // even though `Option<Rc<_>>::clone` never needs them.
181        ScopePtr(self.0.clone())
182    }
183}
184
185impl<K, V> Default for ScopePtr<K, V> {
186    fn default() -> Self {
187        // Not `#[derive(Default)]`, for the same reason as `Clone` above.
188        ScopePtr(None)
189    }
190}
191
192impl<K, V> PartialEq for ScopePtr<K, V> {
193    /// Pointer identity, like the C++ `operator==` (`ptr_ == other.ptr_`).
194    fn eq(&self, other: &Self) -> bool {
195        match (&self.0, &other.0) {
196            (Some(a), Some(b)) => Rc::ptr_eq(a, b),
197            (None, None) => true,
198            _ => false,
199        }
200    }
201}
202
203/// RAII for creating and popping a scope. Port of
204/// `PersistentScopedMapScope`.
205pub struct Scope<'m, K: Eq + Hash + Copy, V: Clone> {
206    base: &'m PersistentScopedMap<K, V>,
207    scope: ScopeRef<K, V>,
208}
209
210impl<'m, K: Eq + Hash + Copy, V: Clone> Scope<'m, K, V> {
211    /// Create (and activate) a new child scope of `base`'s current scope.
212    pub fn new(base: &'m PersistentScopedMap<K, V>) -> Self {
213        let parent = base.scope.borrow().clone();
214        let depth = parent.as_ref().map_or(0, |p| p.depth + 1);
215        let scope = Rc::new(ScopeData {
216            entries: RefCell::new(Vec::new()),
217            parent,
218            depth,
219            active: Cell::new(true),
220        });
221        *base.scope.borrow_mut() = Some(scope.clone());
222        Scope { base, scope }
223    }
224
225    /// \return the depth of the scope.
226    pub fn depth(&self) -> u32 {
227        self.scope.depth
228    }
229
230    /// Return a persistent pointer that retains ownership of the scope so it
231    /// can be reactivated after it has been popped.
232    pub fn ptr(&self) -> ScopePtr<K, V> {
233        ScopePtr::new(self.scope.clone())
234    }
235}
236
237impl<'m, K: Eq + Hash + Copy, V: Clone> Drop for Scope<'m, K, V> {
238    fn drop(&mut self) {
239        self.base.pop_scope(&self.scope);
240    }
241}
242
243/// Scoped hash table similar to `hermes::ScopedHashTable`, but scopes can be
244/// retained and reactivated after they have been popped from the table. See
245/// the module documentation for the full example and for the deviations
246/// from the C++ implementation.
247pub struct PersistentScopedMap<K, V> {
248    /// Maps from keys to the (scope, index) of the innermost definition.
249    map: RefCell<HashMap<K, Slot<K, V>>>,
250    /// The current scope.
251    scope: RefCell<Option<ScopeRef<K, V>>>,
252}
253
254impl<K: Eq + Hash + Copy, V: Clone> Default for PersistentScopedMap<K, V> {
255    fn default() -> Self {
256        Self::new()
257    }
258}
259
260impl<K, V> Drop for PersistentScopedMap<K, V> {
261    fn drop(&mut self) {
262        debug_assert!(
263            self.scope.borrow().is_none(),
264            "Scopes remain when destructing PersistentScopedMap"
265        );
266        debug_assert!(
267            self.map.borrow().is_empty(),
268            "Elements remaining in map without scope!"
269        );
270    }
271}
272
273impl<K: Eq + Hash + Copy, V: Clone> PersistentScopedMap<K, V> {
274    pub fn new() -> Self {
275        PersistentScopedMap {
276            map: RefCell::new(HashMap::new()),
277            scope: RefCell::new(None),
278        }
279    }
280
281    /// Return a pointer to the current scope. The pointer may be null.
282    pub fn current_scope(&self) -> ScopePtr<K, V> {
283        ScopePtr(self.scope.borrow().clone())
284    }
285
286    fn require_current(&self) -> ScopeRef<K, V> {
287        self.scope
288            .borrow()
289            .clone()
290            .expect("PersistentScopedMap has no current scope")
291    }
292
293    fn require_scope(ptr: &ScopePtr<K, V>) -> ScopeRef<K, V> {
294        ptr.get()
295            .cloned()
296            .expect("PersistentScopedMapScopePtr must not be null")
297    }
298
299    /// Push the specified node/entry to the top of the stack for its key.
300    /// `scope` is the scope the entry belongs to (used only for the debug
301    /// check); `key`/`idx` locate the entry in `scope.entries`. Port of
302    /// `pushEntry`.
303    fn push_entry(&self, scope: &ScopeRef<K, V>, key: K, idx: usize) {
304        let prev = self.map.borrow_mut().insert(key, (scope.clone(), idx));
305        if let Some((ref prev_scope, _)) = prev {
306            debug_assert!(
307                prev_scope.depth < scope.depth,
308                "Can't insert values under existing names"
309            );
310        }
311        scope.entries.borrow_mut()[idx].shadowed = prev;
312    }
313
314    /// Create a new entry and insert it into `scope`. Port of
315    /// `insertNewNode`. Returns the new entry's index in `scope.entries`.
316    fn insert_new_node(
317        &self,
318        scope: &ScopeRef<K, V>,
319        key: K,
320        value: V,
321    ) -> usize {
322        let idx = {
323            let mut entries = scope.entries.borrow_mut();
324            entries.push(Entry {
325                key,
326                value,
327                shadowed: None,
328            });
329            entries.len() - 1
330        };
331        self.push_entry(scope, key, idx);
332        idx
333    }
334
335    /// Unlinks the innermost entry for `key` (which must belong to `scope`,
336    /// at index `idx`) from the map, restoring whatever it shadowed. Port of
337    /// `popEntry`.
338    fn pop_entry(&self, scope: &ScopeRef<K, V>, key: K, idx: usize) {
339        let mut map = self.map.borrow_mut();
340        let current = map
341            .get(&key)
342            .cloned()
343            .expect("Asked to pop an empty scope value");
344        debug_assert!(
345            Rc::ptr_eq(&current.0, scope) && current.1 == idx,
346            "Unexpected innermost value for key"
347        );
348        let shadowed = scope.entries.borrow()[idx].shadowed.clone();
349        match shadowed {
350            Some(s) => {
351                map.insert(key, s);
352            }
353            None => {
354                map.remove(&key);
355            }
356        }
357    }
358
359    /// Unlinks all entries in `scope` from the hash map and marks it as
360    /// inactive. `scope` must be the current scope. Port of `popScope`.
361    fn pop_scope(&self, scope: &ScopeRef<K, V>) {
362        debug_assert!(
363            scope.active.get(),
364            "Attempting to pop an inactive scope"
365        );
366        {
367            let current = self.scope.borrow();
368            debug_assert!(
369                matches!(current.as_ref(), Some(c) if Rc::ptr_eq(c, scope)),
370                "Attempting to pop not current scope"
371            );
372        }
373
374        let len = scope.entries.borrow().len();
375        for idx in 0..len {
376            let key = scope.entries.borrow()[idx].key;
377            self.pop_entry(scope, key, idx);
378        }
379        scope.active.set(false);
380        *self.scope.borrow_mut() = scope.parent.clone();
381    }
382
383    /// Push the specified scope, which must be a child of the current
384    /// scope, into the hash map and activate it. Port of `pushChildScope`.
385    fn push_child_scope(&self, scope: &ScopeRef<K, V>) {
386        debug_assert!(
387            !scope.active.get(),
388            "Attempting to push an active scope"
389        );
390        {
391            let current = self.scope.borrow();
392            let is_child_of_current = match (&scope.parent, current.as_ref()) {
393                (Some(p), Some(c)) => Rc::ptr_eq(p, c),
394                (None, None) => true,
395                _ => false,
396            };
397            debug_assert!(
398                is_child_of_current,
399                "Attempting to push a scope that isn't a child of the \
400                 current one"
401            );
402        }
403
404        let len = scope.entries.borrow().len();
405        for idx in 0..len {
406            let key = scope.entries.borrow()[idx].key;
407            self.push_entry(scope, key, idx);
408        }
409        scope.active.set(true);
410        *self.scope.borrow_mut() = Some(scope.clone());
411    }
412
413    /// Attempt to insert an element into the specified scope. Semantics
414    /// equivalent to `std::map::try_emplace()`. Returns the entry's index in
415    /// `scope.entries` and whether the insertion took place.
416    /// A key may not be inserted such that it would be shadowed by another
417    /// scope currently in effect. Attempting to do so results in undefined
418    /// behavior.
419    fn try_emplace_into_scope_impl(
420        &self,
421        scope: &ScopeRef<K, V>,
422        key: K,
423        value: V,
424    ) -> (usize, bool) {
425        debug_assert!(
426            self.require_current().active.get(),
427            "Attempting to modify an inactive scope"
428        );
429        let existing = self.map.borrow().get(&key).cloned();
430        if let Some((ref existing_scope, existing_idx)) = existing {
431            if existing_scope.depth == scope.depth {
432                // The key exists in the current scope.
433                return (existing_idx, false);
434            }
435        }
436        // Otherwise, create a new entry in the current scope.
437        let idx = self.insert_new_node(scope, key, value);
438        (idx, true)
439    }
440
441    /// Attempt to insert an element into the specified scope. Returns
442    /// whether the insertion took place (`false` if `key` already has a
443    /// binding in `scope`). A key may not be inserted such that it would be
444    /// shadowed by another scope currently in effect. Attempting to do so
445    /// results in undefined behavior.
446    pub fn try_emplace_into_scope(
447        &self,
448        scope: &ScopePtr<K, V>,
449        key: K,
450        value: V,
451    ) -> bool {
452        let scope_rc = Self::require_scope(scope);
453        self.try_emplace_into_scope_impl(&scope_rc, key, value).1
454    }
455
456    /// Attempt to insert an element into the current scope. Returns whether
457    /// the insertion took place.
458    pub fn try_emplace(&self, key: K, value: V) -> bool {
459        let scope_rc = self.require_current();
460        self.try_emplace_into_scope_impl(&scope_rc, key, value).1
461    }
462
463    /// Insert or update a value in the specified scope. A key may not be
464    /// inserted such that it would be shadowed by another scope currently in
465    /// effect. Attempting to do so results in undefined behavior.
466    pub fn put_in_scope(&self, scope: &ScopePtr<K, V>, key: K, value: V) {
467        let scope_rc = Self::require_scope(scope);
468        let (idx, inserted) = self.try_emplace_into_scope_impl(
469            &scope_rc,
470            key,
471            value.clone(),
472        );
473        if !inserted {
474            scope_rc.entries.borrow_mut()[idx].value = value;
475        }
476    }
477
478    /// Insert or update an existing value in the current scope.
479    pub fn put(&self, key: K, value: V) {
480        let current = self.current_scope();
481        self.put_in_scope(&current, key, value);
482    }
483
484    /// Returns 1 if the value is defined, 0 if it's not.
485    pub fn count(&self, key: &K) -> u32 {
486        u32::from(self.map.borrow().contains_key(key))
487    }
488
489    /// Gets the innermost value for a key, or `None` if none.
490    pub fn lookup(&self, key: &K) -> Option<V> {
491        self.find(key)
492    }
493
494    /// Return the innermost value for a key, or `None` if none.
495    pub fn find(&self, key: &K) -> Option<V> {
496        let map = self.map.borrow();
497        let (scope, idx) = map.get(key)?;
498        let value = scope.entries.borrow()[*idx].value.clone();
499        Some(value)
500    }
501
502    /// \return the innermost value for a key along with its depth, or `None`
503    /// if none.
504    pub fn find_with_depth(&self, key: &K) -> Option<(V, u32)> {
505        let map = self.map.borrow();
506        let (scope, idx) = map.get(key)?;
507        let value = scope.entries.borrow()[*idx].value.clone();
508        Some((value, scope.depth))
509    }
510
511    /// \return the value for a key if it exists in the current scope, or
512    /// `None` if none.
513    pub fn find_in_current_scope(&self, key: &K) -> Option<V> {
514        let map = self.map.borrow();
515        let (scope, idx) = map.get(key)?;
516        let current_depth = self.require_current().depth;
517        // Result is not in the current scope.
518        if scope.depth != current_depth {
519            return None;
520        }
521        let value = scope.entries.borrow()[*idx].value.clone();
522        Some(value)
523    }
524
525    pub fn activate_scope(&self, new_scope_ptr: &ScopePtr<K, V>) {
526        let new_scope = Self::require_scope(new_scope_ptr);
527        // We need to find the closest active parent of newScope. Then we
528        // need to deactivate and pop all scopes between the current scope
529        // and that parent. Finally, we need to push and activate all scopes
530        // between newScope and the parent.
531
532        // Keep track of scopes that need to be activated in reverse order.
533        let mut activate_list: Vec<ScopeRef<K, V>> = Vec::new();
534        let mut active_parent = Some(new_scope);
535        while let Some(candidate) = active_parent {
536            if candidate.active.get() {
537                active_parent = Some(candidate);
538                break;
539            }
540            active_parent = candidate.parent.clone();
541            activate_list.push(candidate);
542        }
543
544        // Deactivate and pop all scopes between scope_ and active_parent.
545        loop {
546            let current = self.scope.borrow().clone();
547            let reached = match (&current, &active_parent) {
548                (Some(c), Some(a)) => Rc::ptr_eq(c, a),
549                (None, None) => true,
550                _ => false,
551            };
552            if reached {
553                break;
554            }
555            let current = current.expect("ran out of scopes to pop");
556            self.pop_scope(&current);
557        }
558
559        // Push and activate the scopes in activate_list in reverse order
560        // (starting from the topmost).
561        for scope in activate_list.into_iter().rev() {
562            self.push_child_scope(&scope);
563        }
564    }
565
566    /// Gets all values currently in scope. Port of the `UNIT_TEST`-only
567    /// `test_flatten`; kept `pub` (not test-gated) since the resolver's own
568    /// tests use it too.
569    pub fn flatten(&self) -> HashMap<K, V> {
570        let map = self.map.borrow();
571        let mut result = HashMap::with_capacity(map.len());
572        for (key, (scope, idx)) in map.iter() {
573            result.insert(*key, scope.entries.borrow()[*idx].value.clone());
574        }
575        result
576    }
577
578    /// Gets keys in each scope. This may correspond to a `ScopeChain`.
579    /// Shadowed keys are ignored. Index 0 is innermost. Port of the
580    /// `UNIT_TEST`-only `test_getKeysByScope`; kept `pub` for the same
581    /// reason as `flatten`.
582    pub fn keys_by_scope(&self) -> Vec<Vec<K>> {
583        let current = self.require_current();
584        let size = (current.depth + 1) as usize;
585        let mut result: Vec<Vec<K>> = vec![Vec::new(); size];
586
587        for (key, (scope, _)) in self.map.borrow().iter() {
588            debug_assert!(scope.depth <= current.depth, "Node at bad depth");
589            result[size - scope.depth as usize - 1].push(*key);
590        }
591        result
592    }
593}
594
595#[cfg(test)]
596mod tests {
597    use super::*;
598
599    // Ported from unittests/ADT/PersistentScopedMapTest.cpp.
600
601    #[test]
602    fn smoke_test() {
603        let table: PersistentScopedMap<&str, &str> = PersistentScopedMap::new();
604        let scope = Scope::new(&table);
605        table.try_emplace("foo", "bar");
606        assert_eq!(table.lookup(&"foo"), Some("bar"));
607        drop(scope);
608    }
609
610    #[test]
611    fn nesting() {
612        let table: PersistentScopedMap<&str, &str> = PersistentScopedMap::new();
613        let outer = Scope::new(&table);
614        table.try_emplace("key", "outer");
615        assert_eq!(table.lookup(&"key"), Some("outer"));
616        {
617            let _inner = Scope::new(&table);
618            table.try_emplace("key", "inner");
619            assert_eq!(table.lookup(&"key"), Some("inner"));
620        }
621        assert_eq!(table.lookup(&"key"), Some("outer"));
622        drop(outer);
623    }
624
625    #[test]
626    fn overwrite() {
627        let table: PersistentScopedMap<&str, &str> = PersistentScopedMap::new();
628        let outer = Scope::new(&table);
629        table.put("key", "foo");
630        assert_eq!(table.lookup(&"key"), Some("foo"));
631        table.put("key", "outer");
632        assert_eq!(table.lookup(&"key"), Some("outer"));
633        {
634            let _inner = Scope::new(&table);
635            table.put("key", "foo");
636            assert_eq!(table.lookup(&"key"), Some("foo"));
637            table.put("key", "inner");
638            assert_eq!(table.lookup(&"key"), Some("inner"));
639        }
640        assert_eq!(table.lookup(&"key"), Some("outer"));
641        drop(outer);
642    }
643
644    #[test]
645    fn flatten_test() {
646        let table: PersistentScopedMap<&str, &str> = PersistentScopedMap::new();
647        let outer = Scope::new(&table);
648        table.try_emplace("out", "outer");
649        {
650            let _inner = Scope::new(&table);
651            table.try_emplace("in", "inner");
652            let map = table.flatten();
653            assert_eq!(map.len(), 2);
654            assert_eq!(map.get("out"), Some(&"outer"));
655            assert_eq!(map.get("in"), Some(&"inner"));
656        }
657        drop(outer);
658    }
659
660    #[test]
661    fn get_keys_by_scope() {
662        let table: PersistentScopedMap<&str, &str> = PersistentScopedMap::new();
663        let outer = Scope::new(&table);
664        table.try_emplace("out", "outer");
665        table.try_emplace("in", "trash");
666        {
667            let _inner = Scope::new(&table);
668            table.try_emplace("in", "inner");
669            let scopes = table.keys_by_scope();
670            assert_eq!(scopes.len(), 2);
671            assert_eq!(scopes[0].len(), 1);
672            assert_eq!(scopes[1].len(), 1);
673            assert_eq!(scopes[0][0], "in");
674            assert_eq!(scopes[1][0], "out");
675        }
676        drop(outer);
677    }
678
679    #[test]
680    fn put_test() {
681        let table: PersistentScopedMap<&str, &str> = PersistentScopedMap::new();
682        let outer = Scope::new(&table);
683        table.try_emplace("foo", "true");
684        {
685            let _inner = Scope::new(&table);
686            assert_eq!(table.lookup(&"foo"), Some("true"));
687            table.try_emplace("foo", "false");
688            assert_eq!(table.lookup(&"foo"), Some("false"));
689            table.try_emplace("foo", "true");
690            assert_eq!(table.lookup(&"foo"), Some("false"));
691            table.put("foo", "false");
692            assert_eq!(table.lookup(&"foo"), Some("false"));
693        }
694        assert_eq!(table.lookup(&"foo"), Some("true"));
695        drop(outer);
696    }
697
698    #[test]
699    fn find_in_current_scope() {
700        let table: PersistentScopedMap<&str, &str> = PersistentScopedMap::new();
701        let outer = Scope::new(&table);
702        table.try_emplace("foo", "true");
703        {
704            let _inner = Scope::new(&table);
705            table.try_emplace("bar", "true");
706            assert_eq!(table.find_in_current_scope(&"foo"), None);
707            assert_eq!(table.find_in_current_scope(&"bar"), Some("true"));
708        }
709        assert_eq!(table.find_in_current_scope(&"foo"), Some("true"));
710        drop(outer);
711    }
712
713    #[test]
714    fn activate() {
715        let mut ptr: ScopePtr<&str, &str>;
716        let table: PersistentScopedMap<&str, &str> = PersistentScopedMap::new();
717        {
718            let a = Scope::new(&table);
719            table.try_emplace("A", "a");
720            table.try_emplace("key", "keyA");
721            let b = Scope::new(&table);
722            table.try_emplace("B", "b");
723            table.try_emplace("key", "keyB");
724            {
725                let c = Scope::new(&table);
726                table.try_emplace("C", "c");
727                table.try_emplace("key", "keyC");
728                let d = Scope::new(&table);
729                table.try_emplace("D", "d");
730                table.try_emplace("key", "keyD");
731                ptr = d.ptr();
732
733                {
734                    let map = table.flatten();
735                    assert_eq!(map.len(), 5);
736                    assert_eq!(map.get("A"), Some(&"a"));
737                    assert_eq!(map.get("B"), Some(&"b"));
738                    assert_eq!(map.get("C"), Some(&"c"));
739                    assert_eq!(map.get("D"), Some(&"d"));
740                    assert_eq!(map.get("key"), Some(&"keyD"));
741                }
742                drop(d);
743                drop(c);
744            }
745            {
746                let map = table.flatten();
747                assert_eq!(map.len(), 3);
748                assert_eq!(map.get("A"), Some(&"a"));
749                assert_eq!(map.get("B"), Some(&"b"));
750                assert_eq!(map.get("key"), Some(&"keyB"));
751            }
752            let e = Scope::new(&table);
753            table.try_emplace("E", "e");
754            table.try_emplace("key", "keyE");
755            let f = Scope::new(&table);
756            table.try_emplace("F", "f");
757            table.try_emplace("key", "keyF");
758            let g = Scope::new(&table);
759            table.try_emplace("G", "g");
760            table.try_emplace("key", "keyG");
761            //                         -> C->D
762            //                       /
763            //   A(active)->B(active)
764            //                       \
765            //                         -> E(active)->F(active)->G(active)
766            {
767                let map = table.flatten();
768                assert_eq!(map.len(), 6);
769                assert_eq!(map.get("A"), Some(&"a"));
770                assert_eq!(map.get("B"), Some(&"b"));
771                assert_eq!(map.get("E"), Some(&"e"));
772                assert_eq!(map.get("F"), Some(&"f"));
773                assert_eq!(map.get("G"), Some(&"g"));
774                assert_eq!(map.get("key"), Some(&"keyG"));
775            }
776
777            table.activate_scope(&e.ptr());
778            {
779                let map = table.flatten();
780                assert_eq!(map.len(), 4);
781                assert_eq!(map.get("A"), Some(&"a"));
782                assert_eq!(map.get("B"), Some(&"b"));
783                assert_eq!(map.get("E"), Some(&"e"));
784                assert_eq!(map.get("key"), Some(&"keyE"));
785            }
786
787            table.activate_scope(&f.ptr());
788            {
789                let map = table.flatten();
790                assert_eq!(map.len(), 5);
791                assert_eq!(map.get("A"), Some(&"a"));
792                assert_eq!(map.get("B"), Some(&"b"));
793                assert_eq!(map.get("E"), Some(&"e"));
794                assert_eq!(map.get("F"), Some(&"f"));
795                assert_eq!(map.get("key"), Some(&"keyF"));
796            }
797            table.activate_scope(&g.ptr());
798            {
799                let map = table.flatten();
800                assert_eq!(map.len(), 6);
801                assert_eq!(map.get("A"), Some(&"a"));
802                assert_eq!(map.get("B"), Some(&"b"));
803                assert_eq!(map.get("E"), Some(&"e"));
804                assert_eq!(map.get("F"), Some(&"f"));
805                assert_eq!(map.get("G"), Some(&"g"));
806                assert_eq!(map.get("key"), Some(&"keyG"));
807            }
808
809            // Reactivate D
810            table.activate_scope(&ptr);
811            {
812                let map = table.flatten();
813                assert_eq!(map.len(), 5);
814                assert_eq!(map.get("A"), Some(&"a"));
815                assert_eq!(map.get("B"), Some(&"b"));
816                assert_eq!(map.get("C"), Some(&"c"));
817                assert_eq!(map.get("D"), Some(&"d"));
818                assert_eq!(map.get("key"), Some(&"keyD"));
819            }
820
821            table.activate_scope(&g.ptr());
822            {
823                let map = table.flatten();
824                assert_eq!(map.len(), 6);
825                assert_eq!(map.get("A"), Some(&"a"));
826                assert_eq!(map.get("B"), Some(&"b"));
827                assert_eq!(map.get("E"), Some(&"e"));
828                assert_eq!(map.get("F"), Some(&"f"));
829                assert_eq!(map.get("G"), Some(&"g"));
830                assert_eq!(map.get("key"), Some(&"keyG"));
831            }
832            drop(g);
833            drop(f);
834            drop(e);
835            drop(b);
836            drop(a);
837        }
838        // This location is used to check with a debugger whether all scopes
839        // have been freed.
840        ptr.reset();
841    }
842
843    #[test]
844    fn count_and_find() {
845        let table: PersistentScopedMap<&str, &str> = PersistentScopedMap::new();
846        let outer = Scope::new(&table);
847        assert_eq!(table.count(&"foo"), 0);
848        table.try_emplace("foo", "true");
849        assert_eq!(table.count(&"foo"), 1);
850        assert_eq!(table.find(&"foo"), Some("true"));
851        assert_eq!(table.find_with_depth(&"foo"), Some(("true", 0)));
852        {
853            let _inner = Scope::new(&table);
854            table.try_emplace("foo", "inner");
855            assert_eq!(table.find_with_depth(&"foo"), Some(("inner", 1)));
856        }
857        assert_eq!(table.find_with_depth(&"foo"), Some(("true", 0)));
858        drop(outer);
859    }
860
861    /// Rust-specific: dropping the last `ScopePtr` of a *popped* scope frees
862    /// it (running `ScopeData`'s `Drop`, which asserts the scope is
863    /// inactive) without touching the map, and a later `activate_scope` of a
864    /// different retained sibling still works.
865    #[test]
866    fn drop_last_ptr_of_popped_scope_does_not_disturb_siblings() {
867        let table: PersistentScopedMap<&str, &str> = PersistentScopedMap::new();
868        let outer = Scope::new(&table);
869        table.try_emplace("A", "a");
870
871        let mut b_ptr: ScopePtr<&str, &str>;
872        {
873            let b = Scope::new(&table);
874            table.try_emplace("B", "b");
875            b_ptr = b.ptr();
876            // `b` dropped here: scope B is popped but kept alive by
877            // `b_ptr`.
878        }
879        assert_eq!(table.lookup(&"B"), None);
880
881        // Drop the last reference to the (already popped, inactive) scope
882        // B. This must not panic and must not touch the map: it is purely
883        // an `Rc`/`ScopeData` deallocation.
884        b_ptr.reset();
885
886        let c_ptr;
887        {
888            // C is a sibling of (the now-freed) B, also a child of outer.
889            let c = Scope::new(&table);
890            table.try_emplace("C", "c");
891            c_ptr = c.ptr();
892        }
893
894        // A different retained sibling can still be activated normally.
895        table.activate_scope(&c_ptr);
896        assert_eq!(table.lookup(&"A"), Some("a"));
897        assert_eq!(table.lookup(&"C"), Some("c"));
898        assert_eq!(table.lookup(&"B"), None);
899
900        // Restore to `outer` so it can be popped cleanly by its `Drop`.
901        table.activate_scope(&outer.ptr());
902        drop(outer);
903    }
904}