1use std::cell::Cell;
19
20use super::compile::{CompileError, Compiler};
21use super::native::CallSite;
22use super::{Cx, Throw, error};
23use crate::PropertyKey;
24use crate::builtins::{Native, native_closure, native_data};
25use crate::heap::object::{InternalData, InternalTag, ObjectKind, flags};
26use crate::runtime::Intrinsic;
27use crate::value::{ObjectId, ScopeId, Value};
28use crate::{ErrorKind, KeyKind};
29
30#[derive(Clone, Copy, Debug, PartialEq, Eq)]
34pub enum ModuleBinding {
35 Declaration { lexical: bool },
37 Import,
40 ImportMeta,
42}
43
44#[derive(Clone, Debug)]
46pub struct ModuleRequest {
47 pub specifier: String,
48 pub attributes: Vec<(String, String)>,
49}
50
51#[derive(Clone, Debug)]
54pub struct ModuleImport {
55 pub request: u32,
56 pub name: Option<String>,
57 pub binding: u16,
58}
59
60#[derive(Clone, Debug)]
62pub enum ExportTarget {
63 Local(u16),
65 Indirect { request: u32, name: Option<String> },
67}
68
69#[derive(Clone, Debug)]
70pub struct ModuleExport {
71 pub name: String,
72 pub target: ExportTarget,
73}
74
75#[derive(Clone, Copy, Debug)]
79pub struct ModuleInitializer {
80 pub binding: u16,
81 pub function: Option<(u32, bool)>,
82}
83
84#[derive(Clone, Debug, Default)]
86pub struct ModuleTables {
87 pub bindings: Vec<ModuleBinding>,
88 pub requests: Vec<ModuleRequest>,
89 pub imports: Vec<ModuleImport>,
90 pub exports: Vec<ModuleExport>,
91 pub star_exports: Vec<u32>,
92 pub initializers: Vec<ModuleInitializer>,
93 pub has_top_level_await: bool,
94}
95
96pub enum ModuleSource {
99 Compiled {
100 function: ObjectId,
101 tables: ModuleTables,
102 import_meta: Vec<(String, Value)>,
105 },
106 Json(JsonText),
107 Json5(JsonText),
109 Synthetic {
113 exports: Vec<(String, Value)>,
114 },
115}
116
117#[derive(Clone, Debug, Default, PartialEq, Eq)]
121pub struct JsonText {
122 pub units: Vec<u16>,
123 pub invalid: Vec<u32>,
125 pub no_column: Vec<u32>,
129}
130
131impl From<Vec<u16>> for JsonText {
132 fn from(units: Vec<u16>) -> Self {
133 JsonText {
134 units,
135 invalid: Vec::new(),
136 no_column: Vec::new(),
137 }
138 }
139}
140
141impl JsonText {
142 pub fn from_bytes(bytes: &[u8], decode: impl Fn(&[u8]) -> Option<(u32, usize)>) -> Self {
147 let mut text = JsonText {
148 units: Vec::with_capacity(bytes.len()),
149 invalid: Vec::new(),
150 no_column: Vec::new(),
151 };
152 let mut offset = 0;
153 while offset < bytes.len() {
154 let byte = bytes[offset];
155 if byte < 0x80 {
156 text.units.push(u16::from(byte));
157 offset += 1;
158 continue;
159 }
160 match decode(&bytes[offset..]) {
161 Some((code, used)) if code <= 0x10_ffff => {
162 if code <= 0xffff {
163 text.units.push(code as u16);
164 } else {
165 let scalar = code - 0x1_0000;
166 text.units.push(0xd800 | (scalar >> 10) as u16);
167 text.no_column.push(text.units.len() as u32);
168 text.units.push(0xdc00 | (scalar & 0x3ff) as u16);
169 }
170 offset += used;
171 }
172 _ => {
173 if (0x80..0xc0).contains(&byte) {
175 text.no_column.push(text.units.len() as u32);
176 }
177 text.invalid.push(text.units.len() as u32);
178 text.units.push(0x7f);
179 offset += 1;
180 }
181 }
182 }
183 text
184 }
185}
186
187pub type NormalizeFn =
189 fn(&mut Compiler<'_>, &str, &str, &[(String, String)]) -> Result<String, CompileError>;
190
191pub type LoadModuleFn =
193 fn(&mut Compiler<'_>, &str, &[(String, String)]) -> Result<ModuleSource, CompileError>;
194
195#[derive(Clone, Copy, Debug, PartialEq, Eq)]
197pub enum ModulePhase {
198 Load,
199 Link,
200 Evaluate,
201}
202
203#[derive(Clone, Copy)]
205pub struct ModuleHost {
206 pub normalize: NormalizeFn,
207 pub load: LoadModuleFn,
208}
209
210#[derive(Clone, Copy, Debug, PartialEq, Eq)]
213enum Status {
214 Unlinked,
215 Linking,
216 Linked,
217 Evaluating,
218 EvaluatingAsync,
219 Evaluated,
220}
221
222#[derive(Debug)]
224pub struct ModuleData {
225 name: String,
226 tables: ModuleTables,
227 status: Status,
228 requested: Vec<ObjectId>,
230 dfs_index: u32,
231 dfs_ancestor_index: u32,
232 async_evaluation: Option<u64>,
234 pending_async_dependencies: u32,
235 async_parents: Vec<ObjectId>,
236 cycle_root: Option<ObjectId>,
237}
238
239const ENVIRONMENT: usize = 0;
241const FUNCTION: usize = 1;
242const NAMESPACE: usize = 2;
243const ERROR: usize = 3;
244const CAPABILITY: usize = 4;
245const SLOTS: usize = 7;
246
247thread_local! {
248 static ASYNC_ORDER: Cell<u64> = const { Cell::new(0) };
249}
250
251fn data<'a>(cx: &'a Cx<'_>, module: ObjectId) -> &'a ModuleData {
252 match &cx.heap.internal(module).data {
253 InternalData::Module(data) => data,
254 _ => unreachable!("a module record"),
255 }
256}
257
258fn data_mut<'a>(cx: &'a mut Cx<'_>, module: ObjectId) -> &'a mut ModuleData {
259 match &mut cx.heap.internal_mut(module).data {
260 InternalData::Module(data) => data,
261 _ => unreachable!("a module record"),
262 }
263}
264
265fn slot(cx: &Cx<'_>, module: ObjectId, index: usize) -> Value {
266 cx.heap.internal_slot(module, index)
267}
268
269fn set_slot(cx: &mut Cx<'_>, module: ObjectId, index: usize, value: Value) {
270 cx.heap.set_internal_slot(module, index, value);
271}
272
273fn environment(cx: &Cx<'_>, module: ObjectId) -> ScopeId {
274 ScopeId(
275 slot(cx, module, ENVIRONMENT)
276 .as_object()
277 .expect("an environment")
278 .index(),
279 )
280}
281
282fn cell(cx: &Cx<'_>, module: ObjectId, binding: u16) -> Value {
284 cx.heap
285 .scope_get(environment(cx, module), 0, u32::from(binding))
286}
287
288fn compile_error(cx: &mut Cx<'_>, failure: CompileError) -> Throw {
289 super::compile::throw_failure(cx, failure)
290}
291
292fn load(cx: &mut Cx<'_>, name: &str, attributes: &[(String, String)]) -> Result<ObjectId, Throw> {
295 if let Some(&module) = cx.host.modules.get(name) {
296 return Ok(module);
297 }
298 let Some(host) = cx.host.module_host else {
299 return Err(error::throw(
300 cx,
301 ErrorKind::Internal,
302 "unsupported: modules",
303 ));
304 };
305 let source = (host.load)(
306 &mut Compiler::new(cx.heap, cx.static_code, cx.realm),
307 name,
308 attributes,
309 );
310 let source = source.map_err(|failure| compile_error(cx, failure))?;
311 let mark = cx.heap.roots().mark();
312 let result = match source {
313 ModuleSource::Compiled {
314 function,
315 tables,
316 import_meta,
317 } => {
318 cx.heap.roots().push(Value::object(function));
319 create(cx, name, Some(function), tables, &import_meta)
320 }
321 ModuleSource::Json(text) => json_module(cx, name, &text, false),
322 ModuleSource::Json5(text) => json_module(cx, name, &text, true),
323 ModuleSource::Synthetic { exports } => synthetic_module(cx, name, &exports),
324 };
325 cx.heap.roots().truncate(mark);
326 let module = result?;
327 let requests = data(cx, module).tables.requests.clone();
329 let mut requested = Vec::with_capacity(requests.len());
330 for request in &requests {
331 let normalized = (host.normalize)(
332 &mut Compiler::new(cx.heap, cx.static_code, cx.realm),
333 name,
334 &request.specifier,
335 &request.attributes,
336 );
337 let normalized = normalized.map_err(|failure| compile_error(cx, failure))?;
338 requested.push(load(cx, &normalized, &request.attributes)?);
339 }
340 data_mut(cx, module).requested = requested;
341 Ok(module)
342}
343
344fn json_module(
349 cx: &mut Cx<'_>,
350 name: &str,
351 text: &JsonText,
352 extended: bool,
353) -> Result<ObjectId, Throw> {
354 let value = crate::builtins::json_module_text(cx, text, name, extended)?;
355 cx.heap.roots().push(value);
356 synthetic_module(cx, name, &[("default".into(), value)])
357}
358
359fn synthetic_module(
362 cx: &mut Cx<'_>,
363 name: &str,
364 exports: &[(String, Value)],
365) -> Result<ObjectId, Throw> {
366 if exports.len() > usize::from(u16::MAX) {
367 return Err(error::throw(
368 cx,
369 ErrorKind::Range,
370 "too many module exports",
371 ));
372 }
373 for (_, value) in exports {
374 cx.heap.roots().push(*value);
375 }
376 let tables = ModuleTables {
377 bindings: vec![ModuleBinding::Declaration { lexical: false }; exports.len()],
378 exports: exports
379 .iter()
380 .enumerate()
381 .map(|(index, (name, _))| ModuleExport {
382 name: name.as_str().into(),
383 target: ExportTarget::Local(index as u16),
384 })
385 .collect(),
386 ..ModuleTables::default()
387 };
388 let module = create(cx, name, None, tables, &[])?;
389 for (index, (_, value)) in exports.iter().enumerate() {
390 let holder = cell(cx, module, index as u16);
391 cx.heap
392 .scope_set(ScopeId(holder.as_object().unwrap().index()), 0, 0, *value);
393 }
394 Ok(module)
395}
396
397fn create(
398 cx: &mut Cx<'_>,
399 name: &str,
400 function: Option<ObjectId>,
401 tables: ModuleTables,
402 import_meta: &[(String, Value)],
403) -> Result<ObjectId, Throw> {
404 let source = ModuleParts { function, tables };
405 let heap_error = |cx: &mut Cx<'_>, error| error::heap_error(cx, error);
406 let count = source.tables.bindings.len() as u32;
407 let scope = cx
408 .heap
409 .alloc_scope(None, count)
410 .map_err(|e| heap_error(cx, e))?;
411 cx.heap.roots().push(Value::object(scope.object()));
412 for (index, binding) in source.tables.bindings.iter().enumerate() {
413 let initial = match binding {
414 ModuleBinding::Declaration { lexical: true } => Value::EMPTY,
415 ModuleBinding::Declaration { lexical: false } | ModuleBinding::Import => {
416 Value::UNDEFINED
417 }
418 ModuleBinding::ImportMeta => {
419 let meta = cx
420 .heap
421 .alloc_object(ObjectKind::Ordinary, None, 2)
422 .map_err(|e| heap_error(cx, e))?;
423 cx.heap.roots().push(Value::object(meta));
424 for (key, value) in import_meta {
425 let key = cx.heap.property_key(key).map_err(|e| heap_error(cx, e))?;
426 cx.heap
427 .add_property(meta, key, crate::PropertyFlags::DEFAULT, *value)
428 .map_err(|e| heap_error(cx, e))?;
429 }
430 Value::object(meta)
431 }
432 };
433 if *binding == ModuleBinding::Import {
434 continue;
435 }
436 let cell = cx
437 .heap
438 .alloc_scope(None, 1)
439 .map_err(|e| heap_error(cx, e))?;
440 cx.heap.scope_set(cell, 0, 0, initial);
441 cx.heap
442 .scope_set(scope, 0, index as u32, Value::object(cell.object()));
443 }
444 if let Some(function) = source.function {
445 cx.heap.set_function_scope(function, scope);
446 }
447 let mut slots = vec![Value::UNDEFINED; SLOTS];
448 slots[ENVIRONMENT] = Value::object(scope.object());
449 slots[FUNCTION] = source.function.map_or(Value::UNDEFINED, Value::object);
450 slots[ERROR] = Value::EMPTY;
451 let record = ModuleData {
452 name: name.to_owned(),
453 tables: source.tables,
454 status: Status::Unlinked,
455 requested: Vec::new(),
456 dfs_index: 0,
457 dfs_ancestor_index: 0,
458 async_evaluation: None,
459 pending_async_dependencies: 0,
460 async_parents: Vec::new(),
461 cycle_root: None,
462 };
463 let module = cx
464 .heap
465 .alloc_internal(
466 ObjectKind::Builtin,
467 InternalTag::Module,
468 None,
469 0,
470 &slots,
471 InternalData::Module(Box::new(record)),
472 0,
473 )
474 .map_err(|e| heap_error(cx, e))?;
475 cx.heap.modules.push(Value::object(module));
476 cx.host.modules.insert(name.to_owned(), module);
477 Ok(module)
478}
479
480struct ModuleParts {
481 function: Option<ObjectId>,
482 tables: ModuleTables,
483}
484
485#[derive(Clone, Copy, Debug, PartialEq, Eq)]
489enum Resolution {
490 Binding(ObjectId, u16),
491 Namespace(ObjectId),
492}
493
494enum Resolved {
495 Found(Resolution),
496 NotFound,
497 Circular,
499 Ambiguous,
500}
501
502fn resolve_export(
504 cx: &Cx<'_>,
505 module: ObjectId,
506 name: &str,
507 set: &mut Vec<(ObjectId, String)>,
508) -> Resolved {
509 if set.iter().any(|(m, n)| *m == module && n == name) {
510 return Resolved::Circular;
511 }
512 set.push((module, name.to_owned()));
513 let record = data(cx, module);
514 for export in &record.tables.exports {
515 if export.name != name {
516 continue;
517 }
518 return match export.target.clone() {
519 ExportTarget::Local(binding) => {
520 match record
522 .tables
523 .imports
524 .iter()
525 .find(|import| import.binding == binding)
526 {
527 Some(import) => {
528 let imported = record.requested[import.request as usize];
529 let import_name = import.name.clone();
530 match import_name {
531 None => Resolved::Found(Resolution::Namespace(imported)),
532 Some(import_name) => resolve_export(cx, imported, &import_name, set),
533 }
534 }
535 None => Resolved::Found(Resolution::Binding(module, binding)),
536 }
537 }
538 ExportTarget::Indirect {
539 request,
540 name: import_name,
541 } => {
542 let imported = record.requested[request as usize];
543 match import_name {
544 None => Resolved::Found(Resolution::Namespace(imported)),
545 Some(import_name) => resolve_export(cx, imported, &import_name, set),
546 }
547 }
548 };
549 }
550 if name == "default" {
551 return Resolved::NotFound;
552 }
553 let mut star: Option<Resolution> = None;
554 for &request in &record.tables.star_exports {
555 let imported = record.requested[request as usize];
556 match resolve_export(cx, imported, name, set) {
557 Resolved::Ambiguous => return Resolved::Ambiguous,
558 Resolved::NotFound | Resolved::Circular => {}
559 Resolved::Found(resolution) => match star {
560 None => star = Some(resolution),
561 Some(existing) if existing != resolution => return Resolved::Ambiguous,
562 Some(_) => {}
563 },
564 }
565 }
566 match star {
567 Some(resolution) => Resolved::Found(resolution),
568 None => Resolved::NotFound,
569 }
570}
571
572fn exported_names(cx: &Cx<'_>, module: ObjectId, set: &mut Vec<ObjectId>) -> Vec<String> {
574 if set.contains(&module) {
575 return Vec::new();
576 }
577 set.push(module);
578 let record = data(cx, module);
579 let mut names: Vec<String> = record
580 .tables
581 .exports
582 .iter()
583 .map(|export| export.name.clone())
584 .collect();
585 for &request in &record.tables.star_exports {
586 let imported = record.requested[request as usize];
587 for name in exported_names(cx, imported, set) {
588 if name != "default" && !names.contains(&name) {
589 names.push(name);
590 }
591 }
592 }
593 names
594}
595
596fn syntax(cx: &mut Cx<'_>, message: String) -> Throw {
597 error::throw(cx, ErrorKind::Syntax, &message)
598}
599
600#[cold]
602#[inline(never)]
603fn resolve_error(cx: &mut Cx<'_>, resolved: &Resolved, module: ObjectId, name: &str) -> Throw {
604 let name = super::property::str_atom_text(name);
605 let module = super::property::str_atom_text(&data(cx, module).name);
606 let message = match resolved {
607 Resolved::Circular => {
608 format!("circular reference when looking for export '{name}' in module '{module}'")
609 }
610 Resolved::Ambiguous => format!("export '{name}' in module '{module}' is ambiguous"),
611 _ => format!("Could not find export '{name}' in module '{module}'"),
612 };
613 syntax(cx, message)
614}
615
616pub(crate) fn namespace(cx: &mut Cx<'_>, module: ObjectId) -> Result<ObjectId, Throw> {
618 if let Some(namespace) = slot(cx, module, NAMESPACE).as_object() {
619 return Ok(namespace);
620 }
621 let names = exported_names(cx, module, &mut Vec::new());
622 let mut exports: Vec<(Vec<u16>, Resolution)> = Vec::new();
623 for name in names {
624 if let Resolved::Found(resolution) = resolve_export(cx, module, &name, &mut Vec::new()) {
625 exports.push((name.encode_utf16().collect(), resolution));
626 }
627 }
628 exports.sort_by(|a, b| a.0.cmp(&b.0));
629 let mut keys = Vec::with_capacity(exports.len());
630 let mut slots = vec![Value::object(module)];
631 let mark = cx.heap.roots().mark();
633 let namespace = (|| {
634 for (name, resolution) in &exports {
635 let key = cx
636 .heap
637 .property_key_units(name)
638 .map_err(|e| error::heap_error(cx, e))?;
639 if let Some(value) = key.heap_value() {
640 cx.heap.roots().push(value);
641 }
642 keys.push(key);
643 slots.push(match *resolution {
644 Resolution::Binding(owner, binding) => cell(cx, owner, binding),
645 Resolution::Namespace(owner) => Value::object(owner),
646 });
647 }
648 cx.heap
649 .alloc_internal(
650 ObjectKind::ModuleNamespace,
651 InternalTag::None,
652 None,
653 1,
654 &slots,
655 InternalData::Exports(std::mem::take(&mut keys).into_boxed_slice()),
656 0,
657 )
658 .map_err(|e| error::heap_error(cx, e))
659 })();
660 cx.heap.roots().truncate(mark);
661 let namespace = namespace?;
662 cx.heap.clear_object_flag(namespace, flags::EXTENSIBLE);
663 let mark = cx.heap.roots().mark();
665 cx.heap.roots().push(Value::object(namespace));
666 let tag = cx.heap.intern("Module");
667 cx.heap.roots().truncate(mark);
668 let tag = tag.map_err(|e| error::heap_error(cx, e))?;
669 let key = crate::runtime::WellKnown::key(cx.realm.symbols.to_string_tag);
670 cx.heap
671 .add_property(
672 namespace,
673 key,
674 crate::PropertyFlags::data(false, false, false),
675 Value::string(tag),
676 )
677 .map_err(|e| error::heap_error(cx, e))?;
678 set_slot(cx, module, NAMESPACE, Value::object(namespace));
679 Ok(namespace)
680}
681
682pub(crate) fn namespace_value(
685 cx: &mut Cx<'_>,
686 object: ObjectId,
687 position: usize,
688) -> Result<Value, Throw> {
689 let holder = cx.heap.internal_slot(object, position + 1);
690 let holder = holder.as_object().expect("a binding holder");
691 if cx.heap.internal_tag(holder) == InternalTag::Module {
692 return namespace(cx, holder).map(Value::object);
693 }
694 let value = cx.heap.scope_get(ScopeId(holder.index()), 0, 0);
695 if value.is_empty() {
696 let key = namespace_keys(cx, object)[position];
697 let message = format!(
698 "{} is not initialized",
699 super::property::atom_text(cx.heap, key)
700 );
701 return Err(error::throw(cx, ErrorKind::Reference, &message));
702 }
703 Ok(value)
704}
705
706pub(crate) fn namespace_keys<'a>(cx: &'a Cx<'_>, namespace: ObjectId) -> &'a [PropertyKey] {
708 match &cx.heap.internal(namespace).data {
709 InternalData::Exports(keys) => keys,
710 _ => unreachable!("a namespace"),
711 }
712}
713
714pub(crate) fn namespace_position(
716 cx: &Cx<'_>,
717 namespace: ObjectId,
718 key: PropertyKey,
719) -> Option<usize> {
720 if matches!(key.kind(), KeyKind::Symbol(_)) {
721 return None;
722 }
723 namespace_keys(cx, namespace)
724 .iter()
725 .position(|&export| export == key)
726}
727
728fn link(cx: &mut Cx<'_>, module: ObjectId) -> Result<(), Throw> {
732 let mut stack = Vec::new();
733 if let Err(throw) = inner_link(cx, module, &mut stack, 0) {
734 for module in stack {
735 data_mut(cx, module).status = Status::Unlinked;
736 }
737 return Err(throw);
738 }
739 Ok(())
740}
741
742fn inner_link(
744 cx: &mut Cx<'_>,
745 module: ObjectId,
746 stack: &mut Vec<ObjectId>,
747 mut index: u32,
748) -> Result<u32, Throw> {
749 if data(cx, module).status != Status::Unlinked {
750 return Ok(index);
751 }
752 let record = data_mut(cx, module);
753 record.status = Status::Linking;
754 record.dfs_index = index;
755 record.dfs_ancestor_index = index;
756 index += 1;
757 stack.push(module);
758 let requested = data(cx, module).requested.clone();
759 for required in requested {
760 index = inner_link(cx, required, stack, index)?;
761 let required = data(cx, required);
762 if required.status == Status::Linking {
763 let ancestor = required.dfs_ancestor_index;
764 let record = data_mut(cx, module);
765 record.dfs_ancestor_index = record.dfs_ancestor_index.min(ancestor);
766 }
767 }
768 initialize_environment(cx, module)?;
769 let record = data(cx, module);
770 if record.dfs_ancestor_index == record.dfs_index {
771 while let Some(member) = stack.pop() {
772 data_mut(cx, member).status = Status::Linked;
773 if member == module {
774 break;
775 }
776 }
777 }
778 Ok(index)
779}
780
781fn initialize_environment(cx: &mut Cx<'_>, module: ObjectId) -> Result<(), Throw> {
784 let tables = data(cx, module).tables.clone();
785 let requested = data(cx, module).requested.clone();
786 for export in &tables.exports {
787 if let ExportTarget::Indirect { name: Some(_), .. } = export.target {
788 match resolve_export(cx, module, &export.name, &mut Vec::new()) {
789 Resolved::Found(_) => {}
790 failure => return Err(resolve_error(cx, &failure, module, &export.name)),
791 }
792 }
793 }
794 let scope = environment(cx, module);
795 for import in &tables.imports {
796 let imported = requested[import.request as usize];
797 let holder = match &import.name {
798 None => {
799 let namespace = namespace(cx, imported)?;
800 let cell = cx
801 .heap
802 .alloc_scope(None, 1)
803 .map_err(|e| error::heap_error(cx, e))?;
804 cx.heap.scope_set(cell, 0, 0, Value::object(namespace));
805 Value::object(cell.object())
806 }
807 Some(name) => match resolve_export(cx, imported, name, &mut Vec::new()) {
808 Resolved::Found(Resolution::Binding(owner, binding)) => cell(cx, owner, binding),
809 Resolved::Found(Resolution::Namespace(owner)) => {
810 let namespace = namespace(cx, owner)?;
811 let cell = cx
812 .heap
813 .alloc_scope(None, 1)
814 .map_err(|e| error::heap_error(cx, e))?;
815 cx.heap.scope_set(cell, 0, 0, Value::object(namespace));
816 Value::object(cell.object())
817 }
818 failure => return Err(resolve_error(cx, &failure, imported, name)),
819 },
820 };
821 cx.heap
822 .scope_set(scope, 0, u32::from(import.binding), holder);
823 }
824 let Some(function) = slot(cx, module, FUNCTION).as_object() else {
825 return Ok(());
826 };
827 for initializer in &tables.initializers {
828 let value = match initializer.function {
829 None => Value::UNDEFINED,
830 Some((child, default_name)) => {
831 let closure = super::interp::closure_over_captures(cx, function, child)?;
832 if default_name {
833 let name = cx
834 .heap
835 .property_key("default")
836 .map_err(|e| error::heap_error(cx, e))?;
837 super::interp::set_function_name(cx, closure, name, "", false)?;
838 }
839 Value::object(closure)
840 }
841 };
842 let holder = cell(cx, module, initializer.binding);
843 cx.heap
844 .scope_set(ScopeId(holder.as_object().unwrap().index()), 0, 0, value);
845 }
846 Ok(())
847}
848
849fn evaluate(cx: &mut Cx<'_>, module: ObjectId) -> Result<ObjectId, Throw> {
853 let mut module = module;
854 let status = data(cx, module).status;
855 if matches!(status, Status::EvaluatingAsync | Status::Evaluated)
856 && let Some(root) = data(cx, module).cycle_root
857 {
858 module = root;
859 }
860 if let Some(promise) = slot(cx, module, CAPABILITY).as_object() {
861 return Ok(promise);
862 }
863 let prototype = cx.realm.intrinsic(Intrinsic::PromisePrototype);
864 let promise = super::jobs::new_promise(cx, prototype)?;
865 set_slot(cx, module, CAPABILITY, Value::object(promise));
866 let mut stack = Vec::new();
867 match inner_evaluate(cx, module, &mut stack, 0) {
868 Err(throw) if !super::error::catchable(cx) => return Err(throw),
869 Err(Throw(thrown)) => {
870 for member in stack {
871 data_mut(cx, member).status = Status::Evaluated;
872 set_slot(cx, member, ERROR, thrown);
873 }
874 super::jobs::reject_promise(cx, promise, thrown);
875 }
876 Ok(_) => {
877 if data(cx, module).async_evaluation.is_none() {
878 super::jobs::fulfill_promise(cx, promise, Value::UNDEFINED);
879 }
880 }
881 }
882 Ok(promise)
883}
884
885fn evaluation_error(cx: &Cx<'_>, module: ObjectId) -> Option<Value> {
887 let error = slot(cx, module, ERROR);
888 (!error.is_empty()).then_some(error)
889}
890
891fn inner_evaluate(
893 cx: &mut Cx<'_>,
894 module: ObjectId,
895 stack: &mut Vec<ObjectId>,
896 mut index: u32,
897) -> Result<u32, Throw> {
898 match data(cx, module).status {
899 Status::EvaluatingAsync | Status::Evaluated => {
900 return match evaluation_error(cx, module) {
901 Some(error) => Err(Throw(error)),
902 None => Ok(index),
903 };
904 }
905 Status::Evaluating => return Ok(index),
906 _ => {}
907 }
908 let record = data_mut(cx, module);
909 record.status = Status::Evaluating;
910 record.dfs_index = index;
911 record.dfs_ancestor_index = index;
912 record.pending_async_dependencies = 0;
913 index += 1;
914 stack.push(module);
915 let requested = data(cx, module).requested.clone();
916 for required in requested {
917 index = inner_evaluate(cx, required, stack, index)?;
918 let mut required = required;
919 if data(cx, required).status == Status::Evaluating {
920 let ancestor = data(cx, required).dfs_ancestor_index;
921 let record = data_mut(cx, module);
922 record.dfs_ancestor_index = record.dfs_ancestor_index.min(ancestor);
923 } else {
924 required = data(cx, required).cycle_root.unwrap_or(required);
925 if let Some(error) = evaluation_error(cx, required) {
926 return Err(Throw(error));
927 }
928 }
929 if data(cx, required).async_evaluation.is_some() {
930 data_mut(cx, module).pending_async_dependencies += 1;
931 data_mut(cx, required).async_parents.push(module);
932 }
933 }
934 let record = data(cx, module);
935 if record.pending_async_dependencies > 0 || record.tables.has_top_level_await {
936 let order = ASYNC_ORDER.with(|order| {
937 order.set(order.get() + 1);
938 order.get()
939 });
940 data_mut(cx, module).async_evaluation = Some(order);
941 if data(cx, module).pending_async_dependencies == 0 {
942 execute_async(cx, module)?;
943 }
944 } else {
945 execute(cx, module)?;
946 }
947 let record = data(cx, module);
948 if record.dfs_ancestor_index == record.dfs_index {
949 while let Some(member) = stack.pop() {
950 let record = data_mut(cx, member);
951 record.status = if record.async_evaluation.is_none() {
952 Status::Evaluated
953 } else {
954 Status::EvaluatingAsync
955 };
956 record.cycle_root = Some(module);
957 if member == module {
958 break;
959 }
960 }
961 }
962 Ok(index)
963}
964
965fn execute(cx: &mut Cx<'_>, module: ObjectId) -> Result<Value, Throw> {
968 let function = slot(cx, module, FUNCTION);
969 if function.is_undefined() {
970 return Ok(Value::UNDEFINED);
971 }
972 let result = super::call(cx, function, Value::UNDEFINED, &[])?;
973 if !data(cx, module).tables.has_top_level_await
976 && let Some(promise) = result.as_object()
977 && super::jobs::is_promise(cx, result)
978 && super::jobs::state(cx, promise) == super::jobs::REJECTED
979 {
980 cx.heap
981 .set_internal_slot(promise, super::jobs::HANDLED, Value::TRUE);
982 return Err(Throw(super::jobs::result(cx, promise)));
983 }
984 Ok(result)
985}
986
987fn execute_async(cx: &mut Cx<'_>, module: ObjectId) -> Result<(), Throw> {
989 let mark = cx.heap.roots().mark();
990 let result = (|| {
991 let data_object = native_data(cx, &[Value::object(module)])?;
992 cx.heap.roots().push(Value::object(data_object));
993 let fulfilled = native_closure(cx, Native::ModuleAsyncFulfilled, data_object)?;
994 cx.heap.roots().push(Value::object(fulfilled));
995 let rejected = native_closure(cx, Native::ModuleAsyncRejected, data_object)?;
996 cx.heap.roots().push(Value::object(rejected));
997 let promise = execute(cx, module)?;
998 cx.heap.roots().push(promise);
999 let promise = match promise.as_object() {
1000 Some(promise) if super::jobs::is_promise(cx, Value::object(promise)) => promise,
1001 _ => {
1002 let prototype = cx.realm.intrinsic(Intrinsic::PromisePrototype);
1004 let settled = super::jobs::new_promise(cx, prototype)?;
1005 super::jobs::fulfill_promise(cx, settled, Value::UNDEFINED);
1006 settled
1007 }
1008 };
1009 super::jobs::perform_then(
1010 cx,
1011 promise,
1012 Value::object(fulfilled),
1013 Value::object(rejected),
1014 None,
1015 Value::UNDEFINED,
1016 )
1017 })();
1018 cx.heap.roots().truncate(mark);
1019 result
1020}
1021
1022fn async_fulfilled(cx: &mut Cx<'_>, module: ObjectId) -> Result<(), Throw> {
1024 if data(cx, module).status == Status::Evaluated {
1025 return Ok(());
1026 }
1027 let record = data_mut(cx, module);
1028 record.async_evaluation = None;
1029 record.status = Status::Evaluated;
1030 if let Some(promise) = slot(cx, module, CAPABILITY).as_object() {
1031 super::jobs::fulfill_promise(cx, promise, Value::UNDEFINED);
1032 }
1033 let mut list = Vec::new();
1034 gather_available_ancestors(cx, module, &mut list);
1035 list.sort_by_key(|&member| data(cx, member).async_evaluation.unwrap_or(u64::MAX));
1036 for member in list {
1037 if data(cx, member).status == Status::Evaluated {
1038 continue;
1039 }
1040 if data(cx, member).tables.has_top_level_await {
1041 execute_async(cx, member)?;
1042 } else {
1043 match execute(cx, member) {
1044 Err(throw) if !super::error::catchable(cx) => return Err(throw),
1045 Err(Throw(thrown)) => async_rejected(cx, member, thrown),
1046 Ok(_) => {
1047 let record = data_mut(cx, member);
1048 record.async_evaluation = None;
1049 record.status = Status::Evaluated;
1050 if let Some(promise) = slot(cx, member, CAPABILITY).as_object() {
1051 super::jobs::fulfill_promise(cx, promise, Value::UNDEFINED);
1052 }
1053 }
1054 }
1055 }
1056 }
1057 Ok(())
1058}
1059
1060fn gather_available_ancestors(cx: &mut Cx<'_>, module: ObjectId, list: &mut Vec<ObjectId>) {
1062 let parents = data(cx, module).async_parents.clone();
1063 for parent in parents {
1064 let root = data(cx, parent).cycle_root.unwrap_or(parent);
1065 if list.contains(&parent) || evaluation_error(cx, root).is_some() {
1066 continue;
1067 }
1068 let record = data_mut(cx, parent);
1069 record.pending_async_dependencies = record.pending_async_dependencies.saturating_sub(1);
1070 if record.pending_async_dependencies == 0 {
1071 list.push(parent);
1072 if !data(cx, parent).tables.has_top_level_await {
1073 gather_available_ancestors(cx, parent, list);
1074 }
1075 }
1076 }
1077}
1078
1079fn async_rejected(cx: &mut Cx<'_>, module: ObjectId, error: Value) {
1081 if data(cx, module).status == Status::Evaluated {
1082 return;
1083 }
1084 set_slot(cx, module, ERROR, error);
1085 let record = data_mut(cx, module);
1086 record.status = Status::Evaluated;
1087 record.async_evaluation = None;
1088 if let Some(promise) = slot(cx, module, CAPABILITY).as_object() {
1089 super::jobs::reject_promise(cx, promise, error);
1090 }
1091 let parents = data(cx, module).async_parents.clone();
1092 for parent in parents {
1093 async_rejected(cx, parent, error);
1094 }
1095}
1096
1097pub(crate) fn module_async_fulfilled(cx: &mut Cx<'_>, site: CallSite) -> Result<Value, Throw> {
1098 let module = site.data(cx, 0).as_object().unwrap();
1099 async_fulfilled(cx, module)?;
1100 Ok(Value::UNDEFINED)
1101}
1102
1103pub(crate) fn module_async_rejected(cx: &mut Cx<'_>, site: CallSite) -> Result<Value, Throw> {
1104 let module = site.data(cx, 0).as_object().unwrap();
1105 let error = site.arg(cx, 0);
1106 async_rejected(cx, module, error);
1107 Ok(Value::UNDEFINED)
1108}
1109
1110pub(crate) fn run(cx: &mut Cx<'_>, name: &str) -> Result<ObjectId, Throw> {
1115 let module = load(cx, name, &[])?;
1116 link(cx, module)?;
1117 evaluate(cx, module)
1118}
1119
1120fn referrer(cx: &Cx<'_>, function: ObjectId) -> String {
1126 let unit_of = |function: ObjectId| {
1127 cx.heap
1128 .function_bytecode(function)
1129 .map(|bytecode| cx.unit(cx.heap.bytecode_unit(bytecode)))
1130 };
1131 let name = |unit: &crate::bytecode::CodeUnit| {
1132 unit.debug
1133 .filename
1134 .as_deref()
1135 .unwrap_or_default()
1136 .to_owned()
1137 };
1138 let Some(unit) = unit_of(function) else {
1139 return String::new();
1140 };
1141 if !unit.has(crate::bytecode::flags::EVAL) {
1142 return name(&unit);
1143 }
1144 let frames = &cx.heap.vm.frames;
1145 let below = frames
1146 .iter()
1147 .rposition(|frame| frame.function == function)
1148 .map_or(&frames[..0], |at| &frames[..at]);
1149 below
1150 .iter()
1151 .rev()
1152 .map(|frame| cx.unit(frame.unit))
1153 .find(|unit| !unit.has(crate::bytecode::flags::EVAL))
1154 .map(|unit| name(&unit))
1155 .unwrap_or_default()
1156}
1157
1158pub(crate) fn import(
1164 cx: &mut Cx<'_>,
1165 function: ObjectId,
1166 specifier: Value,
1167 options: Value,
1168) -> Result<Value, Throw> {
1169 let prototype = cx.realm.intrinsic(Intrinsic::PromisePrototype);
1170 let promise = super::jobs::new_promise(cx, prototype)?;
1171 let mark = cx.heap.roots().mark();
1172 cx.heap.roots().push(Value::object(promise));
1173 let outcome = (|| -> Result<(), Throw> {
1174 let specifier = super::ops::to_string(cx, specifier)?;
1175 cx.heap.roots().push(specifier);
1176 let attributes = import_attributes(cx, options)?;
1177 let kind = match attributes.iter().find(|(key, _)| key == "type") {
1178 Some((_, value)) => crate::builtins::string_text(cx, value)?,
1179 None => Value::UNDEFINED,
1180 };
1181 cx.heap.roots().push(kind);
1182 let referrer = referrer(cx, function);
1183 let referrer = crate::builtins::string_text(cx, &referrer)?;
1184 cx.heap.roots().push(referrer);
1185 let data_object = native_data(cx, &[Value::object(promise), specifier, referrer, kind])?;
1186 cx.heap.roots().push(Value::object(data_object));
1187 let job = native_closure(cx, Native::ModuleImportJob, data_object)?;
1188 cx.heap.jobs.push_back(super::jobs::Job::Call {
1189 function: Value::object(job),
1190 this: Value::UNDEFINED,
1191 args: Vec::new(),
1192 });
1193 Ok(())
1194 })();
1195 if let Err(Throw(thrown)) = outcome {
1196 if !super::error::catchable(cx) {
1197 cx.heap.roots().truncate(mark);
1198 return Err(Throw(thrown));
1199 }
1200 super::jobs::reject_promise(cx, promise, thrown);
1201 }
1202 cx.heap.roots().truncate(mark);
1203 Ok(Value::object(promise))
1204}
1205
1206pub(crate) fn module_import_job(cx: &mut Cx<'_>, site: CallSite) -> Result<Value, Throw> {
1209 let promise = site.data(cx, 0).as_object().unwrap();
1210 let specifier = site.data(cx, 1);
1211 let referrer = site.data(cx, 2);
1212 let kind = site.data(cx, 3);
1213 let outcome = (|| -> Result<(), Throw> {
1214 let specifier = cx.heap.string_text(specifier.as_string().unwrap());
1215 let referrer = cx.heap.string_text(referrer.as_string().unwrap());
1216 let attributes = match kind.as_string() {
1217 Some(kind) => vec![("type".to_owned(), cx.heap.string_text(kind))],
1218 None => Vec::new(),
1219 };
1220 let Some(host) = cx.host.module_host else {
1221 return Err(error::throw(
1222 cx,
1223 ErrorKind::Internal,
1224 "unsupported: modules",
1225 ));
1226 };
1227 let name = (host.normalize)(
1228 &mut Compiler::new(cx.heap, cx.static_code, cx.realm),
1229 &referrer,
1230 &specifier,
1231 &attributes,
1232 );
1233 let name = name.map_err(|failure| compile_error(cx, failure))?;
1234 let module = load(cx, &name, &attributes)?;
1235 link(cx, module)?;
1236 let evaluation = evaluate(cx, module)?;
1237 cx.heap.roots().push(Value::object(evaluation));
1238 let data_object = native_data(cx, &[Value::object(module), Value::object(promise)])?;
1239 cx.heap.roots().push(Value::object(data_object));
1240 let fulfilled = native_closure(cx, Native::ModuleImportFulfilled, data_object)?;
1241 cx.heap.roots().push(Value::object(fulfilled));
1242 let rejected = native_closure(cx, Native::ModuleImportRejected, data_object)?;
1243 cx.heap.roots().push(Value::object(rejected));
1244 super::jobs::perform_then(
1245 cx,
1246 evaluation,
1247 Value::object(fulfilled),
1248 Value::object(rejected),
1249 None,
1250 Value::UNDEFINED,
1251 )
1252 })();
1253 if let Err(Throw(thrown)) = outcome {
1254 if !super::error::catchable(cx) {
1255 return Err(Throw(thrown));
1256 }
1257 super::jobs::reject_promise(cx, promise, thrown);
1258 }
1259 Ok(Value::UNDEFINED)
1260}
1261
1262fn import_attributes(cx: &mut Cx<'_>, options: Value) -> Result<Vec<(String, String)>, Throw> {
1264 if options.is_undefined() {
1265 return Ok(Vec::new());
1266 }
1267 let Some(options) = options.as_object() else {
1268 return Err(error::type_error(cx, "options must be an object"));
1269 };
1270 let key = cx
1271 .heap
1272 .property_key("with")
1273 .map_err(|e| error::heap_error(cx, e))?;
1274 let with = super::object::get_property(cx, options, key)?;
1275 if with.is_undefined() {
1276 return Ok(Vec::new());
1277 }
1278 let Some(with) = with.as_object() else {
1279 return Err(error::type_error(cx, "options.with must be an object"));
1280 };
1281 let mut attributes = Vec::new();
1282 for key in super::object::enumerable_own_keys(cx, with)? {
1283 let value = super::object::get_property(cx, with, key)?;
1284 let Some(value) = value.as_string() else {
1285 return Err(error::type_error(
1286 cx,
1287 "module attribute values must be strings",
1288 ));
1289 };
1290 let value = cx.heap.string_text(value);
1291 attributes.push((super::property::key_text(cx.heap, key), value));
1292 }
1293 Ok(attributes)
1294}
1295
1296pub(crate) fn module_import_fulfilled(cx: &mut Cx<'_>, site: CallSite) -> Result<Value, Throw> {
1297 let module = site.data(cx, 0).as_object().unwrap();
1298 let promise = site.data(cx, 1).as_object().unwrap();
1299 match namespace(cx, module) {
1300 Ok(namespace) => super::jobs::resolve_promise(cx, promise, Value::object(namespace))?,
1301 Err(Throw(thrown)) => super::jobs::reject_promise(cx, promise, thrown),
1302 }
1303 Ok(Value::UNDEFINED)
1304}
1305
1306pub(crate) fn module_import_rejected(cx: &mut Cx<'_>, site: CallSite) -> Result<Value, Throw> {
1307 let promise = site.data(cx, 1).as_object().unwrap();
1308 let error = site.arg(cx, 0);
1309 super::jobs::reject_promise(cx, promise, error);
1310 Ok(Value::UNDEFINED)
1311}
1312
1313impl crate::Runtime {
1314 pub fn set_module_host(&mut self, host: ModuleHost) {
1316 self.host.module_host = Some(host);
1317 }
1318
1319 pub fn forget_module(&mut self, name: &str) {
1322 self.native_cx().forget_module(name);
1323 }
1324
1325 pub fn run_module(&mut self, name: &str) -> Result<Value, Throw> {
1328 self.native_cx().run_module(name)
1329 }
1330
1331 pub fn run_module_phased(&mut self, name: &str) -> Result<Value, (ModulePhase, Throw)> {
1334 let mut cx = self.cx();
1335 let module = load(&mut cx, name, &[]).map_err(|throw| (ModulePhase::Load, throw))?;
1336 link(&mut cx, module).map_err(|throw| (ModulePhase::Link, throw))?;
1337 evaluate(&mut cx, module)
1338 .map(Value::object)
1339 .map_err(|throw| (ModulePhase::Evaluate, throw))
1340 }
1341
1342 pub fn promise_state(&mut self, promise: Value) -> Option<(i32, Value)> {
1345 self.native_cx().promise_state(promise)
1346 }
1347}
1348
1349impl crate::NativeCx<'_> {
1350 pub fn forget_module(&mut self, name: &str) {
1352 self.host.modules.remove(name);
1353 }
1354
1355 pub fn run_module(&mut self, name: &str) -> Result<Value, Throw> {
1357 run(&mut self.cx(), name).map(Value::object)
1358 }
1359
1360 pub fn promise_state(&mut self, promise: Value) -> Option<(i32, Value)> {
1362 let cx = self.cx();
1363 let object = promise.as_object()?;
1364 if !super::jobs::is_promise(&cx, promise) {
1365 return None;
1366 }
1367 Some((
1368 super::jobs::state(&cx, object),
1369 super::jobs::result(&cx, object),
1370 ))
1371 }
1372}