Skip to main content

sim_kernel/env/
core.rs

1use std::sync::{
2    Arc,
3    atomic::{AtomicU64, Ordering},
4};
5
6use crate::{
7    ContentId,
8    capability::{
9        CapabilityName, CapabilitySet, GrantSeat, macro_expansion_capability_for_phase,
10        read_construct_capability,
11    },
12    control::{ControlPolicy, ControlPolicyRef, NoopControlPolicy},
13    datum_store::BTreeDatumStore,
14    effect_ledger::EffectLedger,
15    error::{Diagnostic, Error, Result},
16    eval::{EvalPolicy, EvalPolicyRef, MacroExpanderRef, Phase},
17    expr::{Expr, SourceRegistry},
18    fact_store::{BTreeFactStore, FactStore},
19    factory::Factory,
20    handle_store::BTreeHandleStore,
21    id::{LibId, Symbol},
22    library::{LoadCx, Registry},
23    list::ListRegistry,
24    number_domain::PromotionSearchLimits,
25    object::Args,
26    table::TableRegistry,
27    value::Value,
28};
29
30use super::{Diagnostics, Env, load_ledger::LibLoadLedger};
31
32/// Monotonic source of per-context grant ids. Only used to bind a [`GrantSeat`]
33/// to the context it was minted with; the value is never observable in output,
34/// so it does not affect evaluation determinism.
35static NEXT_GRANT_ID: AtomicU64 = AtomicU64::new(1);
36
37fn unknown_symbol(symbol: &Symbol) -> Error {
38    Error::UnknownSymbol {
39        symbol: symbol.clone(),
40    }
41}
42
43fn class_protocol(value: &Value) -> Result<&dyn crate::Class> {
44    value.object().as_class().ok_or(Error::TypeMismatch {
45        expected: "class",
46        found: "non-class",
47    })
48}
49
50fn read_constructor_protocol(value: &Value) -> Result<&dyn crate::ReadConstructor> {
51    value
52        .object()
53        .as_read_constructor()
54        .ok_or(Error::TypeMismatch {
55            expected: "read-constructor",
56            found: "non-read-constructor",
57        })
58}
59
60/// The capability state of a [`Cx`]; an alias for [`CapabilitySet`].
61pub type Capabilities = CapabilitySet;
62
63/// The evaluation context threaded through every checked call.
64///
65/// `Cx` bundles the registry handle, factory, capability set, eval and control
66/// policies, the data substrate (datum/fact/handle stores and ledgers), and the
67/// diagnostic sink. The kernel defines this context; libraries supply the
68/// behavior reached through it (registered classes, functions, number domains,
69/// list/table backends, and so on). See the README sections "Library system"
70/// and "Capabilities and trust".
71pub struct Cx {
72    /// Per-context identity that binds a [`GrantSeat`] to exactly this context.
73    /// Minted at construction; a seat can only grant into the context it shares
74    /// this id with, so minting a fresh `(Cx, GrantSeat)` pair cannot be used to
75    /// escalate a different context.
76    grant_id: u64,
77    env: Env,
78    diagnostics: Diagnostics,
79    capabilities: Capabilities,
80    eval_policy: EvalPolicyRef,
81    macro_expander: Option<MacroExpanderRef>,
82    factory: Arc<dyn Factory>,
83    pub(crate) registry: Registry,
84    list_registry: ListRegistry,
85    table_registry: TableRegistry,
86    promotion_search_limits: PromotionSearchLimits,
87    sources: SourceRegistry,
88    pub(super) datum_store: BTreeDatumStore,
89    handles: BTreeHandleStore,
90    pub(super) facts: BTreeFactStore,
91    pub(super) lib_load_ledger: LibLoadLedger,
92    effect_ledger: EffectLedger,
93    control_policy: ControlPolicyRef,
94}
95
96impl Cx {
97    /// Builds a fresh context with the given eval policy and factory.
98    /// The registry, capability set, and stores start empty (boot claims aside);
99    /// libraries register behavior into the returned context.
100    /// `handle_seed` is supplied by the runtime boundary and namespaces every
101    /// live handle allocated by this context.
102    pub fn new(
103        eval_policy: EvalPolicyRef,
104        factory: Arc<dyn Factory>,
105        handle_seed: crate::HandleSeed,
106    ) -> Self {
107        let mut datum_store = BTreeDatumStore::default();
108        let mut facts = BTreeFactStore::default();
109        facts.insert_boot_claims(&mut datum_store);
110
111        Self {
112            grant_id: NEXT_GRANT_ID.fetch_add(1, Ordering::Relaxed),
113            env: Env::default(),
114            diagnostics: Diagnostics::default(),
115            capabilities: Capabilities::default(),
116            eval_policy,
117            macro_expander: None,
118            factory,
119            registry: Registry::default(),
120            list_registry: ListRegistry::default(),
121            table_registry: TableRegistry::default(),
122            promotion_search_limits: PromotionSearchLimits::default(),
123            sources: SourceRegistry::default(),
124            datum_store,
125            handles: BTreeHandleStore::new(handle_seed),
126            facts,
127            lib_load_ledger: LibLoadLedger::default(),
128            effect_ledger: EffectLedger::default(),
129            control_policy: Arc::new(NoopControlPolicy),
130        }
131    }
132
133    /// Allocates a handle from this context's caller-seeded sequence.
134    pub fn fresh_handle(&mut self) -> crate::HandleId {
135        self.handles.fresh_handle()
136    }
137
138    /// Returns the active lexical environment.
139    pub fn env(&self) -> &Env {
140        &self.env
141    }
142
143    /// Returns the active lexical environment mutably.
144    pub fn env_mut(&mut self) -> &mut Env {
145        &mut self.env
146    }
147
148    /// Runs `f` with `env` installed as the active environment, then restores it.
149    pub fn with_env<T>(&mut self, env: Env, f: impl FnOnce(&mut Self) -> Result<T>) -> Result<T> {
150        let saved = std::mem::replace(&mut self.env, env);
151        let result = f(self);
152        self.env = saved;
153        result
154    }
155
156    /// Returns the active object [`Factory`].
157    pub fn factory(&self) -> &dyn Factory {
158        self.factory.as_ref()
159    }
160
161    /// Returns a shared handle to the active object factory.
162    pub fn factory_ref(&self) -> Arc<dyn Factory> {
163        self.factory.clone()
164    }
165
166    /// Runs `f` with `factory` installed as the active factory, then restores it.
167    pub fn with_factory<T>(
168        &mut self,
169        factory: Arc<dyn Factory>,
170        f: impl FnOnce(&mut Self) -> Result<T>,
171    ) -> Result<T> {
172        let saved = std::mem::replace(&mut self.factory, factory);
173        let result = f(self);
174        self.factory = saved;
175        result
176    }
177
178    /// Returns the behavior [`Registry`].
179    pub fn registry(&self) -> &Registry {
180        &self.registry
181    }
182
183    /// Returns the behavior registry mutably, for registering exports.
184    pub fn registry_mut(&mut self) -> &mut Registry {
185        &mut self.registry
186    }
187
188    /// Returns the registered list backend.
189    pub fn list_registry(&self) -> &ListRegistry {
190        &self.list_registry
191    }
192
193    /// Returns the list backend mutably.
194    pub fn list_registry_mut(&mut self) -> &mut ListRegistry {
195        &mut self.list_registry
196    }
197
198    /// Returns the registered table backend.
199    pub fn table_registry(&self) -> &TableRegistry {
200        &self.table_registry
201    }
202
203    /// Returns the table backend mutably.
204    pub fn table_registry_mut(&mut self) -> &mut TableRegistry {
205        &mut self.table_registry
206    }
207
208    /// Runs `f` with `registry` installed as the active registry, then restores it.
209    pub fn with_registry<T>(
210        &mut self,
211        registry: Registry,
212        f: impl FnOnce(&mut Self) -> Result<T>,
213    ) -> Result<T> {
214        let saved = std::mem::replace(&mut self.registry, registry);
215        let result = f(self);
216        self.registry = saved;
217        result
218    }
219
220    /// Builds a list value through the registered list backend.
221    pub fn new_list(&mut self, items: Vec<Value>) -> Result<Value> {
222        let registry = std::mem::take(&mut self.list_registry);
223        let result = registry.new_list(self, items);
224        self.list_registry = registry;
225        result
226    }
227
228    /// Builds a cons cell through the registered list backend.
229    pub fn new_cons(&mut self, car: Value, cdr: Value) -> Result<Value> {
230        let registry = std::mem::take(&mut self.list_registry);
231        let result = registry.new_cons(self, car, cdr);
232        self.list_registry = registry;
233        result
234    }
235
236    /// Builds a table value through the registered table backend.
237    pub fn new_table(&mut self, entries: Vec<(Symbol, Value)>) -> Result<Value> {
238        let registry = std::mem::take(&mut self.table_registry);
239        let result = registry.new_table(self, entries);
240        self.table_registry = registry;
241        result
242    }
243
244    /// Returns the source registry.
245    pub fn sources(&self) -> &SourceRegistry {
246        &self.sources
247    }
248
249    /// Returns the source registry mutably.
250    pub fn sources_mut(&mut self) -> &mut SourceRegistry {
251        &mut self.sources
252    }
253
254    /// Returns the datum store.
255    pub fn datum_store(&self) -> &BTreeDatumStore {
256        &self.datum_store
257    }
258
259    /// Returns the datum store mutably.
260    pub fn datum_store_mut(&mut self) -> &mut BTreeDatumStore {
261        &mut self.datum_store
262    }
263
264    /// Returns the handle store.
265    pub fn handles(&self) -> &BTreeHandleStore {
266        &self.handles
267    }
268
269    /// Returns the handle store mutably.
270    pub fn handles_mut(&mut self) -> &mut BTreeHandleStore {
271        &mut self.handles
272    }
273
274    /// Returns the fact store.
275    pub fn facts(&self) -> &BTreeFactStore {
276        &self.facts
277    }
278
279    /// Returns the fact store mutably.
280    pub fn facts_mut(&mut self) -> &mut BTreeFactStore {
281        &mut self.facts
282    }
283
284    /// Returns the effect ledger.
285    pub fn effect_ledger(&self) -> &EffectLedger {
286        &self.effect_ledger
287    }
288
289    /// Returns the effect ledger mutably.
290    pub fn effect_ledger_mut(&mut self) -> &mut EffectLedger {
291        &mut self.effect_ledger
292    }
293
294    /// Returns the active control policy.
295    pub fn control_policy(&self) -> &dyn ControlPolicy {
296        self.control_policy.as_ref()
297    }
298
299    /// Returns a shared handle to the active control policy.
300    pub fn control_policy_ref(&self) -> ControlPolicyRef {
301        self.control_policy.clone()
302    }
303
304    /// Returns the name of the active control policy.
305    pub fn control_policy_name(&self) -> &'static str {
306        self.control_policy.name()
307    }
308
309    /// Replaces the active control policy.
310    pub fn set_control_policy(&mut self, control_policy: ControlPolicyRef) {
311        self.control_policy = control_policy;
312    }
313
314    pub(crate) fn with_effect_ledger<T>(
315        &mut self,
316        f: impl FnOnce(&mut Self, &mut EffectLedger) -> Result<T>,
317    ) -> Result<T> {
318        let mut ledger = std::mem::take(&mut self.effect_ledger);
319        let result = f(self, &mut ledger);
320        self.effect_ledger = ledger;
321        result
322    }
323
324    /// Inserts a claim into the fact store, subject to capability authorization.
325    pub fn insert_fact(&mut self, claim: crate::Claim) -> Result<crate::Ref> {
326        self.facts
327            .insert_authorized(&self.capabilities, &mut self.datum_store, claim)
328    }
329
330    /// Inserts a claim and records it as part of a loaded library's receipt.
331    ///
332    /// When the claim did not already exist, `unload_lib` retracts it with the
333    /// rest of `lib_id`'s recorded load effects. Pre-existing identical claims
334    /// are left owned by their original publisher.
335    pub fn insert_fact_for_lib(
336        &mut self,
337        lib_id: LibId,
338        claim: crate::Claim,
339    ) -> Result<crate::Ref> {
340        let (reference, inserted) = self.insert_recorded_fact(claim)?;
341        if let Some(inserted) = inserted {
342            self.record_load_claims(lib_id, vec![inserted]);
343        }
344        Ok(reference)
345    }
346
347    pub(crate) fn insert_recorded_fact(
348        &mut self,
349        claim: crate::Claim,
350    ) -> Result<(crate::Ref, Option<ContentId>)> {
351        let id = claim.content_id(&mut self.datum_store)?;
352        let existed = self.facts.get(&id).is_some();
353        let inserted = self.insert_fact(claim)?;
354        let crate::Ref::Content(inserted_id) = inserted else {
355            return Err(Error::Lib(
356                "fact insertion returned a non-content reference".to_owned(),
357            ));
358        };
359        debug_assert_eq!(inserted_id, id);
360        Ok((
361            crate::Ref::Content(inserted_id.clone()),
362            (!existed).then_some(inserted_id),
363        ))
364    }
365
366    /// Queries the fact store for claims matching `pattern`, applying read policy.
367    pub fn query_facts(&self, pattern: crate::ClaimPattern) -> Result<Vec<crate::Claim>> {
368        self.facts.query_authorized(self, pattern)
369    }
370
371    pub(crate) fn record_load_claims(&mut self, lib_id: LibId, claim_ids: Vec<ContentId>) {
372        self.lib_load_ledger.record_claims(lib_id, claim_ids);
373    }
374
375    pub(crate) fn remove_load_claims(&mut self, lib_ids: &[LibId]) {
376        self.lib_load_ledger.remove_claims(lib_ids, &mut self.facts);
377    }
378
379    /// Returns the limits bounding number-domain promotion search.
380    pub fn promotion_search_limits(&self) -> PromotionSearchLimits {
381        self.promotion_search_limits
382    }
383
384    /// Sets the limits bounding number-domain promotion search.
385    pub fn set_promotion_search_limits(&mut self, limits: PromotionSearchLimits) {
386        self.promotion_search_limits = limits;
387    }
388
389    pub(crate) fn load_cx(&self) -> LoadCx {
390        LoadCx::new(
391            self.capabilities.clone(),
392            self.factory.clone(),
393            self.registry.clone(),
394        )
395    }
396
397    /// Returns the active evaluation policy.
398    pub fn eval_policy(&self) -> &dyn EvalPolicy {
399        self.eval_policy.as_ref()
400    }
401
402    /// Returns a shared handle to the active evaluation policy.
403    pub fn eval_policy_ref(&self) -> EvalPolicyRef {
404        self.eval_policy.clone()
405    }
406
407    /// Returns the name of the active evaluation policy.
408    pub fn eval_policy_name(&self) -> &'static str {
409        self.eval_policy.name()
410    }
411
412    /// Replaces the active evaluation policy.
413    pub fn set_eval_policy(&mut self, eval_policy: EvalPolicyRef) {
414        self.eval_policy = eval_policy;
415    }
416
417    /// Installs a macro expander.
418    pub fn set_macro_expander(&mut self, macro_expander: MacroExpanderRef) {
419        self.macro_expander = Some(macro_expander);
420    }
421
422    /// Removes any installed macro expander.
423    pub fn clear_macro_expander(&mut self) {
424        self.macro_expander = None;
425    }
426
427    /// Returns the installed macro expander, if any.
428    pub fn macro_expander_ref(&self) -> Option<MacroExpanderRef> {
429        self.macro_expander.clone()
430    }
431
432    /// Expands macros in `expr` for the given phase, or returns it unchanged.
433    pub fn expand_macros(&mut self, phase: Phase, expr: Expr) -> Result<Expr> {
434        let Some(expander) = self.macro_expander.clone() else {
435            return Ok(expr);
436        };
437        let eval_policy = self.eval_policy_ref();
438        if !eval_policy.allow_macro_expansion(phase) {
439            return Err(Error::Eval(format!(
440                "macro expansion denied by eval policy {} for {phase:?}",
441                eval_policy.name()
442            )));
443        }
444        self.require(&macro_expansion_capability_for_phase(phase))?;
445        expander.expand_expr(self, phase, expr)
446    }
447
448    /// Returns the accumulated diagnostics.
449    pub fn diagnostics(&self) -> &Diagnostics {
450        &self.diagnostics
451    }
452
453    /// Drains and returns the accumulated diagnostics.
454    pub fn take_diagnostics(&mut self) -> Vec<Diagnostic> {
455        self.diagnostics.take()
456    }
457
458    /// Records an already-built diagnostic.
459    pub fn push_diagnostic(&mut self, diagnostic: Diagnostic) {
460        self.diagnostics.push_diagnostic(diagnostic);
461    }
462
463    /// Records an info-level diagnostic from a message.
464    pub fn push_info(&mut self, message: impl Into<String>) {
465        self.diagnostics.push_info(message);
466    }
467
468    /// Constructs a context together with its host [`GrantSeat`].
469    ///
470    /// The caller (a bootloader, a server session, a loader) keeps the seat and
471    /// grants capabilities through it. The seat is never handed to a callable, so
472    /// loaded behavior cannot grant itself a capability. Use this instead of
473    /// [`Cx::new`] wherever the host needs to grant capabilities.
474    pub fn new_seated(
475        eval_policy: EvalPolicyRef,
476        factory: Arc<dyn Factory>,
477        handle_seed: crate::HandleSeed,
478    ) -> (Self, GrantSeat) {
479        let cx = Self::new(eval_policy, factory, handle_seed);
480        let seat = GrantSeat::for_cx(cx.grant_id);
481        (cx, seat)
482    }
483
484    /// The identity that binds a [`GrantSeat`] to this context.
485    pub(crate) fn grant_id(&self) -> u64 {
486        self.grant_id
487    }
488
489    /// Grants a capability into this context under host authority.
490    ///
491    /// This is the single internal insert point; the only ways to reach it are a
492    /// host-held [`GrantSeat`] (production) or the `test-support` grant methods
493    /// (tests). A loaded callable holds a `&mut Cx` but no seat, so it cannot
494    /// grant itself a capability.
495    pub(crate) fn grant_from_host(&mut self, capability: CapabilityName) {
496        self.capabilities.insert(capability);
497    }
498
499    /// Grants a capability to this context. TEST-ONLY: available only under the
500    /// `test-support` feature. Production code grants through a host [`GrantSeat`]
501    /// ([`Cx::new_seated`]); a loaded callable has no grant path at all.
502    #[cfg(feature = "test-support")]
503    pub fn grant(&mut self, capability: CapabilityName) {
504        self.grant_from_host(capability);
505    }
506
507    /// Grants a capability named by a static string. TEST-ONLY: available only
508    /// under the `test-support` feature.
509    #[cfg(feature = "test-support")]
510    pub fn grant_named(&mut self, capability: &'static str) {
511        self.grant_from_host(CapabilityName::new(capability));
512    }
513
514    /// Returns the granted capability set.
515    pub fn capabilities(&self) -> &Capabilities {
516        &self.capabilities
517    }
518
519    /// Runs `f` with `capabilities` installed, then restores the prior set.
520    pub fn with_capabilities<T>(
521        &mut self,
522        capabilities: Capabilities,
523        f: impl FnOnce(&mut Self) -> Result<T>,
524    ) -> Result<T> {
525        let saved = std::mem::replace(&mut self.capabilities, capabilities);
526        let result = f(self);
527        self.capabilities = saved;
528        result
529    }
530
531    /// Resolves a registered class by symbol.
532    pub fn resolve_class(&self, symbol: &Symbol) -> Result<Value> {
533        self.resolve_registered_value(
534            |registry| registry.class_by_symbol(symbol),
535            Error::UnknownClass {
536                class: symbol.clone(),
537            },
538        )
539    }
540
541    /// Resolves a registered function by symbol.
542    pub fn resolve_function(&self, symbol: &Symbol) -> Result<Value> {
543        self.resolve_registered_value(
544            |registry| registry.function_by_symbol(symbol),
545            Error::UnknownFunction {
546                function: symbol.clone(),
547            },
548        )
549    }
550
551    /// Resolves a registered macro by symbol.
552    pub fn resolve_macro(&self, symbol: &Symbol) -> Result<Value> {
553        self.resolve_registered_value(
554            |registry| registry.macro_by_symbol(symbol),
555            unknown_symbol(symbol),
556        )
557    }
558
559    /// Resolves a registered shape by symbol.
560    pub fn resolve_shape(&self, symbol: &Symbol) -> Result<Value> {
561        self.resolve_registered_value(
562            |registry| registry.shape_by_symbol(symbol),
563            unknown_symbol(symbol),
564        )
565    }
566
567    /// Resolves a registered codec by symbol.
568    pub fn resolve_codec(&self, symbol: &Symbol) -> Result<Value> {
569        self.resolve_registered_value(
570            |registry| registry.codec_by_symbol(symbol),
571            unknown_symbol(symbol),
572        )
573    }
574
575    /// Resolves a registered number domain by symbol.
576    pub fn resolve_number_domain(&self, symbol: &Symbol) -> Result<Value> {
577        self.resolve_registered_value(
578            |registry| registry.number_domain_by_symbol(symbol),
579            unknown_symbol(symbol),
580        )
581    }
582
583    /// Resolves a registered value binding by symbol.
584    pub fn resolve_value(&self, symbol: &Symbol) -> Result<Value> {
585        self.resolve_registered_value(
586            |registry| registry.value_by_symbol(symbol),
587            unknown_symbol(symbol),
588        )
589    }
590
591    fn resolve_registered_value(
592        &self,
593        lookup: impl FnOnce(&Registry) -> Option<&Value>,
594        not_found: Error,
595    ) -> Result<Value> {
596        lookup(self.registry()).cloned().ok_or(not_found)
597    }
598
599    /// Calls a callable value with already-evaluated arguments.
600    pub fn call_value(&mut self, value: Value, args: Args) -> Result<Value> {
601        let Some(callable) = value.object().as_callable() else {
602            return Err(Error::TypeMismatch {
603                expected: "callable",
604                found: "non-callable",
605            });
606        };
607        callable.call(self, args)
608    }
609
610    /// Calls a callable value with raw, unevaluated argument expressions.
611    pub fn call_exprs(&mut self, value: Value, args: Vec<crate::expr::Expr>) -> Result<Value> {
612        let Some(callable) = value.object().as_callable() else {
613            return Err(Error::TypeMismatch {
614                expected: "callable",
615                found: "non-callable",
616            });
617        };
618        callable.call_exprs(self, crate::object::RawArgs::new(args))
619    }
620
621    /// Resolves a function by symbol and calls it.
622    pub fn call_function(&mut self, symbol: &Symbol, args: Args) -> Result<Value> {
623        let function = self.resolve_function(symbol)?;
624        self.call_value(function, args)
625    }
626
627    /// Resolves a class by symbol and calls its constructor.
628    pub fn call_class(&mut self, symbol: &Symbol, args: Args) -> Result<Value> {
629        let class = self.resolve_class(symbol)?;
630        self.call_value(class, args)
631    }
632
633    /// Constructs an instance of `class` from read-time arguments.
634    ///
635    /// Requires the [`read_construct_capability`](crate::capability::read_construct_capability).
636    pub fn read_construct(&mut self, class: &Symbol, args: Vec<Value>) -> Result<Value> {
637        self.require(&read_construct_capability())?;
638        let class_value = self.resolve_class(class)?;
639        let constructor = class_protocol(&class_value)?
640            .read_constructor(self)?
641            .ok_or_else(|| Error::Eval(format!("class {class} has no read constructor")))?;
642        read_constructor_protocol(&constructor)?.construct_read(self, args)
643    }
644
645    /// Forces a value to the requested demand through the active eval policy.
646    pub fn force(&mut self, value: Value, demand: crate::eval::Demand) -> Result<Value> {
647        let eval_policy = self.eval_policy.clone();
648        eval_policy.force(self, value, demand)
649    }
650
651    /// Evaluates an expression through the active eval policy.
652    pub fn eval_expr(&mut self, expr: crate::expr::Expr) -> Result<Value> {
653        let eval_policy = self.eval_policy.clone();
654        eval_policy.eval_expr(self, expr)
655    }
656
657    /// Returns true when `name` resolves across environment or registry layers.
658    pub fn symbol_is_bound(&mut self, name: &Symbol) -> bool {
659        self.env().get(name).is_some()
660            || self.resolve_function(name).is_ok()
661            || self.resolve_class(name).is_ok()
662            || self.resolve_shape(name).is_ok()
663            || self.resolve_value(name).is_ok()
664    }
665
666    /// Resolves an unbound value-position symbol through the active eval policy.
667    pub fn resolve_unbound_symbol(&mut self, symbol: Symbol) -> Result<Value> {
668        let eval_policy = self.eval_policy_ref();
669        eval_policy.resolve_unbound_symbol(self, symbol)
670    }
671
672    /// Resolves an unbound call through the active eval policy.
673    pub fn resolve_unbound_call(
674        &mut self,
675        operator: Symbol,
676        args: Vec<crate::expr::Expr>,
677    ) -> Result<Value> {
678        let eval_policy = self.eval_policy_ref();
679        eval_policy.resolve_unbound_call(self, operator, args)
680    }
681
682    /// Demands a capability, returning [`Error::CapabilityDenied`] when absent.
683    pub fn require(&self, capability: &CapabilityName) -> Result<()> {
684        if self.capabilities.contains(capability) {
685            Ok(())
686        } else {
687            Err(Error::CapabilityDenied {
688                capability: capability.clone(),
689            })
690        }
691    }
692
693    /// Demands every capability in turn, failing on the first absent one.
694    pub fn require_all(&self, capabilities: &[CapabilityName]) -> Result<()> {
695        for capability in capabilities {
696            self.require(capability)?;
697        }
698        Ok(())
699    }
700}