From aa2af38cf2f1a3e9dd374247984c0e96c85b50f8 Mon Sep 17 00:00:00 2001 From: jedel1043 Date: Wed, 10 May 2023 22:15:17 -0600 Subject: [PATCH] Modify API and document some methods --- boa_cli/src/main.rs | 4 +- boa_engine/src/context/mod.rs | 21 +---- boa_engine/src/lib.rs | 2 + boa_engine/src/module/mod.rs | 154 +++++++++++++++++++++----------- boa_engine/src/module/source.rs | 134 ++++++++++++++------------- boa_tester/src/exec/mod.rs | 10 +-- 6 files changed, 177 insertions(+), 148 deletions(-) diff --git a/boa_cli/src/main.rs b/boa_cli/src/main.rs index f1b5ab9b879..37b42d9cc15 100644 --- a/boa_cli/src/main.rs +++ b/boa_cli/src/main.rs @@ -66,7 +66,7 @@ use boa_engine::{ builtins::promise::PromiseState, context::ContextBuilder, job::{FutureJob, JobQueue, NativeJob}, - module::{ModuleLoader, SimpleModuleLoader}, + module::{Module, ModuleLoader, SimpleModuleLoader}, optimizer::OptimizerOptions, property::Attribute, vm::flowgraph::{Direction, Graph}, @@ -305,7 +305,7 @@ fn evaluate_files( } } else if args.module { let result = (|| { - let module = context.parse_module(Source::from_bytes(&buffer), None)?; + let module = Module::parse(Source::from_bytes(&buffer), None, context)?; loader.insert( file.canonicalize() diff --git a/boa_engine/src/context/mod.rs b/boa_engine/src/context/mod.rs index 85ffb75799c..685ded85095 100644 --- a/boa_engine/src/context/mod.rs +++ b/boa_engine/src/context/mod.rs @@ -21,7 +21,7 @@ use crate::{ bytecompiler::ByteCompiler, class::{Class, ClassBuilder}, job::{JobQueue, NativeJob, SimpleJobQueue}, - module::{Module, ModuleLoader, SimpleModuleLoader, SourceTextModule}, + module::{ModuleLoader, SimpleModuleLoader}, native_function::NativeFunction, object::{shape::SharedShape, FunctionObjectBuilder, JsObject}, optimizer::{Optimizer, OptimizerOptions, OptimizerStatistics}, @@ -205,25 +205,6 @@ impl<'host> Context<'host> { Ok(result) } - /// Abstract operation [`ParseModule ( sourceText, realm, hostDefined )`][spec] - /// - /// Parses the given source as a module. - /// - /// [spec]: https://tc39.es/ecma262/#sec-parsemodule - pub fn parse_module( - &mut self, - src: Source<'_, R>, - realm: Option, - ) -> JsResult { - let _timer = Profiler::global().start_event("Module parsing", "Main"); - let mut parser = Parser::new(src); - let module = parser.parse_module(&mut self.interner)?; - Ok(SourceTextModule::new( - module, - realm.unwrap_or_else(|| self.realm.clone()), - )) - } - /// Compile the script AST into a `CodeBlock` ready to be executed by the VM. pub fn compile_script(&mut self, statement_list: &StatementList) -> JsResult> { let _timer = Profiler::global().start_event("Script compilation", "Main"); diff --git a/boa_engine/src/lib.rs b/boa_engine/src/lib.rs index a345a1e77b4..f55ea297a1a 100644 --- a/boa_engine/src/lib.rs +++ b/boa_engine/src/lib.rs @@ -175,6 +175,7 @@ pub mod vm; pub mod prelude { pub use crate::{ error::{JsError, JsNativeError, JsNativeErrorKind}, + module::Module, native_function::NativeFunction, object::JsObject, Context, JsBigInt, JsResult, JsString, JsValue, @@ -190,6 +191,7 @@ pub use crate::{ bigint::JsBigInt, context::Context, error::{JsError, JsNativeError, JsNativeErrorKind}, + module::Module, native_function::NativeFunction, object::JsObject, string::JsString, diff --git a/boa_engine/src/module/mod.rs b/boa_engine/src/module/mod.rs index 819a2cf57d0..9bbe2b974ce 100644 --- a/boa_engine/src/module/mod.rs +++ b/boa_engine/src/module/mod.rs @@ -1,7 +1,12 @@ -//! Module related types. +//! Boa's implementation of the ECMAScript's module system. +//! +//! More information: +//! - [ECMAScript reference][spec] +//! +//! [spec]: https://tc39.es/ecma262/#sec-modules mod source; -use boa_parser::Source; +use boa_parser::{Parser, Source}; use boa_profiler::Profiler; pub use source::SourceTextModule; @@ -12,6 +17,7 @@ use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; use std::cell::{Cell, RefCell}; use std::hash::Hash; +use std::io::Read; use std::path::{Path, PathBuf}; use std::rc::Rc; use std::{collections::HashSet, hash::BuildHasherDefault}; @@ -29,11 +35,11 @@ use crate::{ }; use crate::{js_string, JsNativeError, JsSymbol, NativeFunction}; -/// +/// The referrer from which a load request of a module originates. #[derive(Debug)] pub enum Referrer { /// - Module(SourceTextModule), + Module(Module), /// Realm(Realm), // TODO: script } @@ -43,10 +49,10 @@ pub trait ModuleLoader { /// Host hook [`HostLoadImportedModule ( referrer, specifier, hostDefined, payload )`][spec]. /// /// This hook allows to customize the module loading functionality of the engine. Technically, - /// this should return `()` instead of `JsResult`, leaving the responsibility of calling - /// [`FinishLoadingImportedModule`][finish] to the host, but this simpler API just provides - /// a closures that replaces [`FinishLoadingImportedModule`]. Specifically, the requirements of - /// this hook per the spec are as follows: + /// this should call the [`FinishLoadingImportedModule`][finish] operation, but this simpler API just provides + /// a closure that replaces [`FinishLoadingImportedModule`]. + /// + /// # Requirements /// /// - The host environment must perform `FinishLoadingImportedModule(referrer, specifier, payload, result)`, /// where result is either a normal completion containing the loaded Module Record or a throw @@ -144,7 +150,7 @@ impl ModuleLoader for SimpleModuleLoader { } let source = Source::from_filepath(&path) .map_err(|err| JsNativeError::typ().with_message(err.to_string()))?; - let module = context.parse_module(source, None)?; + let module = Module::parse(source, None, context)?; self.insert(path, module.clone()); Ok(module) })(); @@ -156,11 +162,23 @@ impl ModuleLoader for SimpleModuleLoader { /// ECMAScript's [**Abstract module record**][spec]. /// /// [spec]: https://tc39.es/ecma262/#sec-abstract-module-records -#[derive(Debug, Clone, Trace, Finalize)] +#[derive(Clone, Trace, Finalize)] pub struct Module { inner: Gc, } +impl std::fmt::Debug for Module { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Module") + .field("realm", &self.inner.realm.addr()) + .field("environment", &self.inner.environment) + .field("namespace", &self.inner.namespace) + .field("kind", &self.inner.kind) + .field("host_defined", &self.inner.host_defined) + .finish() + } +} + #[derive(Trace, Finalize)] struct Inner { realm: Realm, @@ -170,39 +188,29 @@ struct Inner { host_defined: (), } -impl std::fmt::Debug for Inner { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Inner") - .field("realm", &self.realm.addr()) - .field("environment", &self.environment) - .field("namespace", &self.namespace) - .field("kind", &self.kind) - .field("host_defined", &self.host_defined) - .finish() - } -} - +/// #[derive(Debug, Trace, Finalize)] -enum ModuleKind { - Source(SourceTextModule), +#[non_exhaustive] +pub enum ModuleKind { + /// + SourceText(SourceTextModule), + /// #[allow(unused)] Synthetic, } -#[derive(Debug, Clone)] -struct GraphLoadingState { - capability: PromiseCapability, - loading: Cell, - pending_modules: Cell, - visited: RefCell>, -} - #[derive(Debug, Clone)] pub(crate) struct ExportLocator { module: Module, binding_name: BindingName, } +#[derive(Debug, Clone, Copy)] +pub(crate) enum BindingName { + Name(Identifier), + Namespace, +} + impl ExportLocator { pub(crate) const fn module(&self) -> &Module { &self.module @@ -213,36 +221,71 @@ impl ExportLocator { } } -#[derive(Debug, Clone, Copy)] -pub(crate) enum BindingName { - Name(Identifier), - Namespace, +#[derive(Debug, Clone)] +struct GraphLoadingState { + capability: PromiseCapability, + loading: Cell, + pending_modules: Cell, + visited: RefCell>, } #[derive(Debug, Clone, Copy)] pub(crate) enum ResolveExportError { - NotFound(Sym), - Ambiguous(Sym), + NotFound, + Ambiguous, } impl Module { - pub(crate) fn realm(&self) -> &Realm { - &self.inner.realm + /// Abstract operation [`ParseModule ( sourceText, realm, hostDefined )`][spec]. + /// + /// Parses the provided `src` as an ECMAScript module, returning an error if parsing fails. + /// + /// [spec]: https://tc39.es/ecma262/#sec-parsemodule + pub fn parse( + src: Source<'_, R>, + realm: Option, + context: &mut Context<'_>, + ) -> JsResult { + let _timer = Profiler::global().start_event("Module parsing", "Main"); + let mut parser = Parser::new(src); + let module = parser.parse_module(context.interner_mut())?; + + Ok(Module { + inner: Gc::new(Inner { + realm: realm.unwrap_or_else(|| context.realm().clone()), + environment: GcRefCell::default(), + namespace: GcRefCell::default(), + kind: ModuleKind::SourceText(SourceTextModule::new(module)), + host_defined: (), + }), + }) } - pub(crate) fn environment(&self) -> Option> { - self.inner.environment.borrow().clone() + /// Gets the realm of this `Module`. + pub fn realm(&self) -> &Realm { + &self.inner.realm } - fn kind(&self) -> &ModuleKind { + /// Gets the kind of this `Module`. + pub fn kind(&self) -> &ModuleKind { &self.inner.kind } + /// Gets the environment of this `Module`. + pub(crate) fn environment(&self) -> Option> { + self.inner.environment.borrow().clone() + } + + /// Abstract operation [`LoadRequestedModules ( [ hostDefined ] )`][spec]. + /// + /// Prepares the module for linking by loading all its module dependencies. Returns a `JsPromise` + /// that will resolve when the loading process either completes or fails. /// + /// [spec]: https://tc39.es/ecma262/#table-abstract-methods-of-module-records #[allow(clippy::missing_panics_doc)] pub fn load(&self, context: &mut Context<'_>) -> JsPromise { match self.kind() { - ModuleKind::Source(_) => SourceTextModule::load(self, context), + ModuleKind::SourceText(_) => SourceTextModule::load(self, context), ModuleKind::Synthetic => todo!("synthetic.load()"), } } @@ -250,7 +293,7 @@ impl Module { fn inner_load(&self, state: &Rc, context: &mut Context<'_>) { assert!(state.loading.get()); - if let ModuleKind::Source(_) = self.kind() { + if let ModuleKind::SourceText(_) = self.kind() { SourceTextModule::inner_load(self, state, context); if !state.loading.get() { return; @@ -296,7 +339,7 @@ impl Module { fn get_exported_names(&self, export_star_set: &mut Vec) -> FxHashSet { match self.kind() { - ModuleKind::Source(src) => src.get_exported_names(export_star_set), + ModuleKind::SourceText(src) => src.get_exported_names(export_star_set), ModuleKind::Synthetic => todo!("synthetic.get_exported_names()"), } } @@ -308,7 +351,7 @@ impl Module { resolve_set: &mut FxHashSet<(Module, Sym)>, ) -> Result { match self.kind() { - ModuleKind::Source(_) => { + ModuleKind::SourceText(_) => { SourceTextModule::resolve_export(self, export_name, resolve_set) } ModuleKind::Synthetic => todo!("synthetic.resolve_export()"), @@ -319,7 +362,7 @@ impl Module { #[allow(clippy::missing_panics_doc)] pub fn link(&self, context: &mut Context<'_>) -> JsResult<()> { match self.kind() { - ModuleKind::Source(_) => SourceTextModule::link(self, context), + ModuleKind::SourceText(_) => SourceTextModule::link(self, context), ModuleKind::Synthetic => todo!("synthetic.link()"), } } @@ -331,7 +374,7 @@ impl Module { context: &mut Context<'_>, ) -> JsResult { match self.kind() { - ModuleKind::Source(_) => SourceTextModule::inner_link(self, stack, index, context), + ModuleKind::SourceText(_) => SourceTextModule::inner_link(self, stack, index, context), #[allow(unreachable_code)] ModuleKind::Synthetic => { todo!("synthetic.load()"); @@ -367,7 +410,7 @@ impl Module { #[allow(clippy::missing_panics_doc)] pub fn evaluate(&self, context: &mut Context<'_>) -> JsPromise { match self.kind() { - ModuleKind::Source(src) => src.evaluate(context), + ModuleKind::SourceText(src) => src.evaluate(context), ModuleKind::Synthetic => todo!("synthetic.evaluate()"), } } @@ -379,7 +422,7 @@ impl Module { context: &mut Context<'_>, ) -> JsResult { match self.kind() { - ModuleKind::Source(src) => src.inner_evaluate(stack, index, None, context), + ModuleKind::SourceText(src) => src.inner_evaluate(stack, index, None, context), #[allow(unused, clippy::diverging_sub_expression)] ModuleKind::Synthetic => { let promise: JsPromise = todo!("module.Evaluate()"); @@ -395,8 +438,8 @@ impl Module { } } - /// Loads, links and evaluates the given module `src` by compiling down to bytecode, then - /// returning a promise that will resolve when the module executes. + /// Loads, links and evaluates this module, returning a promise that will resolve after the module + /// finishes its lifecycle. /// /// # Examples /// ```ignore @@ -484,6 +527,9 @@ pub struct ModuleNamespace { } impl ModuleNamespace { + /// Abstract operation [`ModuleNamespaceCreate ( module, exports )`][spec]. + /// + /// [spec]: https://tc39.es/ecma262/#sec-modulenamespacecreate pub(crate) fn create(module: Module, names: Vec, context: &mut Context<'_>) -> JsObject { let mut exports = names .into_iter() @@ -517,10 +563,12 @@ impl ModuleNamespace { namespace } + /// Gets the export names of the `ModuleNamespace` object. pub(crate) const fn exports(&self) -> &IndexMap> { &self.exports } + /// Gest the module associated with this `ModuleNamespace` object. pub(crate) const fn module(&self) -> &Module { &self.module } diff --git a/boa_engine/src/module/source.rs b/boa_engine/src/module/source.rs index b250d0ad70b..92e590d3d90 100644 --- a/boa_engine/src/module/source.rs +++ b/boa_engine/src/module/source.rs @@ -265,8 +265,8 @@ impl std::fmt::Debug for SourceTextModule { } impl SourceTextModule { - #[allow(clippy::new_ret_no_self)] - pub(crate) fn new(code: ModuleItemList, realm: Realm) -> Module { + /// Creates a new `SourceTextModule` from a parsed `ModuleItemList`. + pub(super) fn new(code: ModuleItemList) -> Self { let requested_modules = code.requests(); let import_entries = code.import_entries(); @@ -302,7 +302,7 @@ impl SourceTextModule { let has_tla = contains(&code, ContainsSymbol::AwaitExpression); - let src = SourceTextModule(Gc::new(GcRefCell::new(SourceTextModuleData { + SourceTextModule(Gc::new(GcRefCell::new(SourceTextModuleData { code, requested_modules, has_tla, @@ -314,19 +314,15 @@ impl SourceTextModule { loaded_modules: HashMap::default(), async_parent_modules: Vec::default(), import_meta: None, - }))); - - Module { - inner: Gc::new(super::Inner { - realm, - environment: GcRefCell::new(None), - namespace: GcRefCell::new(None), - kind: ModuleKind::Source(src), - host_defined: (), - }), - } + }))) } + /// Abstract operation [`LoadRequestedModules ( [ hostDefined ] )`][spec]. + /// + /// Prepares the module for linking by loading all its module dependencies. Returns a `JsPromise` + /// that will resolve when the loading process either completes or fails. + /// + /// [spec]: https://tc39.es/ecma262/#table-abstract-methods-of-module-records pub(super) fn load(module: &Module, context: &mut Context<'_>) -> JsPromise { let pc = PromiseCapability::new( &context.intrinsics().constructors().promise().constructor(), @@ -353,7 +349,7 @@ impl SourceTextModule { state: &Rc, context: &mut Context<'_>, ) { - let ModuleKind::Source(src) = module.kind() else { + let ModuleKind::SourceText(src) = module.kind() else { unreachable!("must only be called for `SourceTextModule`s"); }; @@ -376,7 +372,7 @@ impl SourceTextModule { let src = src.clone(); let state = state.clone(); context.module_loader().load_imported_module( - Referrer::Module(src.clone()), + Referrer::Module(module.clone()), name_specifier, Box::new(move |completion, ctx| { if let Ok(loaded) = &completion { @@ -440,12 +436,12 @@ impl SourceTextModule { export_name: Sym, resolve_set: &mut FxHashSet<(Module, Sym)>, ) -> Result { - let ModuleKind::Source(src) = module.kind() else { + let ModuleKind::SourceText(src) = module.kind() else { unreachable!("must only be called for `SourceTextModule`s"); }; if resolve_set.contains(&(module.clone(), export_name)) { - return Err(ResolveExportError::NotFound(export_name)); + return Err(ResolveExportError::NotFound); } resolve_set.insert((module.clone(), export_name)); @@ -463,22 +459,20 @@ impl SourceTextModule { for e in &src.indirect_export_entries { if export_name == e.export_name() { let imported_module = &src.loaded_modules[&e.module_request()]; - match e.import_name() { - ReExportImportName::Star => { - return Ok(ExportLocator { - module: imported_module.clone(), - binding_name: BindingName::Namespace, - }) - } + return match e.import_name() { + ReExportImportName::Star => Ok(ExportLocator { + module: imported_module.clone(), + binding_name: BindingName::Namespace, + }), ReExportImportName::Name(_) => { - return imported_module.resolve_export(export_name, resolve_set); + imported_module.resolve_export(export_name, resolve_set) } - } + }; } } if export_name == Sym::DEFAULT { - return Err(ResolveExportError::NotFound(export_name)); + return Err(ResolveExportError::NotFound); } let mut star_resolution: Option = None; @@ -487,22 +481,22 @@ impl SourceTextModule { let imported_module = &src.loaded_modules[e]; let resolution = match imported_module.resolve_export(export_name, resolve_set) { Ok(resolution) => resolution, - Err(e @ ResolveExportError::Ambiguous(_)) => return Err(e), - Err(ResolveExportError::NotFound(_)) => continue, + Err(e @ ResolveExportError::Ambiguous) => return Err(e), + Err(ResolveExportError::NotFound) => continue, }; if let Some(star_resolution) = &star_resolution { // 1. Assert: There is more than one * import that includes the requested name. if resolution.module != star_resolution.module { - return Err(ResolveExportError::Ambiguous(export_name)); + return Err(ResolveExportError::Ambiguous); } match (resolution.binding_name, star_resolution.binding_name) { (BindingName::Namespace, BindingName::Name(_)) | (BindingName::Name(_), BindingName::Namespace) => { - return Err(ResolveExportError::Ambiguous(export_name)); + return Err(ResolveExportError::Ambiguous); } (BindingName::Name(res), BindingName::Name(star)) if res != star => { - return Err(ResolveExportError::Ambiguous(export_name)); + return Err(ResolveExportError::Ambiguous); } _ => {} } @@ -511,14 +505,14 @@ impl SourceTextModule { } } - star_resolution.ok_or(ResolveExportError::NotFound(export_name)) + star_resolution.ok_or(ResolveExportError::NotFound) } pub(super) fn link(module: &Module, context: &mut Context<'_>) -> JsResult<()> { - let ModuleKind::Source(src) = module.kind() else { + let ModuleKind::SourceText(src) = module.kind() else { unreachable!("must only be called for `SourceTextModule`s"); }; - assert!(matches!( + debug_assert!(matches!( src.borrow().status, Status::Unlinked | Status::Linked { .. } @@ -529,15 +523,15 @@ impl SourceTextModule { let mut stack = Vec::new(); if let Err(err) = Self::inner_link(module, &mut stack, 0, context) { - for source in stack { - assert!(matches!(source.borrow().status, Status::Linking { .. })); - source.borrow_mut().status = Status::Unlinked; + for m in stack { + debug_assert!(matches!(m.borrow().status, Status::Linking { .. })); + m.borrow_mut().status = Status::Unlinked; } assert!(matches!(src.borrow().status, Status::Unlinked)); return Err(err); } - assert!(matches!( + debug_assert!(matches!( src.borrow().status, Status::Linked { .. } | Status::EvaluatingAsync { .. } | Status::Evaluated { .. } )); @@ -552,7 +546,7 @@ impl SourceTextModule { mut index: usize, context: &mut Context<'_>, ) -> JsResult { - let ModuleKind::Source(src) = module.kind() else { + let ModuleKind::SourceText(src) = module.kind() else { unreachable!("must only be called for `SourceTextModule`s"); }; if matches!( @@ -566,7 +560,7 @@ impl SourceTextModule { return Ok(index); } - assert!(matches!(src.borrow().status, Status::Unlinked)); + debug_assert!(matches!(src.borrow().status, Status::Unlinked)); { let mut module = src.borrow_mut(); @@ -587,7 +581,7 @@ impl SourceTextModule { let required_module = src.borrow().loaded_modules[&required].clone(); index = required_module.inner_link(stack, index, context)?; - if let ModuleKind::Source(required_module) = required_module.kind() { + if let ModuleKind::SourceText(required_module) = required_module.kind() { debug_assert!(match required_module.borrow().status { Status::PreLinked { .. } | Status::Linked { .. } @@ -597,7 +591,7 @@ impl SourceTextModule { _ => false, }); - let req_index = if let Status::Linking { + let required_index = if let Status::Linking { info: DfsInfo { dfs_ancestor_index, .. @@ -609,7 +603,7 @@ impl SourceTextModule { None }; - if let Some(req_index) = req_index { + if let Some(required_index) = required_index { let mut module = src.borrow_mut(); let DfsInfo { @@ -618,7 +612,7 @@ impl SourceTextModule { .status .dfs_info_mut() .expect("should be on the linking state"); - *dfs_ancestor_index = usize::min(*dfs_ancestor_index, req_index); + *dfs_ancestor_index = usize::min(*dfs_ancestor_index, required_index); } } } @@ -803,7 +797,7 @@ impl SourceTextModule { let required_module = self.borrow().loaded_modules[&required].clone(); index = required_module.inner_evaluate(stack, index, context)?; - if let ModuleKind::Source(required_module) = required_module.kind() { + if let ModuleKind::SourceText(required_module) = required_module.kind() { debug_assert!(match required_module.borrow().status { Status::EvaluatingAsync { .. } | Status::Evaluated { .. } => true, Status::Evaluating { .. } if stack.contains(required_module) => true, @@ -1017,7 +1011,7 @@ impl SourceTextModule { }, } - let ModuleKind::Source(src) = module.kind() else { + let ModuleKind::SourceText(src) = module.kind() else { unreachable!("must only be called for `SourceTextModule`s"); }; @@ -1027,17 +1021,18 @@ impl SourceTextModule { module .resolve_export(e.export_name(), &mut HashSet::default()) .map_err(|err| match err { - ResolveExportError::NotFound(name) => { + ResolveExportError::NotFound => { JsNativeError::syntax().with_message(format!( "could not find export `{}`", - context.interner().resolve_expect(name) + context.interner().resolve_expect(e.export_name()) )) } - ResolveExportError::Ambiguous(name) => JsNativeError::syntax() - .with_message(format!( + ResolveExportError::Ambiguous => { + JsNativeError::syntax().with_message(format!( "could not resolve ambiguous export `{}`", - context.interner().resolve_expect(name) - )), + context.interner().resolve_expect(e.export_name()) + )) + } })?; } } @@ -1060,22 +1055,25 @@ impl SourceTextModule { let imported_module = &src.loaded_modules[&entry.module_request()]; if let ImportName::Name(name) = entry.import_name() { - let resolution = imported_module - .resolve_export(name, &mut HashSet::default()) - .map_err(|err| match err { - ResolveExportError::NotFound(name) => JsNativeError::syntax() - .with_message(format!( - "could not find export `{}`", - compiler.interner().resolve_expect(name) - )), - ResolveExportError::Ambiguous(name) => JsNativeError::syntax() - .with_message(format!( - "could not resolve ambiguous export `{}`", - compiler.interner().resolve_expect(name) - )), - })?; + let resolution = + imported_module + .resolve_export(name, &mut HashSet::default()) + .map_err(|err| match err { + ResolveExportError::NotFound => JsNativeError::syntax() + .with_message(format!( + "could not find export `{}`", + compiler.interner().resolve_expect(name) + )), + ResolveExportError::Ambiguous => JsNativeError::syntax() + .with_message(format!( + "could not resolve ambiguous export `{}`", + compiler.interner().resolve_expect(name) + )), + })?; + compiler.create_immutable_binding(entry.local_name(), true); let locator = compiler.initialize_immutable_binding(entry.local_name()); + if let BindingName::Name(_) = resolution.binding_name { imports.push(ImportBinding::Single { locator, diff --git a/boa_tester/src/exec/mod.rs b/boa_tester/src/exec/mod.rs index 5345f42e4a6..1b065267168 100644 --- a/boa_tester/src/exec/mod.rs +++ b/boa_tester/src/exec/mod.rs @@ -8,7 +8,7 @@ use crate::{ }; use boa_engine::{ builtins::promise::PromiseState, - module::{ModuleLoader, SimpleModuleLoader}, + module::{Module, ModuleLoader, SimpleModuleLoader}, native_function::NativeFunction, object::FunctionObjectBuilder, optimizer::OptimizerOptions, @@ -236,7 +236,7 @@ impl Test { // TODO: timeout let value = if self.is_module() { - let module = match context.parse_module(source, None) { + let module = match Module::parse(source, None, context) { Ok(module) => module, Err(err) => return (false, format!("Uncaught {err}")), }; @@ -306,7 +306,7 @@ impl Test { context.set_optimizer_options(OptimizerOptions::OPTIMIZE_ALL); if self.is_module() { - match context.parse_module(source, None) { + match Module::parse(source, None, context) { Ok(_) => (false, "ModuleItemList parsing should fail".to_owned()), Err(e) => (true, format!("Uncaught {e}")), } @@ -332,7 +332,7 @@ impl Test { .build() .expect("cannot fail with default global object"); - let module = match context.parse_module(source, None) { + let module = match Module::parse(source, None, context) { Ok(module) => module, Err(err) => return (false, format!("Uncaught {err}")), }; @@ -396,7 +396,7 @@ impl Test { return (false, e); } let error = if self.is_module() { - let module = match context.parse_module(source, None) { + let module = match Module::parse(source, None, context) { Ok(module) => module, Err(e) => return (false, format!("Uncaught {e}")), };