Skip to main content

hara_native/core/
environment.rs

1pub type ProtocolFn = Rc<dyn Fn(&[Value]) -> Result<Value, String>>;
2pub type ProtocolSupports = Rc<dyn Fn(&Value) -> bool>;
3
4#[derive(Clone)]
5struct ProtocolImplementation {
6    supports: ProtocolSupports,
7    invoke: ProtocolFn,
8}
9
10#[derive(Default, Clone)]
11pub struct ProtocolRegistry {
12    methods: Rc<RefCell<HashMap<(String, String), Vec<ProtocolImplementation>>>>,
13    markers: Rc<RefCell<HashMap<String, Vec<ProtocolSupports>>>>,
14    extension_methods: Rc<RefCell<HashMap<(String, String, String, String), ProtocolFn>>>,
15    extension_categories: Rc<RefCell<HashSet<(String, String, String)>>>,
16    guest_methods: Rc<RefCell<HashMap<(String, String, String), Rc<Function>>>>,
17    guest_declarations: Rc<RefCell<HashSet<(String, String)>>>,
18    guest_protocols: Rc<RefCell<HashMap<String, Rc<GuestProtocol>>>>,
19}
20
21#[derive(Clone)]
22pub(crate) struct ProtocolRegistrySnapshot {
23    methods: HashMap<(String, String), Vec<ProtocolImplementation>>,
24    markers: HashMap<String, Vec<ProtocolSupports>>,
25    extension_methods: HashMap<(String, String, String, String), ProtocolFn>,
26    extension_categories: HashSet<(String, String, String)>,
27    guest_methods: HashMap<(String, String, String), Rc<Function>>,
28    guest_declarations: HashSet<(String, String)>,
29    guest_protocols: HashMap<String, Rc<GuestProtocol>>,
30}
31
32#[allow(dead_code)]
33impl ProtocolRegistry {
34    pub fn new() -> Self {
35        Self::default()
36    }
37
38    pub(crate) fn snapshot(&self) -> ProtocolRegistrySnapshot {
39        ProtocolRegistrySnapshot {
40            methods: self.methods.borrow().clone(),
41            markers: self.markers.borrow().clone(),
42            extension_methods: self.extension_methods.borrow().clone(),
43            extension_categories: self.extension_categories.borrow().clone(),
44            guest_methods: self.guest_methods.borrow().clone(),
45            guest_declarations: self.guest_declarations.borrow().clone(),
46            guest_protocols: self.guest_protocols.borrow().clone(),
47        }
48    }
49
50    pub(crate) fn restore(&self, snapshot: ProtocolRegistrySnapshot) {
51        *self.methods.borrow_mut() = snapshot.methods;
52        *self.markers.borrow_mut() = snapshot.markers;
53        *self.extension_methods.borrow_mut() = snapshot.extension_methods;
54        *self.extension_categories.borrow_mut() = snapshot.extension_categories;
55        *self.guest_methods.borrow_mut() = snapshot.guest_methods;
56        *self.guest_declarations.borrow_mut() = snapshot.guest_declarations;
57        *self.guest_protocols.borrow_mut() = snapshot.guest_protocols;
58    }
59
60    pub fn register<F>(
61        &mut self,
62        protocol: impl Into<String>,
63        method: impl Into<String>,
64        function: F,
65    ) where
66        F: Fn(&[Value]) -> Result<Value, String> + 'static,
67    {
68        let protocol = protocol.into();
69        if crate::lang::protocol::find_protocol(&protocol).is_some() {
70            self.register_declared(protocol, method, function);
71            return;
72        }
73        let protocol = protocol;
74        let supported_protocol = protocol.clone();
75        self.register_when(
76            protocol,
77            method,
78            move |value| native_protocol_supports(&supported_protocol, value),
79            function,
80        );
81    }
82
83    pub(crate) fn register_declared<F>(
84        &mut self,
85        protocol: impl Into<String>,
86        method: impl Into<String>,
87        function: F,
88    ) where
89        F: Fn(&[Value]) -> Result<Value, String> + 'static,
90    {
91        let protocol = protocol.into();
92        let method = method.into();
93        let declaration = crate::lang::protocol::find_protocol(&protocol)
94            .unwrap_or_else(|| panic!("unknown built-in protocol declaration: {protocol}"));
95        assert!(
96            declaration.method(&method).is_some(),
97            "method {method} is not declared by protocol {}",
98            declaration.name
99        );
100        let protocol = declaration.runtime_name();
101        let supported_protocol = protocol.clone();
102        self.register_when(
103            protocol,
104            method,
105            move |value| native_protocol_supports(&supported_protocol, value),
106            function,
107        );
108    }
109
110    pub fn register_marker<S>(&mut self, protocol: impl Into<String>, supports: S)
111    where
112        S: Fn(&Value) -> bool + 'static,
113    {
114        self.markers
115            .borrow_mut()
116            .entry(protocol.into())
117            .or_default()
118            .push(Rc::new(supports));
119    }
120
121    pub(crate) fn register_marker_declared<S>(&mut self, protocol: impl Into<String>, supports: S)
122    where
123        S: Fn(&Value) -> bool + 'static,
124    {
125        let protocol = protocol.into();
126        let declaration = crate::lang::protocol::find_protocol(&protocol)
127            .unwrap_or_else(|| panic!("unknown built-in protocol declaration: {protocol}"));
128        self.register_marker(declaration.runtime_name(), supports);
129    }
130
131    pub fn register_when<S, F>(
132        &mut self,
133        protocol: impl Into<String>,
134        method: impl Into<String>,
135        supports: S,
136        function: F,
137    ) where
138        S: Fn(&Value) -> bool + 'static,
139        F: Fn(&[Value]) -> Result<Value, String> + 'static,
140    {
141        let protocol = protocol.into();
142        let protocol = crate::lang::protocol::find_protocol(&protocol)
143            .map(|declaration| declaration.runtime_name())
144            .unwrap_or(protocol);
145        self.methods
146            .borrow_mut()
147            .entry((protocol, method.into()))
148            .or_default()
149            .push(ProtocolImplementation {
150                supports: Rc::new(supports),
151                invoke: Rc::new(function),
152            });
153    }
154
155    /// Registers a protocol implementation for one opaque extension type.
156    ///
157    /// Extension methods are kept separate from the ordinary protocol fallback
158    /// chain so collection primitives can dispatch without recursively entering
159    /// their own built-in protocol implementation.
160    pub fn register_extension<F>(
161        &mut self,
162        provider: impl Into<String>,
163        type_name: impl Into<String>,
164        protocol: impl Into<String>,
165        method: impl Into<String>,
166        function: F,
167    ) where
168        F: Fn(&[Value]) -> Result<Value, String> + 'static,
169    {
170        self.extension_methods.borrow_mut().insert(
171            (
172                provider.into(),
173                type_name.into(),
174                protocol.into(),
175                method.into(),
176            ),
177            Rc::new(function),
178        );
179    }
180
181    /// Marks an opaque extension type as a logical collection category such as
182    /// `map`. Predicates can then preserve the guest-language collection model.
183    pub fn register_extension_category(
184        &mut self,
185        provider: impl Into<String>,
186        type_name: impl Into<String>,
187        category: impl Into<String>,
188    ) {
189        self.extension_categories.borrow_mut().insert((
190            provider.into(),
191            type_name.into(),
192            category.into(),
193        ));
194    }
195
196    pub fn invoke_extension(
197        &self,
198        receiver: &ExtensionValue,
199        protocol: &str,
200        method: &str,
201        arguments: &[Value],
202    ) -> Result<Value, String> {
203        let key = (
204            receiver.provider.clone(),
205            receiver.type_name.clone(),
206            protocol.to_owned(),
207            method.to_owned(),
208        );
209        self.extension_methods
210            .borrow()
211            .get(&key)
212            .cloned()
213            .ok_or_else(|| {
214                format!(
215                    "protocol/unsupported-receiver: extension {}/{} has no {}/{} implementation",
216                    receiver.provider, receiver.type_name, protocol, method
217                )
218            })?(arguments)
219    }
220
221    pub fn extension_has_category(&self, receiver: &ExtensionValue, category: &str) -> bool {
222        self.extension_categories.borrow().contains(&(
223            receiver.provider.clone(),
224            receiver.type_name.clone(),
225            category.to_owned(),
226        ))
227    }
228
229    pub fn register_guest(
230        &self,
231        protocol: impl Into<String>,
232        type_name: impl Into<String>,
233        method: impl Into<String>,
234        function: Rc<Function>,
235    ) {
236        self.guest_methods.borrow_mut().insert(
237            (
238                protocol.into(),
239                type_name.into(),
240                method.into(),
241            ),
242            function,
243        );
244    }
245
246    pub fn declare_guest(&self, protocol: impl Into<String>, method: impl Into<String>) {
247        self.guest_declarations
248            .borrow_mut()
249            .insert((protocol.into(), method.into()));
250    }
251
252    pub fn register_guest_protocol(&self, protocol: Rc<GuestProtocol>) {
253        self.guest_protocols
254            .borrow_mut()
255            .insert(protocol.name.clone(), protocol);
256    }
257
258    fn guest_protocol(&self, name: &str) -> Option<Rc<GuestProtocol>> {
259        self.guest_protocols.borrow().get(name).cloned()
260    }
261
262    pub fn guest_protocol_reaches(&self, source: &str, target: &str) -> bool {
263        let mut pending = vec![source.to_owned()];
264        let mut visited = HashSet::new();
265        while let Some(current) = pending.pop() {
266            if !visited.insert(current.clone()) {
267                continue;
268            }
269            if current == target {
270                return true;
271            }
272            if let Some(protocol) = self.guest_protocol(&current) {
273                pending.extend(protocol.parents.iter().cloned());
274            }
275        }
276        false
277    }
278
279    pub fn replace_guest_protocol(&self, protocol: impl Into<String>) {
280        let protocol = protocol.into();
281        self.guest_declarations
282            .borrow_mut()
283            .retain(|(candidate, _)| candidate != &protocol);
284        self.guest_methods
285            .borrow_mut()
286            .retain(|(candidate, _, _), _| candidate != &protocol);
287        self.guest_protocols.borrow_mut().remove(&protocol);
288    }
289
290    #[cfg(all(feature = "direct-native", not(target_arch = "wasm32")))]
291    pub(crate) fn has_interpreted_guest_functions(&self) -> bool {
292        self.guest_methods
293            .borrow()
294            .values()
295            .any(|function| !is_direct_native_function(function))
296    }
297
298    pub fn invoke(
299        &self,
300        protocol: &str,
301        method: &str,
302        arguments: &[Value],
303    ) -> Result<Value, String> {
304        let protocol = protocol;
305        let known_method = self
306            .methods
307            .borrow()
308            .contains_key(&(protocol.to_owned(), method.to_owned()))
309            || self
310                .guest_declarations
311                .borrow()
312                .contains(&(protocol.to_owned(), method.to_owned()))
313            || protocol_declarations()
314                .iter()
315                .any(|declaration| declaration.runtime_name() == protocol);
316        if !known_method {
317            return Err(format!("missing protocol method: {protocol}/{method}"));
318        }
319        if let Some(Value::Extension(receiver)) = arguments.first() {
320            let extension_method = self.extension_methods.borrow().contains_key(&(
321                receiver.provider.clone(),
322                receiver.type_name.clone(),
323                protocol.to_owned(),
324                method.to_owned(),
325            ));
326            if extension_method {
327                return self.invoke_extension(receiver, protocol, method, arguments);
328            }
329        }
330        let named_type = match arguments.first() {
331            Some(Value::Struct(receiver)) => Some(receiver.ty.name.as_str()),
332            Some(Value::Mutable(receiver)) => Some(receiver.ty.name.as_str()),
333            _ => None,
334        };
335        if let Some(type_name) = named_type {
336            let guest_function = self
337                .guest_methods
338                .borrow()
339                .get(&(protocol.to_owned(), type_name.to_owned(), method.to_owned()))
340                .cloned();
341            if let Some(function) = guest_function {
342                return call_function(&function, arguments.to_vec());
343            }
344        }
345        let methods = self.methods.borrow();
346        let receiver = arguments.first().ok_or_else(|| {
347            format!("protocol/arity: {protocol}/{method} expects at least one argument, received 0")
348        })?;
349        let last_error = format!(
350            "protocol/unsupported-receiver: missing protocol implementation: {protocol}/{method}"
351        );
352        if let Some(implementations) = methods.get(&(protocol.to_string(), method.to_string())) {
353            for implementation in implementations.iter().rev() {
354                if (implementation.supports)(receiver) {
355                    return (implementation.invoke)(arguments);
356                }
357            }
358        }
359        if self
360            .guest_declarations
361            .borrow()
362            .contains(&(protocol.to_owned(), method.to_owned()))
363            || protocol_declarations()
364                .iter()
365                .any(|declaration| declaration.runtime_name() == protocol)
366        {
367            Err(last_error)
368        } else {
369            Err(format!("missing protocol method: {protocol}/{method}"))
370        }
371    }
372
373    pub fn contains(&self, protocol: &str, method: &str) -> bool {
374        let methods = self.methods.borrow();
375        methods
376            .get(&(protocol.to_owned(), method.to_string()))
377            .is_some_and(|implementations| !implementations.is_empty())
378    }
379
380    pub fn satisfies(&self, protocol: &GuestProtocol, value: &Value) -> bool {
381        if let Value::Extension(receiver) = value {
382            let protocol_name = protocol
383                .name
384                .rsplit(|character| character == '/' || character == '.')
385                .next()
386                .unwrap_or(protocol.name.as_str());
387            let category_matches = match protocol_name {
388                "IMapType" => self.extension_has_category(receiver, "map"),
389                "ISetType" => self.extension_has_category(receiver, "set"),
390                "ISequential" => {
391                    self.extension_has_category(receiver, "sequential")
392                        || self.extension_has_category(receiver, "linear")
393                }
394                "ILinearType" => self.extension_has_category(receiver, "linear"),
395                "IColl" => {
396                    self.extension_has_category(receiver, "coll")
397                        || ["map", "set", "linear"]
398                            .iter()
399                            .any(|category| self.extension_has_category(receiver, category))
400                }
401                _ => false,
402            };
403            if category_matches {
404                return true;
405            }
406        }
407        if !protocol.parents.iter().all(|parent| {
408            self.guest_protocol(parent)
409                .is_some_and(|parent| self.satisfies(&parent, value))
410                || crate::lang::protocol::find_protocol(parent)
411                    .is_some_and(|declaration| self.satisfies(&guest_protocol(declaration), value))
412        }) {
413            return false;
414        }
415        let protocol_name = protocol.name.clone();
416        if protocol.methods.is_empty() {
417            if let Some(implementations) = self.markers.borrow().get(&protocol_name) {
418                return implementations.iter().rev().any(|supports| supports(value));
419            }
420            if !protocol.parents.is_empty() {
421                return true;
422            }
423            return false;
424        }
425        if let Value::Extension(receiver) = value {
426            let methods = self.methods.borrow();
427            let extensions = self.extension_methods.borrow();
428            return protocol.methods.keys().all(|method| {
429                extensions.contains_key(&(
430                    receiver.provider.clone(),
431                    receiver.type_name.clone(),
432                    protocol_name.clone(),
433                    method.clone(),
434                ))
435                    || methods
436                        .get(&(protocol_name.clone(), method.clone()))
437                        .is_some_and(|implementations| {
438                            implementations
439                                .iter()
440                                .rev()
441                                .any(|implementation| (implementation.supports)(value))
442                        })
443            });
444        }
445        if let Value::Struct(receiver) = value {
446            return protocol.methods.keys().all(|method| {
447                self.guest_methods.borrow().contains_key(&(
448                    protocol_name.clone(),
449                    receiver.ty.name.clone(),
450                    method.clone(),
451                )) || self
452                    .methods
453                    .borrow()
454                    .get(&(protocol_name.clone(), method.clone()))
455                    .is_some_and(|implementations| {
456                        implementations
457                            .iter()
458                            .rev()
459                            .any(|implementation| (implementation.supports)(value))
460                    })
461            });
462        }
463        if let Value::Mutable(receiver) = value {
464            return protocol.methods.keys().all(|method| {
465                self.guest_methods.borrow().contains_key(&(
466                    protocol_name.clone(),
467                    receiver.ty.name.clone(),
468                    method.clone(),
469                )) || self
470                    .methods
471                    .borrow()
472                    .get(&(protocol_name.clone(), method.clone()))
473                    .is_some_and(|implementations| {
474                        implementations
475                            .iter()
476                            .rev()
477                            .any(|implementation| (implementation.supports)(value))
478                    })
479            });
480        }
481        let methods = self.methods.borrow();
482        if protocol.methods.keys().all(|method| {
483            methods
484                .get(&(protocol_name.clone(), method.clone()))
485                .is_some_and(|implementations| {
486                    implementations
487                        .iter()
488                        .rev()
489                        .any(|implementation| (implementation.supports)(value))
490                })
491        }) {
492            return true;
493        }
494        false
495    }
496
497    /// Returns the built-in collection protocol registry used by evaluator dispatch.
498    pub fn core() -> Self {
499        let mut registry = Self::new();
500        registry.register_marker_declared("IMutable", |value| {
501            native_protocol_supports("IMutable", value)
502        });
503        registry.register_marker_declared("IPersistent", |value| {
504            native_protocol_supports("IPersistent", value)
505        });
506        registry.register_marker_declared("IMapType", |value| {
507            native_protocol_supports("IMapType", value)
508        });
509        registry.register_marker_declared("ISequential", |value| {
510            native_protocol_supports("ISequential", value)
511        });
512        registry.register_marker_declared("ILinearType", |value| {
513            native_protocol_supports("ILinearType", value)
514        });
515        registry.register_marker_declared("ISetType", |value| {
516            native_protocol_supports("ISetType", value)
517        });
518        registry.register_marker_declared("IOFn", |value| matches!(value, Value::Keyword(_)));
519        registry.register("std.protocol.icount.ICount", "count", protocol_count);
520        registry.register("std.protocol.inth.INth", "nth", protocol_nth);
521        registry.register("std.protocol.ilookup.ILookup", "lookup", protocol_lookup);
522        registry.register(
523            "std.protocol.ipointer.IPointer",
524            "ptr-context",
525            protocol_pointer_context,
526        );
527        registry.register("std.protocol.ifind.IFind", "find", protocol_find);
528        registry.register("std.protocol.iassoc.IAssoc", "assoc", protocol_assoc);
529        registry.register("std.protocol.iconj.IConj", "conj", protocol_conj);
530        registry.register("std.protocol.icons.ICons", "cons", protocol_cons);
531        registry.register("std.protocol.idissoc.IDissoc", "dissoc", protocol_dissoc);
532        registry.register("std.protocol.iempty.IEmpty", "empty", protocol_empty);
533        registry.register(
534            "std.protocol.iequality.IEquality",
535            "equality",
536            protocol_equality,
537        );
538        registry.register(
539            "std.protocol.idisplay.IDisplay",
540            "display",
541            protocol_display,
542        );
543        registry.register(
544            "std.protocol.iencodable.IEncodable",
545            "encode-with",
546            protocol_encode_with,
547        );
548        registry.register(
549            "std.protocol.iexinfo.IExInfo",
550            "data",
551            |arguments| match arguments {
552                [Value::ExceptionInfo(value)] => Ok((*value.data).clone()),
553                [_] => {
554                    Err("missing protocol implementation: std.protocol.iexinfo.IExInfo/data".into())
555                }
556                _ => Err("IExInfo/data expects one argument".into()),
557            },
558        );
559        registry.register("std.protocol.ihash.IHash", "hash", protocol_hash);
560        registry.register(
561            "std.protocol.ihashcached.IHashCached",
562            "hash-current",
563            protocol_hash_current,
564        );
565        registry.register(
566            "std.protocol.ihashcached.IHashCached",
567            "hash-put",
568            protocol_hash_put,
569        );
570        registry.register_when(
571            "std.protocol.ifn.IFn",
572            "invoke",
573            Value::supports_native_ifn,
574            protocol_invoke,
575        );
576        registry.register("std.protocol.ipair.IPair", "key", protocol_pair_key);
577        registry.register("std.protocol.ipair.IPair", "value", protocol_pair_value);
578        registry.register(
579            "std.protocol.ipeekfirst.IPeekFirst",
580            "peek-first",
581            protocol_peek_first,
582        );
583        registry.register(
584            "std.protocol.ipeeklast.IPeekLast",
585            "peek-last",
586            protocol_peek_last,
587        );
588        registry.register(
589            "std.protocol.ipopfirst.IPopFirst",
590            "pop-first",
591            protocol_pop_first,
592        );
593        registry.register(
594            "std.protocol.ipoplast.IPopLast",
595            "pop-last",
596            protocol_pop_last,
597        );
598        registry.register(
599            "std.protocol.ipushfirst.IPushFirst",
600            "push-first",
601            protocol_push_first,
602        );
603        registry.register(
604            "std.protocol.ipushlast.IPushLast",
605            "push-last",
606            protocol_push_last,
607        );
608        registry.register("std.protocol.iiter.IIter", "iter", protocol_iter);
609        registry.register(
610            "std.protocol.iiterator.IIterator",
611            "iter-next?",
612            |arguments| {
613                arguments
614                    .first()
615                    .ok_or_else(|| "IIterator/iter-next? expects one argument".to_string())
616                    .and_then(iterator_has_next)
617            },
618        );
619        registry.register(
620            "std.protocol.iiterator.IIterator",
621            "iter-next",
622            |arguments| {
623                arguments
624                    .first()
625                    .ok_or_else(|| "IIterator/iter-next expects one argument".to_string())
626                    .and_then(iterator_next)
627            },
628        );
629        registry.register(
630            "std.protocol.iclose.IClose",
631            "close",
632            |arguments| match arguments {
633                [Value::Coroutine(coroutine)] => {
634                    coroutine_close(coroutine)?;
635                    Ok(Value::Coroutine(coroutine.clone()))
636                }
637                [Value::Stream(stream)] => {
638                    stream_close(stream)?;
639                    Ok(Value::Stream(stream.clone()))
640                }
641                [value] => iterator_close(value),
642                _ => Err("IClose/close expects one argument".into()),
643            },
644        );
645        registry.register(
646            "std.protocol.inamespaced.INamespaced",
647            "name",
648            protocol_namespaced_name,
649        );
650        registry.register(
651            "std.protocol.inamespaced.INamespaced",
652            "namespace",
653            protocol_namespaced_namespace,
654        );
655        registry.register(
656            "std.protocol.istringlike.IStringLike",
657            "to-string",
658            protocol_string_like_to_string,
659        );
660        registry.register(
661            "std.protocol.istringlike.IStringLike",
662            "from-string",
663            protocol_string_like_from_string,
664        );
665        registry.register("std.protocol.iobjtype.IObjType", "meta", protocol_meta);
666        registry.register(
667            "std.protocol.imetadata.IMetadata",
668            "metatype",
669            protocol_metatype,
670        );
671        registry.register(
672            "std.protocol.iobjtype.IObjType",
673            "with-meta",
674            protocol_with_meta,
675        );
676        registry.register(
677            "std.protocol.icoll.IColl",
678            "start-string",
679            protocol_coll_start,
680        );
681        registry.register("std.protocol.icoll.IColl", "end-string", protocol_coll_end);
682        registry.register("std.protocol.icoll.IColl", "sep-string", protocol_coll_sep);
683        registry.register("std.protocol.ideref.IDeref", "deref", protocol_deref);
684        registry.register(
685            "std.protocol.iapplicable.IApplicable",
686            "apply-default",
687            protocol_apply_default,
688        );
689        registry.register(
690            "std.protocol.iapplicable.IApplicable",
691            "apply-in",
692            protocol_apply_in,
693        );
694        registry.register(
695            "std.protocol.iapplicable.IApplicable",
696            "transform-in",
697            protocol_transform_in,
698        );
699        registry.register(
700            "std.protocol.iapplicable.IApplicable",
701            "transform-out",
702            protocol_transform_out,
703        );
704        registry.register(
705            "std.protocol.idereftimeout.IDerefTimeout",
706            "deref-timeout",
707            protocol_deref_timeout,
708        );
709        registry.register("std.protocol.ireset.IReset", "reset", protocol_reset);
710        registry.register("std.protocol.icas.ICas", "cas", protocol_cas);
711        registry.register("std.protocol.ireduce.IReduce", "reduce", protocol_reduce);
712        registry.register(
713            "std.protocol.itomutable.IToMutable",
714            "to-mutable",
715            protocol_to_mutable,
716        );
717        registry.register(
718            "std.protocol.itopersistent.IToPersistent",
719            "to-persistent",
720            protocol_to_persistent,
721        );
722        registry.register(
723            "std.protocol.ipromise.IPromise",
724            "state",
725            protocol_promise_state,
726        );
727        registry.register(
728            "std.protocol.ipromise.IPromise",
729            "value",
730            protocol_promise_value,
731        );
732        registry.register("std.protocol.ipromise.IPromise", "then", |arguments| {
733            protocol_promise_chain("promise/then", arguments)
734        });
735        registry.register("std.protocol.ipromise.IPromise", "catch", |arguments| {
736            protocol_promise_chain("promise/catch", arguments)
737        });
738        registry.register("std.protocol.ipromise.IPromise", "finally", |arguments| {
739            protocol_promise_chain("promise/finally", arguments)
740        });
741        registry.register(
742            "std.protocol.ipromise.IPromise",
743            "cancel",
744            protocol_promise_cancel,
745        );
746        registry.register(
747            "std.protocol.icoroutine.ICoroutine",
748            "status",
749            protocol_coroutine_status,
750        );
751        registry.register(
752            "std.protocol.icoroutine.ICoroutine",
753            "resume",
754            protocol_coroutine_resume,
755        );
756        registry.register(
757            "std.protocol.istream.IStream",
758            "next",
759            |arguments| match arguments {
760                [Value::Stream(stream)] => Ok(stream_next(stream)),
761                [_] => Err("IStream/next expects a stream".into()),
762                _ => Err("IStream/next expects one argument".into()),
763            },
764        );
765        registry.register(
766            "std.protocol.istreamwrite.IStreamWrite",
767            "write",
768            |arguments| match arguments {
769                [_target, _value] => Err("IStreamWrite/write expects a writable stream".into()),
770                _ => Err("IStreamWrite/write expects two arguments".into()),
771            },
772        );
773        registry.register(
774            "std.protocol.iabort.IAbort",
775            "abort",
776            |arguments| match arguments {
777                [_target, _error] => Err("IAbort/abort expects an abortable stream".into()),
778                _ => Err("IAbort/abort expects two arguments".into()),
779            },
780        );
781        registry.register(
782            "std.protocol.iwatch.IWatch",
783            "watch-add",
784            protocol_watch_add,
785        );
786        registry.register(
787            "std.protocol.iwatch.IWatch",
788            "watch-remove",
789            protocol_watch_remove,
790        );
791        registry.register(
792            "std.protocol.iwatch.IWatch",
793            "watch-list",
794            protocol_watch_list,
795        );
796        registry
797    }
798}
799
800thread_local! {
801    static ACTIVE_PROTOCOLS: RefCell<Option<ProtocolRegistry>> = const { RefCell::new(None) };
802    static ACTIVE_NAMESPACES: RefCell<Option<NamespaceRegistry<Value>>> = const { RefCell::new(None) };
803    static ACTIVE_DEFINITION_ORIGIN: Cell<VarOrigin> = const { Cell::new(VarOrigin::Source) };
804    static ACTIVE_PROMISE_PROVIDER: RefCell<Option<Rc<dyn PromiseProvider>>> = const { RefCell::new(None) };
805    static ACTIVE_FILE_PROVIDER: RefCell<Option<Rc<dyn FileProvider>>> = const { RefCell::new(None) };
806    static ACTIVE_SOCKET_PROVIDER: RefCell<Option<Rc<dyn SocketProvider>>> = const { RefCell::new(None) };
807    static ACTIVE_KERNEL_PROVIDER: RefCell<Option<Rc<KernelProvider>>> = const { RefCell::new(None) };
808    static ACTIVE_PACKAGE_CATALOG: RefCell<Option<PackageCatalog>> = const { RefCell::new(None) };
809    static ACTIVE_PROCESS_ALLOWED: Cell<bool> = const { Cell::new(false) };
810    static ACTIVE_TEST_RUNNER: RefCell<String> = RefCell::new("code.test".into());
811    static HOST_CALL_HANDLER: RefCell<Option<Rc<dyn Fn(String, String, Vec<Value>) -> Result<Value, String>>>> = const { RefCell::new(None) };
812    static NAMESPACE_SOURCE_PROVIDER: RefCell<Option<Rc<dyn Fn(&str) -> Option<NamespaceResource>>>> = const { RefCell::new(None) };
813    static ACTIVE_THROWN_VALUE: RefCell<Option<(String, Value)>> = const { RefCell::new(None) };
814    static ACTIVE_MULTIMETHODS: RefCell<HashMap<String, Rc<RefCell<MultiMethod>>>> = RefCell::new(HashMap::new());
815    #[cfg(all(feature = "direct-native", not(target_arch = "wasm32")))]
816    static ACTIVE_DIRECT_NATIVE_MULTIMETHODS: RefCell<Option<MultiMethodRegistry>> = const { RefCell::new(None) };
817    #[cfg(all(feature = "direct-native", not(target_arch = "wasm32")))]
818    static ACTIVE_DIRECT_NATIVE_NAMESPACE_LOADER: RefCell<Option<Rc<dyn Fn(&str, NamespaceResource, &mut HashMap<String, Value>) -> Result<(), String>>>> = const { RefCell::new(None) };
819    #[cfg(all(feature = "direct-native", not(target_arch = "wasm32")))]
820    static ACTIVE_DIRECT_NATIVE_EXECUTION: Cell<bool> = const { Cell::new(false) };
821}
822
823#[cfg(all(feature = "direct-native", not(target_arch = "wasm32")))]
824pub(crate) type MultiMethodRegistry =
825    Rc<RefCell<HashMap<String, Rc<RefCell<MultiMethod>>>>>;
826
827#[cfg(all(feature = "direct-native", not(target_arch = "wasm32")))]
828#[derive(Clone)]
829pub(crate) struct DirectNativeContext {
830    pub(crate) namespaces: NamespaceRegistry<Value>,
831    /// The namespace in which the frame was compiled. Namespace registries
832    /// share their mutable current pointer, so a suspended child must restore
833    /// this selection explicitly when it resumes instead of inheriting a
834    /// caller which happened to run in the meantime.
835    pub(crate) namespace: String,
836    pub(crate) protocols: ProtocolRegistry,
837    pub(crate) promise_provider: Rc<dyn PromiseProvider>,
838    pub(crate) file_provider: Option<Rc<dyn FileProvider>>,
839    pub(crate) socket_provider: Option<Rc<dyn SocketProvider>>,
840    pub(crate) process_allowed: bool,
841    pub(crate) kernel_provider: Option<Rc<KernelProvider>>,
842    pub(crate) package_catalog: PackageCatalog,
843    pub(crate) macros: Rc<RefCell<HashMap<(String, String), Rc<Function>>>>,
844    pub(crate) namespace_source:
845        Option<Rc<dyn Fn(&str) -> Option<NamespaceResource>>>,
846    pub(crate) host_handler:
847        Option<Rc<dyn Fn(String, String, Vec<Value>) -> Result<Value, String>>>,
848    pub(crate) test_runner: String,
849    pub(crate) definition_origin: VarOrigin,
850    pub(crate) multimethods: MultiMethodRegistry,
851    pub(crate) native_namespace_loader: Option<
852        Rc<dyn Fn(
853            &str,
854            NamespaceResource,
855            &mut HashMap<String, Value>,
856        ) -> Result<(), String>>,
857    >,
858    pub(crate) work_context: Option<crate::work::WorkContext>,
859}
860
861#[cfg(all(feature = "direct-native", not(target_arch = "wasm32")))]
862impl DirectNativeContext {
863    pub(crate) fn capture() -> Self {
864        // A callback created during native execution must retain the same
865        // registry as its parent. Declaration macros execute their work in
866        // callback thunks, so copying here would hide a `defmulti` from the
867        // following `defmethod` in the same compiled namespace.
868        let multimethods = ACTIVE_DIRECT_NATIVE_MULTIMETHODS
869            .with(|active| active.borrow().clone())
870            .unwrap_or_else(|| {
871                Rc::new(RefCell::new(
872                    ACTIVE_MULTIMETHODS.with(|active| active.borrow().clone()),
873                ))
874            });
875        Self::capture_with_multimethods(multimethods)
876    }
877
878    pub(crate) fn capture_with_multimethods(multimethods: MultiMethodRegistry) -> Self {
879        let namespaces = namespace_registry()
880            .unwrap_or_else(|_| NamespaceRegistry::new("user"));
881        let namespace = namespaces.current().name().as_str().to_owned();
882        let protocols = ACTIVE_PROTOCOLS
883            .with(|active| active.borrow().clone())
884            .unwrap_or_else(ProtocolRegistry::core);
885        let promise_provider = ACTIVE_PROMISE_PROVIDER
886            .with(|active| active.borrow().clone())
887            .unwrap_or_else(|| Rc::new(LocalPromiseProvider));
888        let file_provider = ACTIVE_FILE_PROVIDER.with(|active| active.borrow().clone());
889        let socket_provider = ACTIVE_SOCKET_PROVIDER.with(|active| active.borrow().clone());
890        let process_allowed = ACTIVE_PROCESS_ALLOWED.get();
891        let kernel_provider = ACTIVE_KERNEL_PROVIDER.with(|active| active.borrow().clone());
892        let package_catalog = ACTIVE_PACKAGE_CATALOG
893            .with(|active| active.borrow().clone())
894            .unwrap_or_default();
895        let macros = ACTIVE_MACROS.with(|active| {
896            active
897                .borrow()
898                .clone()
899                .unwrap_or_else(|| Rc::new(RefCell::new(HashMap::new())))
900        });
901        let namespace_source = NAMESPACE_SOURCE_PROVIDER
902            .with(|active| active.borrow().clone());
903        let host_handler = HOST_CALL_HANDLER.with(|active| active.borrow().clone());
904        let test_runner = ACTIVE_TEST_RUNNER.with(|active| active.borrow().clone());
905        let definition_origin = ACTIVE_DEFINITION_ORIGIN.with(Cell::get);
906        let native_namespace_loader = ACTIVE_DIRECT_NATIVE_NAMESPACE_LOADER
907            .with(|active| active.borrow().clone());
908        let work_context = crate::work::current_work_context();
909        Self {
910            namespaces,
911            namespace,
912            protocols,
913            promise_provider,
914            file_provider,
915            socket_provider,
916            process_allowed,
917            kernel_provider,
918            package_catalog,
919            macros,
920            namespace_source,
921            host_handler,
922            test_runner,
923            definition_origin,
924            multimethods,
925            native_namespace_loader,
926            work_context,
927        }
928    }
929
930    pub(crate) fn with<R>(&self, operation: impl FnOnce() -> R) -> R {
931        let namespaces = self.namespaces.clone();
932        let namespace = self.namespace.clone();
933        let run = || {
934            let previous = namespaces.current().name().as_str().to_owned();
935            namespaces.set_current(&namespace);
936            let result = with_test_runner(&self.test_runner, || {
937                with_capability_providers(
938                    self.file_provider.clone(),
939                    self.socket_provider.clone(),
940                    self.process_allowed,
941                    self.kernel_provider.clone(),
942                    || {
943                        with_package_catalog(&self.package_catalog, || {
944                            with_promise_provider(self.promise_provider.clone(), || {
945                                with_macros(self.macros.clone(), || {
946                                    with_namespace_registry(&self.namespaces, || {
947                                        with_definition_origin(self.definition_origin, || {
948                                            with_protocols(&self.protocols, || {
949                                                with_direct_native_context_values(self, operation)
950                                            })
951                                        })
952                                    })
953                                })
954                            })
955                        })
956                    },
957                )
958            });
959            namespaces.set_current(&previous);
960            result
961        };
962        if let Some(context) = self.work_context.clone() {
963            crate::work::with_current_work_context(context, run)
964        } else {
965            run()
966        }
967    }
968}
969
970#[cfg(all(feature = "direct-native", not(target_arch = "wasm32")))]
971fn with_direct_native_context_values<R>(
972    context: &DirectNativeContext,
973    operation: impl FnOnce() -> R,
974) -> R {
975    ACTIVE_DIRECT_NATIVE_MULTIMETHODS.with(|direct_native| {
976        let previous_direct_native = direct_native.replace(Some(context.multimethods.clone()));
977        let result = {
978            let run_with_multimethods = || {
979                ACTIVE_MULTIMETHODS.with(|active| {
980                    let previous = std::mem::replace(
981                        &mut *active.borrow_mut(),
982                        context.multimethods.borrow().clone(),
983                    );
984                    let result = operation();
985                    *context.multimethods.borrow_mut() = active.borrow().clone();
986                    *active.borrow_mut() = previous;
987                    result
988                })
989            };
990            let run_with_loader = || {
991                if let Some(loader) = context.native_namespace_loader.clone() {
992                    with_direct_native_namespace_loader(loader, run_with_multimethods)
993                } else {
994                    run_with_multimethods()
995                }
996            };
997            let run_with_source = || {
998                if let Some(provider) = context.namespace_source.clone() {
999                    with_namespace_source(provider, run_with_loader)
1000                } else {
1001                    run_with_loader()
1002                }
1003            };
1004            if let Some(handler) = context.host_handler.clone() {
1005                with_host_calls(handler, run_with_source)
1006            } else {
1007                run_with_source()
1008            }
1009        };
1010        direct_native.replace(previous_direct_native);
1011        result
1012    })
1013}
1014
1015pub(crate) fn with_test_runner<R>(runner: &str, f: impl FnOnce() -> R) -> R {
1016    ACTIVE_TEST_RUNNER.with(|active| {
1017        let previous = active.replace(runner.into());
1018        let result = f();
1019        active.replace(previous);
1020        result
1021    })
1022}
1023
1024pub(crate) fn snapshot_multimethods() -> HashMap<String, MultiMethod> {
1025    ACTIVE_MULTIMETHODS.with(|active| {
1026        active
1027            .borrow()
1028            .iter()
1029            .map(|(name, state)| (name.clone(), state.borrow().clone()))
1030            .collect()
1031    })
1032}
1033
1034pub(crate) fn restore_multimethods(snapshot: HashMap<String, MultiMethod>) {
1035    ACTIVE_MULTIMETHODS.with(|active| {
1036        *active.borrow_mut() = snapshot
1037            .into_iter()
1038            .map(|(name, state)| (name, Rc::new(RefCell::new(state))))
1039            .collect();
1040    });
1041}
1042
1043pub(crate) fn register_multimethod(name: String, state: Rc<RefCell<MultiMethod>>) {
1044    ACTIVE_MULTIMETHODS.with(|active| {
1045        active.borrow_mut().insert(name, state);
1046    });
1047}
1048
1049pub(crate) fn multimethod_state(name: &str) -> Option<Rc<RefCell<MultiMethod>>> {
1050    ACTIVE_MULTIMETHODS.with(|active| active.borrow().get(name).cloned())
1051}
1052
1053pub(crate) fn active_protocol_registry() -> Result<ProtocolRegistry, String> {
1054    ACTIVE_PROTOCOLS
1055        .with(|active| active.borrow().clone())
1056        .ok_or_else(|| "protocol registry is unavailable".into())
1057}
1058
1059#[derive(Clone)]
1060pub enum NamespaceResource {
1061    Source(String),
1062    /// A native host source whose contents are read only when the namespace
1063    /// crosses the require boundary.
1064    #[cfg(not(target_arch = "wasm32"))]
1065    SourcePath(std::path::PathBuf),
1066    #[cfg(feature = "bytecode-vm")]
1067    Bytecode {
1068        namespace_form: String,
1069        artifact: Vec<u8>,
1070    },
1071}
1072
1073#[cfg(not(target_arch = "wasm32"))]
1074pub(crate) fn read_source_resource(
1075    resource: &NamespaceResource,
1076    namespace: &str,
1077) -> Result<String, String> {
1078    match resource {
1079        NamespaceResource::Source(source) => Ok(source.clone()),
1080        NamespaceResource::SourcePath(path) => std::fs::read_to_string(path)
1081            .map_err(|error| format!("{namespace}: cannot read {}: {error}", path.display())),
1082        #[cfg(feature = "bytecode-vm")]
1083        NamespaceResource::Bytecode { .. } => {
1084            Err(format!("{namespace}: bytecode resource is not source text"))
1085        }
1086    }
1087}
1088
1089pub(crate) fn thrown_error(value: Value) -> String {
1090    thrown_error_at(value, current_exception_site())
1091}
1092
1093pub(crate) fn thrown_error_at(value: Value, site: Option<ExceptionSite>) -> String {
1094    record_exception_throw(&value, site);
1095    record_trace_failure();
1096    let error = format!("thrown: {}", value.display());
1097    ACTIVE_THROWN_VALUE.with(|active| {
1098        *active.borrow_mut() = Some((error.clone(), value));
1099    });
1100    error
1101}
1102
1103/// Captures the uncaught exception value from one evaluator boundary without
1104/// changing the string error API used by the runtime.  The previous dynamic
1105/// value is restored so a diagnostic request cannot leak exception state into
1106/// a later evaluation in the same broker session.
1107pub fn with_thrown_value_capture<R>(operation: impl FnOnce() -> R) -> (R, Option<Value>) {
1108    ACTIVE_THROWN_VALUE.with(|active| {
1109        let previous = active.replace(None);
1110        let result = operation();
1111        let captured = active.take().map(|(_, value)| value);
1112        active.replace(previous);
1113        (result, captured)
1114    })
1115}
1116
1117pub(crate) fn promise_rejection_error(error: PromiseRejection) -> String {
1118    match error {
1119        PromiseRejection::Message(message) => message,
1120        PromiseRejection::Value(value) | PromiseRejection::Cancelled(value) => thrown_error(value),
1121    }
1122}
1123
1124pub(crate) fn caught_error(error: &str) -> Value {
1125    ACTIVE_THROWN_VALUE.with(|active| {
1126        let mut active = active.borrow_mut();
1127        if active
1128            .as_ref()
1129            .is_some_and(|(thrown_error, _)| error.starts_with(thrown_error))
1130        {
1131            return active.take().unwrap().1;
1132        }
1133        Value::String(error.to_owned())
1134    })
1135}
1136
1137pub(crate) fn catch_matches(error: &str, class: &str) -> bool {
1138    if class == "Exception" || class == "Throwable" {
1139        return true;
1140    }
1141    if let Some(selectors) = class
1142        .strip_prefix('[')
1143        .and_then(|value| value.strip_suffix(']'))
1144    {
1145        return selectors
1146            .split(',')
1147            .any(|selector| catch_matches(error, selector));
1148    }
1149    if let Some(selector) = class.strip_prefix(':') {
1150        return ACTIVE_THROWN_VALUE.with(|active| {
1151            active.borrow().as_ref().is_some_and(|(message, value)| {
1152                error.starts_with(message)
1153                    && matches!(value, Value::ExceptionInfo(info)
1154                        if map_entries(&info.data).is_some_and(|entries| entries.iter().any(|(key, value)| {
1155                            matches!(key, Value::Keyword(name) if name.as_str() == "ex/code")
1156                                && matches!(value, Value::Keyword(code) if code.as_str() == selector)
1157                        })))
1158            })
1159        });
1160    }
1161    ACTIVE_THROWN_VALUE.with(|active| {
1162        active.borrow().as_ref().is_some_and(|(message, value)| {
1163            error.starts_with(message)
1164                && match value {
1165                    Value::Struct(value) => {
1166                        value.ty.name == class || value.ty.name.ends_with(&format!("/{class}"))
1167                    }
1168                    Value::Mutable(value) => {
1169                        value.ty.name == class || value.ty.name.ends_with(&format!("/{class}"))
1170                    }
1171                    _ => false,
1172                }
1173        })
1174    })
1175}
1176
1177/// Runs an evaluation with a namespace registry available to namespace builtins.
1178pub fn with_namespace_registry<R>(
1179    registry: &NamespaceRegistry<Value>,
1180    operation: impl FnOnce() -> R,
1181) -> R {
1182    ACTIVE_NAMESPACES.with(|active| {
1183        let previous = active.replace(Some(registry.clone()));
1184        let result = operation();
1185        active.replace(previous);
1186        result
1187    })
1188}
1189
1190pub fn with_definition_origin<R>(origin: VarOrigin, operation: impl FnOnce() -> R) -> R {
1191    ACTIVE_DEFINITION_ORIGIN.with(|active| {
1192        let previous = active.replace(origin);
1193        let result = operation();
1194        active.set(previous);
1195        result
1196    })
1197}
1198
1199pub(crate) fn definition_origin() -> VarOrigin {
1200    ACTIVE_DEFINITION_ORIGIN.with(Cell::get)
1201}
1202
1203pub(crate) fn binding_is_local(var: &KernelVar<Value>) -> bool {
1204    namespace_registry()
1205        .map(|registry| {
1206            var.symbol().get_namespace().is_none()
1207                || var.symbol().get_namespace() == Some(registry.current().name().as_str())
1208        })
1209        .unwrap_or(true)
1210}
1211
1212/// Names a fresh local var cell: qualified to the current namespace when
1213/// a registry is active, bare otherwise. Qualifying matters: an
1214/// unqualified cell fails `binding_is_local`, so redefining the name in
1215/// the same eval used to shadow with a fresh cell instead of resetting
1216/// the existing one — the answer then depended on whether the name had
1217/// survived a previous eval's namespace save-back (which qualifies
1218/// cells). The JVM runtime always resets the same cell; qualifying at
1219/// creation makes the tree evaluator agree on both first and later
1220/// evals (issue #223).
1221pub(crate) fn local_var_name(name: &str) -> String {
1222    match namespace_registry() {
1223        Ok(registry) => format!("{}/{}", registry.current().name().as_str(), name),
1224        Err(_) => name.to_string(),
1225    }
1226}
1227
1228fn prepare_owned_definition(env: &mut HashMap<String, Value>, name: &str) -> Result<(), String> {
1229    if let Some(Value::Var(var)) = env.get(name) {
1230        if !binding_is_local(var) {
1231            if let Ok(registry) = namespace_registry() {
1232                registry.current().unmap(&Symbol::parse(name));
1233            }
1234            env.remove(name);
1235        }
1236    }
1237    Ok(())
1238}
1239
1240/// Defines or updates a global var in the current namespace, mirroring
1241/// the evaluator's `def` arm (`core.rs` special forms) without the flat
1242/// env bridge: an existing var local to the current namespace is reused
1243/// (identity preserved), a referred or missing name gets a fresh cell.
1244/// Used by the bytecode VM's `DefGlobal` (issue #223).
1245pub(crate) fn vm_def_global(
1246    name: &str,
1247    value: Value,
1248    metadata: Option<Rc<Metadata>>,
1249) -> Result<KernelVar<Value>, String> {
1250    let registry = namespace_registry()?;
1251    let current = registry.current();
1252    let local = Symbol::create(None, name);
1253    if let Some(existing) = current.resolve(&local) {
1254        if binding_is_local(&existing) {
1255            existing.reset_value(value);
1256            if metadata.is_some() {
1257                existing.set_hara_metadata(metadata);
1258            }
1259            existing.set_origin(definition_origin());
1260            refresh_schema_contract(&existing)?;
1261            return Ok(existing);
1262        }
1263        current.unmap(&local);
1264    }
1265    let var = KernelVar::new(format!("{}/{}", current.name().as_str(), name), value);
1266    var.set_hara_metadata(metadata);
1267    var.set_origin(definition_origin());
1268    current.map_var(local, var.clone());
1269    refresh_schema_contract(&var)?;
1270    Ok(var)
1271}
1272
1273pub(crate) fn vm_def_macro(
1274    name: &str,
1275    value: Value,
1276    metadata: Option<Rc<Metadata>>,
1277) -> Result<KernelVar<Value>, String> {
1278    let Value::Function(function) = value else {
1279        return Err("defmacro expects a function value".into());
1280    };
1281    // VM closures are materialized as regular native functions.  A macro must
1282    // retain its macro classification in the Var value as well as in the
1283    // current namespace macro registry: `intern-var` uses this flag when a
1284    // facade re-exports the macro into another namespace.
1285    let function = if function.is_macro {
1286        function
1287    } else {
1288        let mut macro_function = (*function).clone();
1289        macro_function.is_macro = true;
1290        Rc::new(macro_function)
1291    };
1292    let value = Value::Function(function.clone());
1293    let namespace = namespace_registry()?.current().name().as_str().to_owned();
1294    let var = vm_def_global(name, value, metadata)?;
1295    register_macro(&namespace, name, function)?;
1296    Ok(var)
1297}
1298
1299/// Declares a global var without assigning it, mirroring the evaluator's
1300/// `declare` arm: an existing local var is kept (value untouched), a
1301/// missing name gets a fresh nil cell. Used by the VM (issue #223).
1302pub(crate) fn vm_declare_global(name: &str) -> Result<KernelVar<Value>, String> {
1303    let registry = namespace_registry()?;
1304    let current = registry.current();
1305    let local = Symbol::create(None, name);
1306    if let Some(existing) = current.resolve(&local) {
1307        if binding_is_local(&existing) {
1308            existing.set_origin(definition_origin());
1309            return Ok(existing);
1310        }
1311        // An explicit `declare` is the source-level ownership boundary.  It
1312        // authorizes the following definition to replace a referred Var in
1313        // this namespace; a direct definition still goes through the
1314        // compiler's ownership check and remains protected.
1315        current.unmap(&local);
1316    }
1317    let var = KernelVar::new(format!("{}/{}", current.name().as_str(), name), Value::Nil);
1318    var.set_origin(definition_origin());
1319    current.map_var(local, var.clone());
1320    Ok(var)
1321}
1322
1323/// Resolves a global var by (possibly qualified) name through the
1324/// registry: current-namespace mappings, aliases, and qualified names.
1325pub(crate) fn vm_resolve_global(name: &str) -> Result<KernelVar<Value>, String> {
1326    let registry = namespace_registry()?;
1327    if let Some(var) = registry.resolve(&Symbol::parse(name)) {
1328        return Ok(var);
1329    }
1330    if let Some((namespace, _)) = name.rsplit_once('/') {
1331        if NAMESPACE_SOURCE_PROVIDER.with(|active| {
1332            active
1333                .borrow()
1334                .as_ref()
1335                .is_some_and(|provider| provider(namespace).is_some())
1336        }) {
1337            require_namespace(&registry, &mut HashMap::new(), namespace)?;
1338            if let Some(var) = registry.resolve(&Symbol::parse(name)) {
1339                return Ok(var);
1340            }
1341        }
1342    }
1343    Err(format!("unbound symbol: {name}"))
1344}
1345
1346/// Resolves a bare namespace symbol as the evaluator does. Namespace aliases
1347/// are values (and therefore callable through their `run` Var), but they are
1348/// not Vars themselves and cannot be represented by `vm_resolve_global`.
1349/// Lazy aliases are materialized at execution time so compiled programs keep
1350/// the same load boundary as interpreted forms.
1351pub(crate) fn vm_resolve_namespace_value(name: &str) -> Result<Value, String> {
1352    let registry = namespace_registry()?;
1353    if let Some(namespace) = registry
1354        .current()
1355        .aliases()
1356        .into_iter()
1357        .find_map(|(alias, namespace)| (alias.as_str() == name).then_some(namespace))
1358    {
1359        return Ok(Value::Namespace(Rc::new(namespace)));
1360    }
1361    if let Some(target) = registry.current().lazy_target(name) {
1362        require_namespace(&registry, &mut HashMap::new(), target.as_str())?;
1363        let namespace = registry
1364            .find(target.as_str())
1365            .ok_or_else(|| format!("Cannot require missing namespace: {target}"))?;
1366        registry.current().alias(name, namespace.clone());
1367        return Ok(Value::Namespace(Rc::new(namespace)));
1368    }
1369    registry
1370        .find(name)
1371        .map(|namespace| Value::Namespace(Rc::new(namespace)))
1372        .ok_or_else(|| format!("unbound symbol: {name}"))
1373}
1374
1375fn validate_named_definition(kind: &str, name: &str, fields: &[NamedField]) -> Result<(), String> {
1376    if name.contains('/') {
1377        return Err(format!("{kind} name must be an unqualified symbol"));
1378    }
1379    if fields
1380        .iter()
1381        .any(|field| field.name.is_empty() || field.name.contains('/'))
1382    {
1383        return Err(format!("{kind} field names must be unqualified symbols"));
1384    }
1385    if fields
1386        .iter()
1387        .map(|field| &field.name)
1388        .collect::<HashSet<_>>()
1389        .len()
1390        != fields.len()
1391    {
1392        return Err(format!("Duplicate {kind} field"));
1393    }
1394    Ok(())
1395}
1396
1397/// Runs a source declaration as one registry operation.
1398///
1399/// Declarations touch the namespace registry, the active protocol dispatch
1400/// registry, and the flat evaluator environment. Restore all three views when
1401/// validation or a later inline extension fails.
1402pub(crate) fn with_declaration_transaction<R>(
1403    environment: &mut HashMap<String, Value>,
1404    operation: impl FnOnce(&mut HashMap<String, Value>) -> Result<R, String>,
1405) -> Result<R, String> {
1406    let registry = namespace_registry()?;
1407    let registry_snapshot = registry.snapshot();
1408    let environment_snapshot = environment.clone();
1409    let protocol_snapshot = ACTIVE_PROTOCOLS.with(|active| {
1410        active
1411            .borrow()
1412            .as_ref()
1413            .map(ProtocolRegistry::snapshot)
1414    });
1415    let multimethod_snapshot = snapshot_multimethods();
1416
1417    let result = operation(environment);
1418    if result.is_err() {
1419        registry.restore(registry_snapshot);
1420        *environment = environment_snapshot;
1421        if let Some(snapshot) = protocol_snapshot {
1422            ACTIVE_PROTOCOLS.with(|active| {
1423                if let Some(registry) = active.borrow().as_ref() {
1424                    registry.restore(snapshot);
1425                }
1426            });
1427        }
1428        restore_multimethods(multimethod_snapshot);
1429    }
1430    result
1431}
1432
1433fn prepare_named_binding(namespace: &crate::kernel::Namespace<Value>, name: &str) {
1434    let symbol = Symbol::parse(name);
1435    if let Some(existing) = namespace.resolve(&symbol) {
1436        if existing.symbol().get_namespace() != Some(namespace.name().as_str()) {
1437            namespace.unmap(&symbol);
1438        }
1439    }
1440}
1441
1442/// Publishes the type Var and its positional and map constructors for a
1443/// defstruct or defmutable declaration. `Base/struct` and `Base/mutable`
1444/// use this path after Foundation macro expansion.
1445pub(crate) fn publish_named_value(
1446    kind: &str,
1447    name: &str,
1448    fields: Vec<NamedField>,
1449    environment: &mut HashMap<String, Value>,
1450    metadata: Option<Rc<Metadata>>,
1451) -> Result<Value, String> {
1452    validate_named_definition(kind, name, &fields)?;
1453    let mutable = kind == "defmutable";
1454    let schema_form = named_value_schema_form(
1455        &format!("{}/{}", namespace_registry()?.current().name().as_str(), name),
1456        mutable,
1457        &fields,
1458    );
1459    let metadata = assoc_metadata(metadata, "schema", metadata_value(&schema_form)?)
1460        .ok_or_else(|| "named value schema metadata could not be created".to_string())?;
1461    let field_names = fields
1462        .iter()
1463        .map(|field| field.name.clone())
1464        .collect::<Vec<_>>();
1465    with_declaration_transaction(environment, |environment| {
1466        let registry = namespace_registry()?;
1467        let namespace = registry.current();
1468        let namespace_name = namespace.name().as_str().to_owned();
1469        let type_name = format!("{}/{}", namespace_name, name);
1470        let declaration = Rc::new(NamedDeclaration::new(
1471            type_name.clone(),
1472            mutable,
1473            fields.clone(),
1474            schema_form.clone(),
1475        ));
1476
1477        let (type_value, map_constructor) = if mutable {
1478            let ty = Rc::new(MutableType {
1479                name: type_name.clone(),
1480                fields: field_names.clone(),
1481                declaration: Some(declaration.clone()),
1482            });
1483            let map_type = ty.clone();
1484            let constructor = native_function(&format!("map->{}", name), 1, move |values| {
1485                let source = values.first().expect("native arity is checked");
1486                let values = map_type
1487                    .fields
1488                    .iter()
1489                    .map(|field| {
1490                        map_value(source, &named_field_key(field))
1491                            .cloned()
1492                            .unwrap_or(Value::Nil)
1493                    })
1494                    .collect();
1495                Ok(Value::Mutable(Rc::new(MutableValue::from_values(
1496                    map_type.clone(),
1497                    values,
1498                    None,
1499                )?)))
1500            });
1501            (Value::MutableType(ty), constructor)
1502        } else {
1503            let ty = Rc::new(StructType {
1504                name: type_name.clone(),
1505                fields: field_names,
1506                declaration: Some(declaration),
1507            });
1508            let map_type = ty.clone();
1509            let constructor = native_function(&format!("map->{}", name), 1, move |values| {
1510                let source = values.first().expect("native arity is checked");
1511                let values = map_type
1512                    .fields
1513                    .iter()
1514                    .map(|field| {
1515                        map_value(source, &named_field_key(field))
1516                            .cloned()
1517                            .unwrap_or(Value::Nil)
1518                    })
1519                    .collect();
1520                Ok(Value::Struct(Rc::new(StructValue::from_values(
1521                    map_type.clone(),
1522                    values,
1523                    None,
1524                )?)))
1525            });
1526            (Value::StructType(ty), constructor)
1527        };
1528
1529        let bindings = [
1530            (name.to_owned(), type_value.clone()),
1531            (format!("->{}", name), type_value),
1532            (format!("map->{}", name), map_constructor),
1533        ];
1534        for (binding, value) in bindings {
1535            prepare_named_binding(&namespace, &binding);
1536            let var = namespace.intern(&binding, value);
1537            var.set_origin(definition_origin());
1538            if binding == name {
1539                var.set_hara_metadata(Some(metadata.clone()));
1540                refresh_schema_contract(&var)?;
1541            }
1542            environment.insert(binding.clone(), Value::Var(var.clone()));
1543            environment.insert(
1544                format!("{}/{}", namespace_name, binding),
1545                Value::Var(var),
1546            );
1547        }
1548        Ok(Value::Nil)
1549    })
1550}
1551
1552/// Publishes a guest protocol and all of its method Vars through one
1553/// namespace/dispatch transaction.
1554pub(crate) fn publish_guest_protocol(
1555    name: &str,
1556    methods: HashMap<String, usize>,
1557    parents: Vec<String>,
1558    environment: &mut HashMap<String, Value>,
1559) -> Result<Value, String> {
1560    if name.contains('/') || name.is_empty() {
1561        return Err("defprotocol name must be an unqualified symbol".into());
1562    }
1563    if methods.keys().any(|method| method.contains('/')) {
1564        return Err("protocol method names must be unqualified symbols".into());
1565    }
1566    if methods
1567        .iter()
1568        .any(|(method, arity)| method.is_empty() || *arity == 0)
1569    {
1570        return Err("protocol methods must have a receiver and a non-empty name".into());
1571    }
1572    if parents.iter().any(|parent| parent.is_empty()) {
1573        return Err("protocol parent names must not be empty".into());
1574    }
1575    with_declaration_transaction(environment, |environment| {
1576        let registry = namespace_registry()?;
1577        let namespace = registry.current();
1578        let namespace_name = namespace.name().as_str().to_owned();
1579        let protocol_name = format!("{}.{}", namespace_name, name);
1580        ACTIVE_PROTOCOLS.with(|active| -> Result<(), String> {
1581            let registry = active.borrow();
1582            let registry = registry
1583                .as_ref()
1584                .ok_or_else(|| "protocol registry is unavailable".to_string())?;
1585            if parents.iter().any(|parent| {
1586                parent == &protocol_name || registry.guest_protocol_reaches(parent, &protocol_name)
1587            }) {
1588                return Err(format!("protocol inheritance cycle: {protocol_name}"));
1589            }
1590            Ok(())
1591        })?;
1592        let previous_protocol = namespace
1593            .resolve(&Symbol::parse(name))
1594            .filter(|var| var.symbol().get_namespace() == Some(namespace_name.as_str()))
1595            .and_then(|var| match var.deref_value() {
1596                Value::Protocol(protocol) if protocol.name == protocol_name => Some(protocol),
1597                _ => None,
1598            });
1599
1600        for method in methods.keys() {
1601            for (local, var) in namespace.mappings() {
1602                if local.as_str() == name
1603                    || var.symbol().get_namespace() != Some(namespace_name.as_str())
1604                {
1605                    continue;
1606                }
1607                if let Value::Protocol(other) = var.deref_value() {
1608                    if other.methods.contains_key(method) {
1609                        return Err(format!(
1610                            "Protocol method Var already belongs to {}: {}/{}",
1611                            local.as_str(),
1612                            namespace_name,
1613                            method
1614                        ));
1615                    }
1616                }
1617            }
1618            let existing = namespace.resolve(&Symbol::parse(method));
1619            let same_protocol_reload = previous_protocol
1620                .as_ref()
1621                .is_some_and(|previous| previous.methods.contains_key(method));
1622            if existing
1623                .as_ref()
1624                .is_some_and(|var| var.symbol().get_namespace() == Some(namespace_name.as_str()))
1625                && !same_protocol_reload
1626            {
1627                return Err(format!(
1628                    "Protocol method Var already exists: {}/{}",
1629                    namespace_name,
1630                    method
1631                ));
1632            }
1633        }
1634
1635        if let Some(previous) = &previous_protocol {
1636            for old_method in previous.methods.keys() {
1637                if !methods.contains_key(old_method) {
1638                    let old = Symbol::parse(old_method);
1639                    if namespace.resolve(&old).is_some_and(|var| {
1640                        var.symbol().get_namespace() == Some(namespace_name.as_str())
1641                    }) {
1642                        namespace.unmap(&old);
1643                    }
1644                    environment.remove(old_method);
1645                    environment.remove(&format!("{}/{}", namespace_name, old_method));
1646                }
1647            }
1648        }
1649
1650        for method in methods.keys() {
1651            prepare_named_binding(&namespace, method);
1652        }
1653        prepare_named_binding(&namespace, name);
1654
1655        let protocol = Rc::new(GuestProtocol {
1656            name: protocol_name.clone(),
1657            methods,
1658            parents,
1659        });
1660        let protocol_value = Value::Protocol(protocol.clone());
1661        ACTIVE_PROTOCOLS.with(|active| -> Result<(), String> {
1662            let registry = active.borrow();
1663            let registry = registry
1664                .as_ref()
1665                .ok_or_else(|| "protocol registry is unavailable".to_string())?;
1666            registry.replace_guest_protocol(protocol_name.clone());
1667            registry.register_guest_protocol(protocol.clone());
1668            for method in protocol.methods.keys() {
1669                registry.declare_guest(protocol_name.clone(), method.clone());
1670            }
1671            Ok(())
1672        })?;
1673
1674        let protocol_var = namespace.intern(name, protocol_value.clone());
1675        protocol_var.set_origin(definition_origin());
1676        environment.insert(name.to_owned(), Value::Var(protocol_var.clone()));
1677        environment.insert(
1678            format!("{}/{}", namespace_name, name),
1679            Value::Var(protocol_var),
1680        );
1681        for method in protocol.methods.keys() {
1682            let protocol_name = protocol_name.clone();
1683            let method_name = method.clone();
1684            let display_name = format!("{}/{}", namespace_name, method);
1685            let method_value = native_variadic_function(&display_name, move |arguments| {
1686                protocol_call(&protocol_name, &method_name, &arguments)
1687            });
1688            let method_var = namespace.intern(method, method_value);
1689            method_var.set_origin(definition_origin());
1690            environment.insert(method.clone(), Value::Var(method_var.clone()));
1691            environment.insert(
1692                format!("{}/{}", namespace_name, method),
1693                Value::Var(method_var),
1694            );
1695        }
1696        Ok(protocol_value)
1697    })
1698}
1699
1700/// Direct field access is reserved for mutable named values. Immutable
1701/// structs use ordinary associative lookup.
1702pub(crate) fn mutable_field_value(value: &Value, field: &str) -> Result<Value, String> {
1703    let Value::Mutable(value) = value else {
1704        return Err("field expects a mutable value".into());
1705    };
1706    value
1707        .get(field)
1708        .ok_or_else(|| format!("unknown mutable field: {field}"))
1709}
1710
1711/// Replaces one declared mutable field and returns the replacement value.
1712pub(crate) fn mutable_field_set(
1713    value: &Value,
1714    field: &str,
1715    replacement: Value,
1716) -> Result<Value, String> {
1717    let Value::Mutable(value) = value else {
1718        return Err("field expects a mutable value".into());
1719    };
1720    value.set(field, replacement)
1721}
1722
1723/// Named-value type identity check shared with the `instance?` special form.
1724pub(crate) fn named_instance_of(type_value: &Value, value: &Value) -> Result<Value, String> {
1725    let matches = match type_value {
1726        Value::StructType(ty) => {
1727            matches!(value, Value::Struct(value) if Rc::ptr_eq(ty, &value.ty))
1728        }
1729        Value::MutableType(ty) => {
1730            matches!(value, Value::Mutable(value) if Rc::ptr_eq(ty, &value.ty))
1731        }
1732        Value::NativeType(native) => native_type_instance(native, value)?,
1733        _ => return Err("instance? expects a struct or mutable type".into()),
1734    };
1735    Ok(Value::Bool(matches))
1736}
1737
1738pub(crate) fn namespace_registry() -> Result<NamespaceRegistry<Value>, String> {
1739    ACTIVE_NAMESPACES
1740        .with(|active| active.borrow().clone())
1741        .ok_or_else(|| "namespace runtime is unavailable".into())
1742}
1743
1744/// Returns a fresh evaluator environment for the registry's current
1745/// namespace, including its qualified and aliased bindings.
1746pub(crate) fn current_namespace_environment() -> Result<HashMap<String, Value>, String> {
1747    let registry = namespace_registry()?;
1748    let mut environment = registry
1749        .current()
1750        .mappings()
1751        .into_iter()
1752        .map(|(name, var)| (name.as_str().to_owned(), Value::Var(var)))
1753        .collect();
1754    refresh_namespace_environment(&registry, &mut environment);
1755    Ok(environment)
1756}
1757
1758/// Saves all unqualified evaluator bindings into the registry current namespace.
1759pub fn save_namespace_environment(
1760    registry: &NamespaceRegistry<Value>,
1761    env: &mut HashMap<String, Value>,
1762) {
1763    let namespace = registry.current();
1764    let namespace_name = namespace.name().as_str().to_owned();
1765    let locals = env
1766        .iter()
1767        .filter(|(name, _)| !name.contains('/'))
1768        .map(|(name, value)| (name.clone(), value.clone()))
1769        .collect::<Vec<_>>();
1770    for (name, value) in locals {
1771        let path = format!("{namespace_name}/{name}");
1772        if matches!(&value, Value::Var(var) if
1773            (var.symbol().get_namespace().is_some()
1774                && var.symbol().get_namespace() != Some(namespace_name.as_str()))
1775                || var.symbol().as_str().starts_with("std.native.")
1776                || var.symbol().as_str().starts_with("std.protocol.")
1777        )
1778        {
1779            continue;
1780        }
1781        let var = match value {
1782            Value::Var(var) if var.symbol().as_str() == path => var,
1783            Value::Var(var) => var.requalify(&path),
1784            value => namespace.intern(&name, value),
1785        };
1786        namespace.map_var(crate::lang::data::Symbol::parse(&name), var.clone());
1787        env.insert(name, Value::Var(var));
1788    }
1789}
1790
1791/// Rebuilds qualified and aliased bindings without changing local bindings.
1792pub fn refresh_namespace_environment(
1793    registry: &NamespaceRegistry<Value>,
1794    env: &mut HashMap<String, Value>,
1795) {
1796    env.retain(|name, _| !name.contains('/'));
1797    for namespace in registry.all() {
1798        for (_, var) in namespace.mappings() {
1799            env.insert(var.symbol().as_str().to_owned(), Value::Var(var));
1800        }
1801    }
1802    for (alias, namespace) in registry.current().aliases() {
1803        for (local, var) in namespace.mappings() {
1804            env.insert(
1805                format!("{}/{}", alias.as_str(), local.as_str()),
1806                Value::Var(var),
1807            );
1808        }
1809    }
1810}
1811
1812/// Saves the current namespace, selects name, and loads its bindings.
1813pub fn select_namespace_environment(
1814    registry: &NamespaceRegistry<Value>,
1815    env: &mut HashMap<String, Value>,
1816    name: &str,
1817) {
1818    save_namespace_environment(registry, env);
1819    let namespace = registry.set_current(name);
1820    *env = namespace
1821        .mappings()
1822        .into_iter()
1823        .map(|(name, var)| (name.as_str().to_owned(), Value::Var(var)))
1824        .collect();
1825    refresh_namespace_environment(registry, env);
1826}
1827
1828pub fn apply_global_aliases(registry: &NamespaceRegistry<Value>, namespace: &str) {
1829    let target = registry.find_or_create(namespace);
1830    for (alias, library) in registry.global_aliases() {
1831        if target.name() == &library {
1832            continue;
1833        }
1834        if let Some(source) = registry.find(library.as_str()) {
1835            target.alias(alias.as_str(), source);
1836        } else {
1837            target.lazy_alias(alias.as_str(), library.as_str());
1838        }
1839    }
1840}
1841
1842pub fn apply_global_imports(registry: &NamespaceRegistry<Value>, namespace: &str) {
1843    let target = registry.find_or_create(namespace);
1844    for (local, canonical) in registry.global_imports() {
1845        if target.resolve(&local).is_none() {
1846            if let Some(var) = registry.resolve(&canonical) {
1847                target.map_var(local, var);
1848            }
1849        }
1850    }
1851}
1852
1853/// Runs an evaluation with a registry available to protocol dispatch.
1854pub fn with_protocols<R>(registry: &ProtocolRegistry, operation: impl FnOnce() -> R) -> R {
1855    ACTIVE_PROTOCOLS.with(|active| {
1856        let previous = active.replace(Some(registry.clone()));
1857        let result = operation();
1858        active.replace(previous);
1859        result
1860    })
1861}
1862
1863pub fn with_package_catalog<R>(catalog: &PackageCatalog, operation: impl FnOnce() -> R) -> R {
1864    ACTIVE_PACKAGE_CATALOG.with(|active| {
1865        let previous = active.replace(Some(catalog.clone()));
1866        let result = operation();
1867        active.replace(previous);
1868        result
1869    })
1870}
1871
1872fn package_catalog() -> PackageCatalog {
1873    ACTIVE_PACKAGE_CATALOG.with(|active| active.borrow().clone().unwrap_or_default())
1874}
1875
1876/// Runs an evaluation through the selected runtime promise provider.
1877pub fn with_promise_provider<R>(
1878    provider: Rc<dyn PromiseProvider>,
1879    operation: impl FnOnce() -> R,
1880) -> R {
1881    ACTIVE_PROMISE_PROVIDER.with(|active| {
1882        let previous = active.replace(Some(provider));
1883        let result = operation();
1884        active.replace(previous);
1885        result
1886    })
1887}
1888
1889fn promise_provider() -> Rc<dyn PromiseProvider> {
1890    ACTIVE_PROMISE_PROVIDER.with(|active| {
1891        active
1892            .borrow()
1893            .clone()
1894            .unwrap_or_else(|| Rc::new(LocalPromiseProvider))
1895    })
1896}
1897/// Runs an evaluation through the selected runtime capability providers.
1898pub fn with_capability_providers<R>(
1899    file: Option<Rc<dyn FileProvider>>,
1900    socket: Option<Rc<dyn SocketProvider>>,
1901    process: bool,
1902    kernel: Option<Rc<KernelProvider>>,
1903    operation: impl FnOnce() -> R,
1904) -> R {
1905    ACTIVE_FILE_PROVIDER.with(|active_file| {
1906        ACTIVE_SOCKET_PROVIDER.with(|active_socket| {
1907            ACTIVE_KERNEL_PROVIDER.with(|active_kernel| {
1908                ACTIVE_PROCESS_ALLOWED.with(|active_process| {
1909                    let previous_file = active_file.replace(file);
1910                    let previous_socket = active_socket.replace(socket);
1911                    let previous_kernel = active_kernel.replace(kernel);
1912                    let previous_process = active_process.replace(process);
1913                    let result = operation();
1914                    active_file.replace(previous_file);
1915                    active_socket.replace(previous_socket);
1916                    active_kernel.replace(previous_kernel);
1917                    active_process.set(previous_process);
1918                    result
1919                })
1920            })
1921        })
1922    })
1923}
1924
1925pub type KernelProvider = dyn Fn(String, Vec<Value>) -> Result<Value, String>;
1926
1927fn kernel_provider(operation: &str) -> Result<Rc<KernelProvider>, String> {
1928    ACTIVE_KERNEL_PROVIDER.with(|active| {
1929        active
1930            .borrow()
1931            .clone()
1932            .ok_or_else(|| format!("std.native.Kernel/{operation} requires a kernel provider"))
1933    })
1934}
1935
1936fn file_provider(operation: &str) -> Result<Rc<dyn FileProvider>, String> {
1937    ACTIVE_FILE_PROVIDER.with(|active| {
1938        active
1939            .borrow()
1940            .clone()
1941            .ok_or_else(|| format!("{operation} is unsupported or file access is denied"))
1942    })
1943}
1944
1945fn socket_provider(operation: &str) -> Result<Rc<dyn SocketProvider>, String> {
1946    ACTIVE_SOCKET_PROVIDER.with(|active| {
1947        active
1948            .borrow()
1949            .clone()
1950            .ok_or_else(|| format!("{operation} is unsupported or network access is denied"))
1951    })
1952}
1953
1954pub(crate) fn native_capability_granted(capability: &str) -> bool {
1955    match capability {
1956        "kernel" | "sandbox" => ACTIVE_KERNEL_PROVIDER.with(|active| active.borrow().is_some()),
1957        "file" => ACTIVE_FILE_PROVIDER.with(|active| active.borrow().is_some()),
1958        "network" => ACTIVE_SOCKET_PROVIDER.with(|active| active.borrow().is_some()),
1959        "native-runtime" => ACTIVE_PROCESS_ALLOWED.get(),
1960        "host-call" => HOST_CALL_HANDLER.with(|active| active.borrow().is_some()),
1961        _ => false,
1962    }
1963}
1964
1965pub(crate) fn native_capability_error_value(
1966    native_type: &str,
1967    method: &str,
1968    capability: &str,
1969) -> Value {
1970    Value::ExceptionInfo(Rc::new(ExceptionInfo {
1971        message: format!(
1972            "std.native.{native_type}/{method} requires capability :{capability}"
1973        ),
1974        data: Box::new(Value::Map(
1975            [
1976                (
1977                    Value::Keyword("ex/code".into()),
1978                    Value::Keyword("native/capability-denied".into()),
1979                ),
1980                (
1981                    Value::Keyword("ex/class".into()),
1982                    Value::Keyword("ex.class/host".into()),
1983                ),
1984                (
1985                    Value::Keyword("native/type".into()),
1986                    Value::String(format!("std.native.{native_type}")),
1987                ),
1988                (
1989                    Value::Keyword("native/method".into()),
1990                    Value::String(method.into()),
1991                ),
1992                (
1993                    Value::Keyword("native/capability".into()),
1994                    Value::Keyword(capability.into()),
1995                ),
1996            ]
1997            .into_iter()
1998            .collect(),
1999        )),
2000        cause: None,
2001        provenance: Rc::new(RefCell::new(Default::default())),
2002    }))
2003}
2004
2005pub(crate) fn native_capability_denied(
2006    native_type: &str,
2007    method: &str,
2008    capability: &str,
2009) -> String {
2010    thrown_error(native_capability_error_value(native_type, method, capability))
2011}
2012
2013pub(crate) fn native_capability_denied_promise(
2014    native_type: &str,
2015    method: &str,
2016    capability: &str,
2017) -> Value {
2018    let promise = Promise::new();
2019    promise.reject_value(native_capability_error_value(native_type, method, capability));
2020    Value::Promise(promise)
2021}
2022
2023pub(crate) fn require_native_capability(
2024    native_type: &str,
2025    method: &str,
2026    capability: &str,
2027) -> Result<(), String> {
2028    native_capability_granted(capability)
2029        .then_some(())
2030        .ok_or_else(|| native_capability_denied(native_type, method, capability))
2031}
2032
2033fn require_process_access(operation: &str) -> Result<(), String> {
2034    ACTIVE_PROCESS_ALLOWED.with(|allowed| {
2035        allowed
2036            .get()
2037            .then_some(())
2038            .ok_or_else(|| {
2039                let method = operation
2040                    .strip_prefix("std.native.Process/")
2041                    .unwrap_or(operation);
2042                native_capability_denied("Process", method, "native-runtime")
2043            })
2044    })
2045}
2046
2047#[cfg(test)]
2048mod capability_profile_tests {
2049    use super::*;
2050    use std::collections::BTreeSet;
2051
2052    #[derive(Debug)]
2053    struct Profile {
2054        id: String,
2055        grants: BTreeSet<String>,
2056    }
2057
2058    fn profile_corpus() -> (Vec<String>, Vec<Profile>) {
2059        let source = include_str!("../../assets/native-capability-profiles-v1.edn");
2060        let root = read_edn(source).expect("capability profile corpus must parse");
2061        let format = field(&root, "format");
2062        assert_eq!(
2063            format,
2064            Value::String("hara.native/capability-profiles/v1".into())
2065        );
2066        let capabilities = keywords(field(&root, "capabilities"), "capabilities");
2067        let profiles = values(field(&root, "profiles"), "profiles")
2068            .into_iter()
2069            .map(|value| {
2070                let id = match field(&value, "id") {
2071                    Value::Keyword(id) => id,
2072                    _ => panic!("capability profile ids must be keywords"),
2073                };
2074                let grants = keywords(field(&value, "grants"), "profile grants")
2075                    .into_iter()
2076                    .collect();
2077                Profile {
2078                    id: id.as_str().to_owned(),
2079                    grants,
2080                }
2081            })
2082            .collect::<Vec<_>>();
2083        (capabilities, profiles)
2084    }
2085
2086    fn field(value: &Value, name: &str) -> Value {
2087        map_entries(value)
2088            .expect("capability profile records must be maps")
2089            .into_iter()
2090            .find_map(|(key, value)| {
2091                matches!(key, Value::Keyword(ref keyword) if keyword.as_str() == name)
2092                    .then_some(value)
2093            })
2094            .unwrap_or_else(|| panic!("capability profile record is missing :{name}"))
2095    }
2096
2097    fn values(value: Value, field: &str) -> Vec<Value> {
2098        match value {
2099            Value::Vector(values) => values.iter().cloned().collect(),
2100            Value::Tuple(values) => values.iter().cloned().collect(),
2101            _ => panic!("{field} must be a vector"),
2102        }
2103    }
2104
2105    fn keywords(value: Value, field: &str) -> Vec<String> {
2106        values(value, field)
2107            .into_iter()
2108            .map(|value| match value {
2109                Value::Keyword(keyword) => keyword.as_str().to_owned(),
2110                _ => panic!("{field} entries must be keywords"),
2111            })
2112            .collect()
2113    }
2114
2115    fn with_profile<R>(grants: &BTreeSet<String>, operation: impl FnOnce() -> R) -> R {
2116        let file = grants
2117            .contains("file")
2118            .then(|| Rc::new(NativeFileProvider::new(".")) as Rc<dyn FileProvider>);
2119        let socket = grants
2120            .contains("network")
2121            .then(|| Rc::new(NativeSocketProvider::default()) as Rc<dyn SocketProvider>);
2122        let kernel = grants
2123            .contains("kernel")
2124            .then(|| Rc::new(|_, _| Ok(Value::Nil)) as Rc<KernelProvider>);
2125        with_capability_providers(
2126            file,
2127            socket,
2128            grants.contains("native-runtime"),
2129            kernel,
2130            || {
2131                if grants.contains("host-call") {
2132                    with_host_calls(Rc::new(|_, _, _| Ok(Value::Nil)), operation)
2133                } else {
2134                    operation()
2135                }
2136            },
2137        )
2138    }
2139
2140    #[test]
2141    fn native_capability_profiles_are_shared_and_exact() {
2142        let (capabilities, profiles) = profile_corpus();
2143        assert_eq!(
2144            capabilities,
2145            [
2146                "kernel",
2147                "sandbox",
2148                "file",
2149                "network",
2150                "native-runtime",
2151                "host-call",
2152            ]
2153        );
2154        assert_eq!(
2155            profiles
2156                .iter()
2157                .map(|profile| profile.id.as_str())
2158                .collect::<Vec<_>>(),
2159            [
2160                "zero",
2161                "kernel-sandbox",
2162                "file",
2163                "network",
2164                "native-runtime",
2165                "host-call",
2166                "all"
2167            ]
2168        );
2169
2170        for profile in profiles {
2171            assert!(profile
2172                .grants
2173                .iter()
2174                .all(|grant| capabilities.contains(grant)));
2175            let observed = with_profile(&profile.grants, || {
2176                capabilities
2177                    .iter()
2178                    .filter(|capability| native_capability_granted(capability))
2179                    .cloned()
2180                    .collect::<BTreeSet<_>>()
2181            });
2182            assert_eq!(observed, profile.grants, "profile {}", profile.id);
2183
2184            with_profile(&profile.grants, || {
2185                for capability in &capabilities {
2186                    if profile.grants.contains(capability) {
2187                        require_native_capability("Profile", "probe", capability).unwrap();
2188                    } else {
2189                        let error =
2190                            require_native_capability("Profile", "probe", capability).unwrap_err();
2191                        assert!(error.contains("std.native.Profile/probe requires capability"));
2192                        assert!(error.contains(":native/capability-denied"));
2193                    }
2194                }
2195            });
2196        }
2197    }
2198}