diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/modules.rs | 131 | ||||
-rw-r--r-- | core/runtime.rs | 63 |
2 files changed, 184 insertions, 10 deletions
diff --git a/core/modules.rs b/core/modules.rs index f1f083fe9..a21e97464 100644 --- a/core/modules.rs +++ b/core/modules.rs @@ -186,6 +186,8 @@ impl ModuleLoader for FsModuleLoader { enum LoadInit { /// Main module specifier. Main(String), + /// Module specifier for side module. + Side(String), /// Dynamic import specifier with referrer. DynamicImport(String, String), } @@ -221,6 +223,10 @@ impl RecursiveModuleLoad { Self::new(LoadInit::Main(specifier.to_string()), module_map_rc) } + pub fn side(specifier: &str, module_map_rc: Rc<RefCell<ModuleMap>>) -> Self { + Self::new(LoadInit::Side(specifier.to_string()), module_map_rc) + } + pub fn dynamic_import( specifier: &str, referrer: &str, @@ -267,6 +273,11 @@ impl RecursiveModuleLoad { .loader .resolve(self.op_state.clone(), specifier, ".", true) } + LoadInit::Side(ref specifier) => { + self + .loader + .resolve(self.op_state.clone(), specifier, ".", false) + } LoadInit::DynamicImport(ref specifier, ref referrer) => self .loader .resolve(self.op_state.clone(), specifier, referrer, false), @@ -283,6 +294,13 @@ impl RecursiveModuleLoad { .resolve(op_state.clone(), specifier, ".", true)?; (spec, None) } + LoadInit::Side(ref specifier) => { + let spec = + self + .loader + .resolve(op_state.clone(), specifier, ".", false)?; + (spec, None) + } LoadInit::DynamicImport(ref specifier, ref referrer) => { let spec = self @@ -305,7 +323,9 @@ impl RecursiveModuleLoad { } pub fn is_currently_loading_main_module(&self) -> bool { - !self.is_dynamic_import() && self.state == LoadState::LoadingRoot + !self.is_dynamic_import() + && matches!(self.init, LoadInit::Main(..)) + && self.state == LoadState::LoadingRoot } pub fn register_and_recurse( @@ -575,6 +595,17 @@ impl ModuleMap { import_specifiers.push(module_specifier); } + if main { + let maybe_main_module = self.info.values().find(|module| module.main); + if let Some(main_module) = maybe_main_module { + return Err(generic_error( + format!("Trying to create \"main\" module ({:?}), when one already exists ({:?})", + name, + main_module.name, + ))); + } + } + let handle = v8::Global::<v8::Module>::new(tc_scope, module); let id = self.next_module_id; self.next_module_id += 1; @@ -644,6 +675,15 @@ impl ModuleMap { Ok(load) } + pub async fn load_side( + module_map_rc: Rc<RefCell<ModuleMap>>, + specifier: &str, + ) -> Result<RecursiveModuleLoad, AnyError> { + let load = RecursiveModuleLoad::side(specifier, module_map_rc.clone()); + load.prepare().await?; + Ok(load) + } + // Initiate loading of a module graph imported using `import()`. pub fn load_dynamic_import( module_map_rc: Rc<RefCell<ModuleMap>>, @@ -903,7 +943,7 @@ mod tests { ..Default::default() }); let spec = crate::resolve_url("file:///a.js").unwrap(); - let a_id_fut = runtime.load_module(&spec, None); + let a_id_fut = runtime.load_main_module(&spec, None); let a_id = futures::executor::block_on(a_id_fut).expect("Failed to load"); let _ = runtime.mod_evaluate(a_id); @@ -1363,7 +1403,7 @@ mod tests { let fut = async move { let spec = crate::resolve_url("file:///circular1.js").unwrap(); - let result = runtime.load_module(&spec, None).await; + let result = runtime.load_main_module(&spec, None).await; assert!(result.is_ok()); let circular1_id = result.unwrap(); let _ = runtime.mod_evaluate(circular1_id); @@ -1435,7 +1475,7 @@ mod tests { let fut = async move { let spec = crate::resolve_url("file:///redirect1.js").unwrap(); - let result = runtime.load_module(&spec, None).await; + let result = runtime.load_main_module(&spec, None).await; println!(">> result {:?}", result); assert!(result.is_ok()); let redirect1_id = result.unwrap(); @@ -1500,7 +1540,8 @@ mod tests { ..Default::default() }); let spec = crate::resolve_url("file:///main.js").unwrap(); - let mut recursive_load = runtime.load_module(&spec, None).boxed_local(); + let mut recursive_load = + runtime.load_main_module(&spec, None).boxed_local(); let result = recursive_load.poll_unpin(&mut cx); assert!(result.is_pending()); @@ -1548,7 +1589,7 @@ mod tests { ..Default::default() }); let spec = crate::resolve_url("file:///bad_import.js").unwrap(); - let mut load_fut = runtime.load_module(&spec, None).boxed_local(); + let mut load_fut = runtime.load_main_module(&spec, None).boxed_local(); let result = load_fut.poll_unpin(&mut cx); if let Poll::Ready(Err(err)) = result { assert_eq!( @@ -1583,7 +1624,7 @@ mod tests { // The behavior should be very similar to /a.js. let spec = crate::resolve_url("file:///main_with_code.js").unwrap(); let main_id_fut = runtime - .load_module(&spec, Some(MAIN_WITH_CODE_SRC.to_owned())) + .load_main_module(&spec, Some(MAIN_WITH_CODE_SRC.to_owned())) .boxed_local(); let main_id = futures::executor::block_on(main_id_fut).expect("Failed to load"); @@ -1622,4 +1663,80 @@ mod tests { ); assert_eq!(modules.get_children(d_id), Some(&vec![])); } + + #[test] + fn main_and_side_module() { + struct ModsLoader {} + + let main_specifier = crate::resolve_url("file:///main_module.js").unwrap(); + let side_specifier = crate::resolve_url("file:///side_module.js").unwrap(); + + impl ModuleLoader for ModsLoader { + fn resolve( + &self, + _op_state: Rc<RefCell<OpState>>, + specifier: &str, + referrer: &str, + _is_main: bool, + ) -> Result<ModuleSpecifier, AnyError> { + let s = crate::resolve_import(specifier, referrer).unwrap(); + Ok(s) + } + + fn load( + &self, + _op_state: Rc<RefCell<OpState>>, + module_specifier: &ModuleSpecifier, + _maybe_referrer: Option<ModuleSpecifier>, + _is_dyn_import: bool, + ) -> Pin<Box<ModuleSourceFuture>> { + let module_source = match module_specifier.as_str() { + "file:///main_module.js" => Ok(ModuleSource { + module_url_specified: "file:///main_module.js".to_string(), + module_url_found: "file:///main_module.js".to_string(), + code: "if (!import.meta.main) throw Error();".to_owned(), + }), + "file:///side_module.js" => Ok(ModuleSource { + module_url_specified: "file:///side_module.js".to_string(), + module_url_found: "file:///side_module.js".to_string(), + code: "if (import.meta.main) throw Error();".to_owned(), + }), + _ => unreachable!(), + }; + async move { module_source }.boxed() + } + } + + let loader = Rc::new(ModsLoader {}); + let mut runtime = JsRuntime::new(RuntimeOptions { + module_loader: Some(loader), + ..Default::default() + }); + + let main_id_fut = runtime + .load_main_module(&main_specifier, None) + .boxed_local(); + let main_id = + futures::executor::block_on(main_id_fut).expect("Failed to load"); + + let _ = runtime.mod_evaluate(main_id); + futures::executor::block_on(runtime.run_event_loop(false)).unwrap(); + + // Try to add another main module - it should error. + let side_id_fut = runtime + .load_main_module(&side_specifier, None) + .boxed_local(); + futures::executor::block_on(side_id_fut) + .expect_err("Should have failed to load second main module"); + + // And now try to load it as a side module + let side_id_fut = runtime + .load_side_module(&side_specifier, None) + .boxed_local(); + let side_id = + futures::executor::block_on(side_id_fut).expect("Failed to load"); + + let _ = runtime.mod_evaluate(side_id); + futures::executor::block_on(runtime.run_event_loop(false)).unwrap(); + } } diff --git a/core/runtime.rs b/core/runtime.rs index d5089569d..f2e804abb 100644 --- a/core/runtime.rs +++ b/core/runtime.rs @@ -1350,11 +1350,14 @@ impl JsRuntime { state_rc.borrow_mut().pending_dyn_mod_evaluate = still_pending; } - /// Asynchronously load specified module and all of its dependencies + /// Asynchronously load specified module and all of its dependencies. + /// + /// The module will be marked as "main", and because of that + /// "import.meta.main" will return true when checked inside that module. /// /// User must call `JsRuntime::mod_evaluate` with returned `ModuleId` /// manually after load is finished. - pub async fn load_module( + pub async fn load_main_module( &mut self, specifier: &ModuleSpecifier, code: Option<String>, @@ -1363,6 +1366,7 @@ impl JsRuntime { if let Some(code) = code { module_map_rc.borrow_mut().new_module( &mut self.handle_scope(), + // main module true, specifier.as_str(), &code, @@ -1383,6 +1387,59 @@ impl JsRuntime { Ok(root_id) } + /// Asynchronously load specified ES module and all of its dependencies. + /// + /// This method is meant to be used when loading some utility code that + /// might be later imported by the main module (ie. an entry point module). + /// + /// User must call `JsRuntime::mod_evaluate` with returned `ModuleId` + /// manually after load is finished. + pub async fn load_side_module( + &mut self, + specifier: &ModuleSpecifier, + code: Option<String>, + ) -> Result<ModuleId, AnyError> { + let module_map_rc = Self::module_map(self.v8_isolate()); + if let Some(code) = code { + module_map_rc.borrow_mut().new_module( + &mut self.handle_scope(), + // not main module + false, + specifier.as_str(), + &code, + )?; + } + + let mut load = + ModuleMap::load_side(module_map_rc.clone(), specifier.as_str()).await?; + + while let Some(info_result) = load.next().await { + let info = info_result?; + let scope = &mut self.handle_scope(); + load.register_and_recurse(scope, &info)?; + } + + let root_id = load.root_module_id.expect("Root module should be loaded"); + self.instantiate_module(root_id)?; + Ok(root_id) + } + + /// Asynchronously load specified module and all of its dependencies + /// + /// User must call `JsRuntime::mod_evaluate` with returned `ModuleId` + /// manually after load is finished. + #[deprecated( + since = "0.100.0", + note = "This method had a bug, marking multiple modules loaded as \"main\". Use `load_main_module` or `load_side_module` instead." + )] + pub async fn load_module( + &mut self, + specifier: &ModuleSpecifier, + code: Option<String>, + ) -> Result<ModuleId, AnyError> { + self.load_main_module(specifier, code).await + } + fn poll_pending_ops( &mut self, cx: &mut Context, @@ -2040,7 +2097,7 @@ pub mod tests { let source_code = "Deno.core.print('hello\\n')".to_string(); let module_id = futures::executor::block_on( - runtime.load_module(&specifier, Some(source_code)), + runtime.load_main_module(&specifier, Some(source_code)), ) .unwrap(); |