1use super::*;
2
3impl Default for Interpreter {
4 fn default() -> Self {
5 Self::new()
6 }
7}
8
9impl Interpreter {
10 pub fn new() -> Self {
11 let mut arena = Arena::new();
12 let mut global: HashMap<String, NanValue> = HashMap::new();
13
14 args::register_nv(&mut global, &mut arena);
16 console::register_nv(&mut global, &mut arena);
17 http::register_nv(&mut global, &mut arena);
18 http_server::register_nv(&mut global, &mut arena);
19 disk::register_nv(&mut global, &mut arena);
20 env::register_nv(&mut global, &mut arena);
21 random::register_nv(&mut global, &mut arena);
22 tcp::register_nv(&mut global, &mut arena);
23 #[cfg(feature = "terminal")]
24 terminal::register_nv(&mut global, &mut arena);
25 time::register_nv(&mut global, &mut arena);
26 bool::register_nv(&mut global, &mut arena);
27 int::register_nv(&mut global, &mut arena);
28 float::register_nv(&mut global, &mut arena);
29 string::register_nv(&mut global, &mut arena);
30 list::register_nv(&mut global, &mut arena);
31 map::register_nv(&mut global, &mut arena);
32 vector::register_nv(&mut global, &mut arena);
33 char::register_nv(&mut global, &mut arena);
34 byte::register_nv(&mut global, &mut arena);
35
36 {
38 use std::rc::Rc;
39 let mut members: Vec<(Rc<str>, NanValue)> = Vec::new();
40 let ok_idx = arena.push_builtin("__ctor:Result.Ok");
41 members.push((Rc::from("Ok"), NanValue::new_builtin(ok_idx)));
42 let err_idx = arena.push_builtin("__ctor:Result.Err");
43 members.push((Rc::from("Err"), NanValue::new_builtin(err_idx)));
44 for (name, builtin_name) in result::extra_members() {
45 let idx = arena.push_builtin(&builtin_name);
46 members.push((Rc::from(name), NanValue::new_builtin(idx)));
47 }
48 let ns_idx = arena.push(crate::nan_value::ArenaEntry::Namespace {
49 name: Rc::from("Result"),
50 members,
51 });
52 global.insert("Result".to_string(), NanValue::new_namespace(ns_idx));
53 }
54 {
56 use std::rc::Rc;
57 let mut members: Vec<(Rc<str>, NanValue)> = Vec::new();
58 let some_idx = arena.push_builtin("__ctor:Option.Some");
59 members.push((Rc::from("Some"), NanValue::new_builtin(some_idx)));
60 members.push((Rc::from("None"), NanValue::NONE));
61 for (name, builtin_name) in option::extra_members() {
62 let idx = arena.push_builtin(&builtin_name);
63 members.push((Rc::from(name), NanValue::new_builtin(idx)));
64 }
65 let ns_idx = arena.push(crate::nan_value::ArenaEntry::Namespace {
66 name: Rc::from("Option"),
67 members,
68 });
69 global.insert("Option".to_string(), NanValue::new_namespace(ns_idx));
70 }
71
72 let mut record_schemas = HashMap::new();
73 record_schemas.insert(
74 "HttpResponse".to_string(),
75 vec![
76 "status".to_string(),
77 "body".to_string(),
78 "headers".to_string(),
79 ],
80 );
81 record_schemas.insert(
82 "HttpRequest".to_string(),
83 vec![
84 "method".to_string(),
85 "path".to_string(),
86 "body".to_string(),
87 "headers".to_string(),
88 ],
89 );
90 record_schemas.insert(
91 "Header".to_string(),
92 vec!["name".to_string(), "value".to_string()],
93 );
94 record_schemas.insert(
95 "Tcp.Connection".to_string(),
96 vec!["id".to_string(), "host".to_string(), "port".to_string()],
97 );
98 #[cfg(feature = "terminal")]
99 record_schemas.insert(
100 "Terminal.Size".to_string(),
101 vec!["width".to_string(), "height".to_string()],
102 );
103
104 Interpreter {
105 env: vec![EnvFrame::Owned(global)],
106 env_base: 1,
107 arena,
108 module_cache: HashMap::new(),
109 record_schemas,
110 call_stack: Vec::new(),
111 active_local_slots: None,
112 memo_fns: HashSet::new(),
113 memo_cache: HashMap::new(),
114 replay_state: EffectReplayState::default(),
115 recording_sink: None,
116 verify_match_coverage: None,
117 runtime_policy: None,
118 cli_args: Vec::new(),
119 }
120 }
121
122 pub fn execution_mode(&self) -> ExecutionMode {
123 match self.replay_state.mode() {
124 EffectReplayMode::Normal => ExecutionMode::Normal,
125 EffectReplayMode::Record => ExecutionMode::Record,
126 EffectReplayMode::Replay => ExecutionMode::Replay,
127 }
128 }
129
130 pub fn set_execution_mode_normal(&mut self) {
131 self.replay_state.set_normal();
132 }
133
134 pub fn start_recording(&mut self) {
135 self.replay_state.start_recording();
136 }
137
138 pub fn start_replay(&mut self, effects: Vec<EffectRecord>, validate_args: bool) {
139 self.replay_state.start_replay(effects, validate_args);
140 }
141
142 pub fn take_recorded_effects(&mut self) -> Vec<EffectRecord> {
143 self.replay_state.take_recorded_effects()
144 }
145
146 pub fn replay_progress(&self) -> (usize, usize) {
147 self.replay_state.replay_progress()
148 }
149
150 pub fn ensure_replay_consumed(&self) -> Result<(), RuntimeError> {
151 self.replay_state
152 .ensure_replay_consumed()
153 .map_err(|err| match err {
154 crate::replay::ReplayFailure::Unconsumed { remaining } => {
155 RuntimeError::ReplayUnconsumed { remaining }
156 }
157 other => RuntimeError::Error(format!("invalid replay state: {:?}", other)),
158 })
159 }
160
161 pub fn configure_recording_sink(&mut self, cfg: RecordingConfig) {
162 self.recording_sink = Some(RecordingSink {
163 path: cfg.path,
164 request_id: cfg.request_id,
165 timestamp: cfg.timestamp,
166 program_file: cfg.program_file,
167 module_root: cfg.module_root,
168 entry_fn: cfg.entry_fn,
169 input: cfg.input,
170 });
171 }
172
173 pub fn recording_sink_path(&self) -> Option<std::path::PathBuf> {
174 self.recording_sink.as_ref().map(|s| s.path.clone())
175 }
176
177 pub fn persist_recording_snapshot(&self, output: RecordedOutcome) -> Result<(), RuntimeError> {
178 let Some(sink) = &self.recording_sink else {
179 return Ok(());
180 };
181
182 let recording = SessionRecording {
183 schema_version: 1,
184 request_id: sink.request_id.clone(),
185 timestamp: sink.timestamp.clone(),
186 program_file: sink.program_file.clone(),
187 module_root: sink.module_root.clone(),
188 entry_fn: sink.entry_fn.clone(),
189 input: sink.input.clone(),
190 effects: self.replay_state.recorded_effects().to_vec(),
191 output,
192 };
193
194 let json = session_recording_to_string_pretty(&recording);
195 std::fs::write(&sink.path, json).map_err(|e| {
196 RuntimeError::Error(format!(
197 "Cannot write recording '{}': {}",
198 sink.path.display(),
199 e
200 ))
201 })?;
202 Ok(())
203 }
204
205 pub fn enable_memo(&mut self, fns: HashSet<String>) {
207 self.memo_fns = fns;
208 }
209
210 pub fn set_runtime_policy(&mut self, config: crate::config::ProjectConfig) {
213 self.runtime_policy = Some(config);
214 }
215
216 pub fn set_cli_args(&mut self, args: Vec<String>) {
218 self.cli_args = args;
219 }
220
221 pub(super) fn check_runtime_policy(
224 &self,
225 name: &str,
226 args: &[Value],
227 ) -> Result<(), RuntimeError> {
228 if self.execution_mode() == ExecutionMode::Replay {
229 return Ok(());
230 }
231 let Some(policy) = &self.runtime_policy else {
232 return Ok(());
233 };
234
235 if name.starts_with("Http.") {
236 if let Some(Value::Str(url)) = args.first() {
237 policy
238 .check_http_host(name, url)
239 .map_err(RuntimeError::Error)?;
240 }
241 } else if name.starts_with("Disk.") {
242 if let Some(Value::Str(path)) = args.first() {
243 policy
244 .check_disk_path(name, path)
245 .map_err(RuntimeError::Error)?;
246 }
247 } else if name.starts_with("Env.")
248 && let Some(Value::Str(key)) = args.first()
249 {
250 policy
251 .check_env_key(name, key)
252 .map_err(RuntimeError::Error)?;
253 }
254
255 Ok(())
256 }
257
258 pub fn start_verify_match_coverage(&mut self, fn_name: &str) {
259 let Ok(fn_val) = self.lookup(fn_name) else {
260 self.verify_match_coverage = None;
261 return;
262 };
263 let Value::Fn(function) = fn_val else {
264 self.verify_match_coverage = None;
265 return;
266 };
267
268 let mut expected = std::collections::BTreeMap::new();
269 Self::collect_match_sites_from_fn_body(function.body.as_ref(), &mut expected);
270 if expected.is_empty() {
271 self.verify_match_coverage = None;
272 return;
273 }
274
275 self.verify_match_coverage = Some(VerifyMatchCoverageTracker {
276 target_fn: fn_name.to_string(),
277 expected_arms: expected,
278 visited_arms: HashMap::new(),
279 });
280 }
281
282 pub fn finish_verify_match_coverage(&mut self) -> Vec<VerifyMatchCoverageMiss> {
283 let Some(tracker) = self.verify_match_coverage.take() else {
284 return vec![];
285 };
286
287 let mut misses = Vec::new();
288 for ((line, arm_count), expected_total) in tracker.expected_arms {
289 let visited = tracker.visited_arms.get(&(line, arm_count));
290 let mut missing = Vec::new();
291 for arm_idx in 0..expected_total {
292 let covered = visited.is_some_and(|set| set.contains(&arm_idx));
293 if !covered {
294 missing.push(arm_idx);
295 }
296 }
297 if !missing.is_empty() {
298 misses.push(VerifyMatchCoverageMiss {
299 line,
300 total_arms: expected_total,
301 missing_arms: missing,
302 });
303 }
304 }
305 misses
306 }
307
308 pub(super) fn note_verify_match_arm(&mut self, line: usize, arm_count: usize, arm_idx: usize) {
309 let Some(tracker) = self.verify_match_coverage.as_mut() else {
310 return;
311 };
312 let Some(frame) = self.call_stack.last() else {
313 return;
314 };
315 if frame.name.as_str() != tracker.target_fn {
316 return;
317 }
318 let key = (line, arm_count);
319 if !tracker.expected_arms.contains_key(&key) {
320 return;
321 }
322 tracker.visited_arms.entry(key).or_default().insert(arm_idx);
323 }
324
325 fn collect_match_sites_from_fn_body(
326 body: &FnBody,
327 out: &mut std::collections::BTreeMap<MatchSiteKey, usize>,
328 ) {
329 for stmt in body.stmts() {
330 Self::collect_match_sites_from_stmt(stmt, out);
331 }
332 }
333
334 fn collect_match_sites_from_stmt(
335 stmt: &Stmt,
336 out: &mut std::collections::BTreeMap<MatchSiteKey, usize>,
337 ) {
338 match stmt {
339 Stmt::Binding(_, _, expr) | Stmt::Expr(expr) => {
340 Self::collect_match_sites_from_expr(expr, out);
341 }
342 }
343 }
344
345 fn collect_match_sites_from_expr(
346 expr: &Expr,
347 out: &mut std::collections::BTreeMap<MatchSiteKey, usize>,
348 ) {
349 match expr {
350 Expr::Match {
351 subject,
352 arms,
353 line,
354 } => {
355 out.insert((*line, arms.len()), arms.len());
356 Self::collect_match_sites_from_expr(subject, out);
357 for arm in arms {
358 Self::collect_match_sites_from_expr(&arm.body, out);
359 }
360 }
361 Expr::FnCall(fn_expr, args) => {
362 Self::collect_match_sites_from_expr(fn_expr, out);
363 for arg in args {
364 Self::collect_match_sites_from_expr(arg, out);
365 }
366 }
367 Expr::BinOp(_, left, right) => {
368 Self::collect_match_sites_from_expr(left, out);
369 Self::collect_match_sites_from_expr(right, out);
370 }
371 Expr::Attr(obj, _) | Expr::ErrorProp(obj) => {
372 Self::collect_match_sites_from_expr(obj, out);
373 }
374 Expr::Constructor(_, maybe_arg) => {
375 if let Some(arg) = maybe_arg {
376 Self::collect_match_sites_from_expr(arg, out);
377 }
378 }
379 Expr::InterpolatedStr(parts) => {
380 for part in parts {
381 if let StrPart::Parsed(expr) = part {
382 Self::collect_match_sites_from_expr(expr, out);
383 }
384 }
385 }
386 Expr::List(items) | Expr::Tuple(items) => {
387 for item in items {
388 Self::collect_match_sites_from_expr(item, out);
389 }
390 }
391 Expr::MapLiteral(entries) => {
392 for (key, value) in entries {
393 Self::collect_match_sites_from_expr(key, out);
394 Self::collect_match_sites_from_expr(value, out);
395 }
396 }
397 Expr::RecordCreate { fields, .. } => {
398 for (_, expr) in fields {
399 Self::collect_match_sites_from_expr(expr, out);
400 }
401 }
402 Expr::RecordUpdate { base, updates, .. } => {
403 Self::collect_match_sites_from_expr(base, out);
404 for (_, expr) in updates {
405 Self::collect_match_sites_from_expr(expr, out);
406 }
407 }
408 Expr::TailCall(boxed) => {
409 for arg in &boxed.1 {
410 Self::collect_match_sites_from_expr(arg, out);
411 }
412 }
413 Expr::Literal(_) | Expr::Ident(_) | Expr::Resolved(_) => {}
414 }
415 }
416
417 pub(super) fn push_env(&mut self, frame: EnvFrame) {
421 self.env.push(frame);
422 }
423
424 pub(super) fn pop_env(&mut self) {
425 if self.env.len() > 1 {
426 self.env.pop();
427 }
428 }
429
430 pub(super) fn last_owned_scope_mut(
431 &mut self,
432 ) -> Result<&mut HashMap<String, NanValue>, RuntimeError> {
433 let frame = self
434 .env
435 .last_mut()
436 .ok_or_else(|| RuntimeError::Error("No active scope".to_string()))?;
437 match frame {
438 EnvFrame::Owned(scope) => Ok(scope),
439 EnvFrame::Shared(_) | EnvFrame::Slots(_) => Err(RuntimeError::Error(
440 "Cannot define name in non-owned frame".to_string(),
441 )),
442 }
443 }
444
445 pub(super) fn lookup_nv(&self, name: &str) -> Result<NanValue, RuntimeError> {
447 let env = &self.env;
448 let mut i = env.len();
449 let base = self.env_base;
450 while i > base {
451 i -= 1;
452 match &env[i] {
453 EnvFrame::Owned(scope) => {
454 if let Some(v) = scope.get(name) {
455 return Ok(*v);
456 }
457 }
458 EnvFrame::Shared(scope) => {
459 if let Some(v) = scope.get(name) {
460 return Ok(*v);
461 }
462 }
463 EnvFrame::Slots(_) => {}
464 }
465 }
466 match &env[0] {
467 EnvFrame::Owned(scope) => scope.get(name).copied(),
468 EnvFrame::Shared(scope) => scope.get(name).copied(),
469 EnvFrame::Slots(_) => None,
470 }
471 .ok_or_else(|| RuntimeError::Error(format!("Undefined variable: '{}'", name)))
472 }
473
474 pub(super) fn global_scope_clone(&self) -> Result<HashMap<String, NanValue>, RuntimeError> {
475 let frame = self
476 .env
477 .first()
478 .ok_or_else(|| RuntimeError::Error("No global scope".to_string()))?;
479 match frame {
480 EnvFrame::Owned(scope) => Ok(scope.clone()),
481 EnvFrame::Shared(scope) => Ok((**scope).clone()),
482 EnvFrame::Slots(_) => Err(RuntimeError::Error(
483 "Invalid global scope frame: Slots".to_string(),
484 )),
485 }
486 }
487
488 pub fn lookup(&self, name: &str) -> Result<Value, RuntimeError> {
490 let nv = self.lookup_nv(name)?;
491 Ok(nv.to_value(&self.arena))
492 }
493
494 pub fn define(&mut self, name: String, val: Value) {
496 let nv = NanValue::from_value(&val, &mut self.arena);
497 if let Ok(scope) = self.last_owned_scope_mut() {
498 scope.insert(name, nv);
499 }
500 }
501
502 pub(super) fn define_nv(&mut self, name: String, nv: NanValue) {
504 if let Ok(scope) = self.last_owned_scope_mut() {
505 scope.insert(name, nv);
506 }
507 }
508
509 fn alias_exposed_type_namespaces(&mut self, module_val: &Value) {
510 let Value::Namespace { members, .. } = module_val else {
511 return;
512 };
513 for (name, member) in members {
514 if matches!(member, Value::Namespace { .. }) {
515 self.define(name.clone(), member.clone());
516 }
517 }
518 }
519
520 pub(super) fn lookup_slot(&self, slot: u16) -> Result<NanValue, RuntimeError> {
522 let idx = self.env.len() - 1;
523 match &self.env[idx] {
524 EnvFrame::Slots(v) => Ok(v[slot as usize]),
525 _ => Err(RuntimeError::Error(
526 "Resolved lookup on non-Slots frame".to_string(),
527 )),
528 }
529 }
530
531 pub(super) fn define_slot(&mut self, slot: u16, val: NanValue) {
533 let idx = self.env.len() - 1;
534 if let EnvFrame::Slots(v) = &mut self.env[idx] {
535 v[slot as usize] = val;
536 }
537 }
538
539 pub fn define_module_path(&mut self, path: &str, val: Value) -> Result<(), RuntimeError> {
540 let alias_source = val.clone();
541 let parts: Vec<&str> = path.split('.').filter(|s| !s.is_empty()).collect();
542 if parts.is_empty() {
543 return Err(RuntimeError::Error("Empty module path".to_string()));
544 }
545 if parts.len() == 1 {
546 self.define(parts[0].to_string(), val);
547 self.alias_exposed_type_namespaces(&alias_source);
548 return Ok(());
549 }
550
551 let head = parts[0];
552 let tail = &parts[1..];
553
554 let result = {
555 let scope = self.last_owned_scope_mut()?;
556 if let Some(existing_nv) = scope.remove(head) {
557 let existing = existing_nv.to_value(&self.arena);
558 match existing {
559 Value::Namespace { name, mut members } => {
560 Self::insert_namespace_path(&mut members, tail, val)?;
561 let ns = Value::Namespace { name, members };
562 let nv = NanValue::from_value(&ns, &mut self.arena);
563 let scope = self.last_owned_scope_mut()?;
565 scope.insert(head.to_string(), nv);
566 Ok(())
567 }
568 _ => {
569 let scope2 = self.last_owned_scope_mut()?;
571 scope2.insert(head.to_string(), existing_nv);
572 Err(RuntimeError::Error(format!(
573 "Cannot mount module '{}': '{}' is not a namespace",
574 parts.join("."),
575 head
576 )))
577 }
578 }
579 } else {
580 let mut members = HashMap::new();
581 Self::insert_namespace_path(&mut members, tail, val)?;
582 let ns = Value::Namespace {
583 name: head.to_string(),
584 members,
585 };
586 let nv = NanValue::from_value(&ns, &mut self.arena);
587 let scope = self.last_owned_scope_mut()?;
588 scope.insert(head.to_string(), nv);
589 Ok(())
590 }
591 };
592
593 if result.is_ok() {
594 self.alias_exposed_type_namespaces(&alias_source);
595 }
596 result
597 }
598
599 pub(super) fn insert_namespace_path(
600 scope: &mut HashMap<String, Value>,
601 parts: &[&str],
602 val: Value,
603 ) -> Result<(), RuntimeError> {
604 if parts.len() == 1 {
605 scope.insert(parts[0].to_string(), val);
606 return Ok(());
607 }
608
609 let head = parts[0];
610 let tail = &parts[1..];
611
612 if let Some(existing) = scope.remove(head) {
613 match existing {
614 Value::Namespace { name, mut members } => {
615 Self::insert_namespace_path(&mut members, tail, val)?;
616 scope.insert(head.to_string(), Value::Namespace { name, members });
617 Ok(())
618 }
619 _ => Err(RuntimeError::Error(format!(
620 "Cannot mount module '{}': '{}' is not a namespace",
621 parts.join("."),
622 head
623 ))),
624 }
625 } else {
626 let mut members = HashMap::new();
627 Self::insert_namespace_path(&mut members, tail, val)?;
628 scope.insert(
629 head.to_string(),
630 Value::Namespace {
631 name: head.to_string(),
632 members,
633 },
634 );
635 Ok(())
636 }
637 }
638
639 pub(super) fn module_cache_key(path: &Path) -> String {
640 canonicalize_path(path).to_string_lossy().to_string()
641 }
642
643 pub(super) fn module_decl(items: &[TopLevel]) -> Option<&Module> {
644 items.iter().find_map(|item| {
645 if let TopLevel::Module(m) = item {
646 Some(m)
647 } else {
648 None
649 }
650 })
651 }
652
653 pub(super) fn exposed_set(items: &[TopLevel]) -> Option<HashSet<String>> {
654 Self::module_decl(items).and_then(|m| {
655 if m.exposes.is_empty() {
656 None
657 } else {
658 Some(m.exposes.iter().cloned().collect())
659 }
660 })
661 }
662
663 pub(super) fn cycle_display(loading: &[String], next: &str) -> String {
664 let mut chain = loading
665 .iter()
666 .map(|key| {
667 Path::new(key)
668 .file_stem()
669 .and_then(|s| s.to_str())
670 .unwrap_or(key)
671 .to_string()
672 })
673 .collect::<Vec<_>>();
674 chain.push(
675 Path::new(next)
676 .file_stem()
677 .and_then(|s| s.to_str())
678 .unwrap_or(next)
679 .to_string(),
680 );
681 chain.join(" -> ")
682 }
683
684 pub fn load_module(
685 &mut self,
686 name: &str,
687 base_dir: &str,
688 loading: &mut Vec<String>,
689 loading_set: &mut HashSet<String>,
690 ) -> Result<Value, RuntimeError> {
691 let path = find_module_file(name, base_dir).ok_or_else(|| {
692 RuntimeError::Error(format!("Module '{}' not found in '{}'", name, base_dir))
693 })?;
694 let cache_key = Self::module_cache_key(&path);
695
696 if let Some(cached) = self.module_cache.get(&cache_key) {
697 return Ok(cached.clone());
698 }
699
700 if loading_set.contains(&cache_key) {
701 return Err(RuntimeError::Error(format!(
702 "Circular import: {}",
703 Self::cycle_display(loading, &cache_key)
704 )));
705 }
706
707 loading.push(cache_key.clone());
708 loading_set.insert(cache_key.clone());
709 let result = (|| -> Result<Value, RuntimeError> {
710 let src = std::fs::read_to_string(&path).map_err(|e| {
711 RuntimeError::Error(format!("Cannot read '{}': {}", path.display(), e))
712 })?;
713 let mut items = parse_source(&src).map_err(|e| {
714 RuntimeError::Error(format!("Parse error in '{}': {}", path.display(), e))
715 })?;
716 require_module_declaration(&items, &path.to_string_lossy())
717 .map_err(RuntimeError::Error)?;
718 crate::resolver::resolve_program(&mut items);
719
720 if let Some(module) = Self::module_decl(&items) {
721 let expected = name.rsplit('.').next().unwrap_or(name);
722 if module.name != expected {
723 return Err(RuntimeError::Error(format!(
724 "Module name mismatch: expected '{}' (from '{}'), found '{}' in '{}'",
725 expected,
726 name,
727 module.name,
728 path.display()
729 )));
730 }
731 }
732
733 let mut sub = Interpreter::new();
734
735 if let Some(module) = Self::module_decl(&items) {
736 for dep_name in &module.depends {
737 let dep_ns = self.load_module(dep_name, base_dir, loading, loading_set)?;
738 sub.define_module_path(dep_name, dep_ns)?;
739 }
740 }
741
742 for item in &items {
743 if let TopLevel::TypeDef(td) = item {
744 sub.register_type_def(td);
745 }
746 }
747 for item in &items {
748 if let TopLevel::FnDef(fd) = item {
749 sub.exec_fn_def(fd)?;
750 }
751 }
752 let sub_globals_nv = sub.global_scope_clone()?;
755 let mut reencoded_globals: HashMap<String, NanValue> =
756 HashMap::with_capacity(sub_globals_nv.len());
757 for (k, nv) in &sub_globals_nv {
758 let old_val = nv.to_value(&sub.arena);
759 let new_nv = NanValue::from_value(&old_val, &mut self.arena);
760 reencoded_globals.insert(k.clone(), new_nv);
761 }
762 let module_globals = Rc::new(reencoded_globals);
763
764 let exposed = Self::exposed_set(&items);
765 let opaque_set: HashSet<String> = Self::module_decl(&items)
766 .map(|m| m.exposes_opaque.iter().cloned().collect())
767 .unwrap_or_default();
768 let mut members = HashMap::new();
769 for item in &items {
770 match item {
771 TopLevel::FnDef(fd) => {
772 let include = match &exposed {
773 Some(set) => set.contains(&fd.name),
774 None => !fd.name.starts_with('_'),
775 };
776 if include {
777 let mut val = sub.lookup(&fd.name).map_err(|_| {
778 RuntimeError::Error(format!(
779 "Failed to export '{}.{}'",
780 name, fd.name
781 ))
782 })?;
783 if let Value::Fn(function) = &mut val {
784 Rc::make_mut(function).home_globals =
785 Some(Rc::clone(&module_globals));
786 }
787 members.insert(fd.name.clone(), val);
788 }
789 }
790 TopLevel::TypeDef(TypeDef::Sum {
791 name: type_name, ..
792 }) => {
793 if opaque_set.contains(type_name) {
795 continue;
796 }
797 let include = match &exposed {
798 Some(set) => set.contains(type_name),
799 None => !type_name.starts_with('_'),
800 };
801 if include {
802 let val = sub.lookup(type_name).map_err(|_| {
803 RuntimeError::Error(format!(
804 "Failed to export '{}.{}'",
805 name, type_name
806 ))
807 })?;
808 members.insert(type_name.clone(), val);
809 }
810 }
811 _ => {}
812 }
813 }
814
815 Ok(Value::Namespace {
816 name: name.to_string(),
817 members,
818 })
819 })();
820 loading.pop();
821 loading_set.remove(&cache_key);
822
823 match result {
824 Ok(ns) => {
825 self.module_cache.insert(cache_key, ns.clone());
826 Ok(ns)
827 }
828 Err(e) => Err(e),
829 }
830 }
831}