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