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 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 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(¤t) {
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 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 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 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 #[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
1103pub 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
1177pub 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
1212pub(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
1240pub(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 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
1299pub(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 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
1323pub(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(®istry, &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
1346pub(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(®istry, &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
1397pub(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
1442pub(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
1552pub(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
1700pub(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
1711pub(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
1723pub(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
1744pub(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(®istry, &mut environment);
1755 Ok(environment)
1756}
1757
1758pub 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
1791pub 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
1812pub 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
1853pub 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
1876pub 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}
1897pub 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}