pub struct Scope {
pub parent: Option<Arc<Scope>>,
pub path_node: Option<Arc<str>>,
pub locals: Locals,
pub current_dir: Arc<str>,
pub cache_namespace: Arc<str>,
pub root_ref: Option<RootRef>,
pub list_context: Option<Arc<ListContext>>,
pub thunks: Thunks,
}Expand description
Single environment frame. Cheap to derive (every field is either
Clone or Arc-shared) and wrapped in Arc<Scope> at every call
site so backtracking through parent stays copy-free.
locals and thunks are kept as Mutex<HashMap> rather than
raw HashMap so multiple evaluator threads can share an embedder
scope (e.g. the empty root scope) without external locking. The
inner HashMap is constructed empty (HashMap::new() does not
allocate until first insert), so hot-loop children that never
register a binding pay no heap cost for these fields beyond the
inline mutex word and the Scope’s own Arc allocation.
Fields§
§parent: Option<Arc<Scope>>Enclosing scope. None only at the document root.
path_node: Option<Arc<str>>Most-recent path segment opened by Scope::with_path /
Scope::with_list_context; &sibling / &uncle peel these off when
rebuilding the relative target path.
locals: LocalsBindings introduced inside this frame (closure params, comprehension
loop vars, where clauses, imported aliases).
current_dir: Arc<str>Working directory used when resolving relative #import paths.
Wrapped in Arc<str> so child-scope construction (which clones
the field on every list / dict / closure descent) is a refcount
bump rather than a heap String alloc.
cache_namespace: Arc<str>Stable namespace for the path cache; usually the canonical id of the
surrounding module so different modules can’t collide on identical
paths. Same Arc<str> reasoning as current_dir.
root_ref: Option<RootRef>&root anchor. None only at scopes that haven’t yet acquired one
(typically just the pre-eval root scope before the evaluator’s
eval_root stamps it). See RootRef for invariants.
list_context: Option<Arc<ListContext>>Active list iteration, if any.
thunks: ThunksLazily-resolved bindings for the dict that owns this scope.
Backends register and force entries through the public helpers
below (Scope::thunks_for_write / Scope::get_thunk).
Implementations§
Source§impl Scope
impl Scope
Sourcepub fn get_local(&self, name: &str) -> Option<Value>
pub fn get_local(&self, name: &str) -> Option<Value>
Look up name in this scope’s locals, walking up parent chain.
Hot path optimization: before the HashMap lookup, peek at
list_context.iter_binding. Comprehension hot loops park the
active for x in xs binding there (no HashMap allocation per
element), so x resolves with a single Mutex peek instead of
going through locals.
Sourcepub fn current_iter_binding(&self) -> Option<(Arc<str>, Value)>
pub fn current_iter_binding(&self) -> Option<(Arc<str>, Value)>
Snapshot the active iter binding, if any, into a (name, value)
pair. Closure construction calls this so the captured environment
holds a value, not a pointer into a mutable iter slot that a
later iteration would clobber.
Sourcepub fn get_thunk(&self, name: &str) -> Option<Arc<Thunk>>
pub fn get_thunk(&self, name: &str) -> Option<Arc<Thunk>>
Look up a lazy thunk in this scope’s own table, walking up the
parent chain on miss.
Sourcepub fn get_own_thunk(&self, name: &str) -> Option<Arc<Thunk>>
pub fn get_own_thunk(&self, name: &str) -> Option<Arc<Thunk>>
Look up a lazy thunk in this scope’s own table only (no parent chain). Used by the evaluator when validating that a key belongs to the immediate dict frame.
Sourcepub fn locals_for_write(&self) -> MutexGuard<'_, HashMap<Arc<str>, Value>>
pub fn locals_for_write(&self) -> MutexGuard<'_, HashMap<Arc<str>, Value>>
Acquire a write lock on this scope’s locals.
Centralizes every write-side scope.locals.lock().unwrap() so
the locking strategy can evolve (e.g. swap to a lock-free or
thread-local representation under a feature) without touching
every call site. Read paths still go through
Scope::get_local which walks the parent chain.
Keys are Arc<str>; callers writing a String should hand it
over via Arc::<str>::from(name) so the buffer moves into the
Arc allocation once, instead of paying a String::clone on
every rebind.
Sourcepub fn thunks_for_write(&self) -> MutexGuard<'_, HashMap<Arc<str>, Arc<Thunk>>>
pub fn thunks_for_write(&self) -> MutexGuard<'_, HashMap<Arc<str>, Arc<Thunk>>>
Same as Scope::locals_for_write but for the dict thunks table.
Sourcepub fn full_path(&self) -> Vec<String>
pub fn full_path(&self) -> Vec<String>
Reconstruct the path from the document root to the current scope by
walking parent pointers and collecting path_node segments.
Sourcepub fn path_cache_key(&self, path: &[String]) -> String
pub fn path_cache_key(&self, path: &[String]) -> String
Build a stable cache key for path under this scope’s namespace.
Sourcepub fn child(self: &Arc<Self>) -> Arc<Self>
pub fn child(self: &Arc<Self>) -> Arc<Self>
Open a fresh child frame inheriting flow-state fields (current_dir,
cache_namespace, root_ref, list_context) from self but
with empty locals/thunks and no path_node.
This is the workhorse for every new lexical block — Dict body,
comprehension iteration, closure body. The with_* methods below all
build on top of it and only differ by which field they override.
The empty Mutex<HashMap> pair doesn’t touch the heap until
somebody actually inserts a binding (zero-capacity HashMap +
inline mutex), so the comprehension hot loop only pays for the
Scope’s own Arc allocation.
Sourcepub fn with_local(
self: &Arc<Self>,
name: impl Into<Arc<str>>,
val: Value,
) -> Arc<Self>
pub fn with_local( self: &Arc<Self>, name: impl Into<Arc<str>>, val: Value, ) -> Arc<Self>
Bind a single name -> val in a fresh child frame.
Accepts anything that can be cheaply turned into an Arc<str>
(a String, a &str, or an already-shared Arc<str>) so hot
paths can hand the key over without an extra clone. Callers
that already hold an Arc<str> should pass Arc::clone(&id)
to skip the heap copy entirely.
pub fn with_locals( self: &Arc<Self>, new_locals: HashMap<Arc<str>, Value>, ) -> Arc<Self>
pub fn with_path(self: &Arc<Self>, node: impl Into<Arc<str>>) -> Arc<Self>
pub fn with_list_context( self: &Arc<Self>, index: usize, elements: Arc<[Arc<Thunk>]>, ) -> Arc<Self>
Sourcepub fn with_iter_loop(
self: &Arc<Self>,
elements: Arc<[Arc<Thunk>]>,
) -> Arc<Self>
pub fn with_iter_loop( self: &Arc<Self>, elements: Arc<[Arc<Thunk>]>, ) -> Arc<Self>
Open the outer frame for a comprehension’s hot loop.
Unlike Scope::with_list_context, this builds the frame once
per comprehension. The caller then drives the loop with
Scope::set_iter_binding which updates the binding + index in
place — no per-element Arc<Scope> allocation, eliminating the
48 MB / 200 K-block hot site flagged by P1-B.
Note: the returned Arc<Scope> is mutated through the inner
Mutexes on ListContext. Callers must NOT alias this scope as
a closure’s captured environment without first snapshotting the
binding via Scope::current_iter_binding (see the Expr::Closure
branch in eval.rs).
Sourcepub fn set_iter_binding(&self, name: Arc<str>, value: Value, index: usize)
pub fn set_iter_binding(&self, name: Arc<str>, value: Value, index: usize)
Refresh the named iter binding + index on a comprehension outer
frame previously built by Scope::with_iter_loop.
O(1): one Mutex lock and one Value clone — no heap allocation
for the scope frame itself. The path node is rewritten too so
full_path() reports the right segment if the body asks for it.
Trait Implementations§
Auto Trait Implementations§
impl !Freeze for Scope
impl RefUnwindSafe for Scope
impl Send for Scope
impl Sync for Scope
impl Unpin for Scope
impl UnsafeUnpin for Scope
impl UnwindSafe for Scope
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more