diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/extensions.rs | 10 | ||||
-rw-r--r-- | core/lib.rs | 1 | ||||
-rw-r--r-- | core/modules.rs | 238 | ||||
-rw-r--r-- | core/runtime.rs | 131 |
4 files changed, 124 insertions, 256 deletions
diff --git a/core/extensions.rs b/core/extensions.rs index ba151da3d..a8b52eb3b 100644 --- a/core/extensions.rs +++ b/core/extensions.rs @@ -471,16 +471,6 @@ impl Extension { pub fn disable(self) -> Self { self.enabled(false) } - - pub(crate) fn find_esm( - &self, - specifier: &str, - ) -> Option<&ExtensionFileSource> { - self - .get_esm_sources()? - .iter() - .find(|s| s.specifier == specifier) - } } // Provides a convenient builder pattern to declare Extensions diff --git a/core/lib.rs b/core/lib.rs index 58140bb22..8edc8be18 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -79,7 +79,6 @@ pub use crate::module_specifier::resolve_url; pub use crate::module_specifier::resolve_url_or_path; pub use crate::module_specifier::ModuleResolutionError; pub use crate::module_specifier::ModuleSpecifier; -pub use crate::modules::ExtModuleLoader; pub use crate::modules::ExtModuleLoaderCb; pub use crate::modules::FsModuleLoader; pub use crate::modules::ModuleCode; diff --git a/core/modules.rs b/core/modules.rs index 2acc14684..5a9226b6c 100644 --- a/core/modules.rs +++ b/core/modules.rs @@ -12,8 +12,8 @@ use crate::snapshot_util::SnapshottedData; use crate::Extension; use crate::JsRuntime; use crate::OpState; +use anyhow::anyhow; use anyhow::Error; -use core::panic; use futures::future::FutureExt; use futures::stream::FuturesUnordered; use futures::stream::Stream; @@ -385,154 +385,90 @@ impl ModuleLoader for NoopModuleLoader { pub type ExtModuleLoaderCb = Box<dyn Fn(&ExtensionFileSource) -> Result<ModuleCode, Error>>; -pub struct ExtModuleLoader { - module_loader: Rc<dyn ModuleLoader>, - extensions: Rc<RefCell<Vec<Extension>>>, - ext_resolution_allowed: RefCell<bool>, - used_esm_sources: RefCell<HashMap<String, bool>>, - maybe_load_callback: Option<ExtModuleLoaderCb>, -} - -impl Default for ExtModuleLoader { - fn default() -> Self { - Self { - module_loader: Rc::new(NoopModuleLoader), - extensions: Default::default(), - ext_resolution_allowed: Default::default(), - used_esm_sources: RefCell::new(HashMap::default()), - maybe_load_callback: None, - } - } +pub(crate) struct ExtModuleLoader { + maybe_load_callback: Option<Rc<ExtModuleLoaderCb>>, + sources: RefCell<HashMap<String, ExtensionFileSource>>, + used_specifiers: RefCell<HashSet<String>>, } impl ExtModuleLoader { pub fn new( - module_loader: Option<Rc<dyn ModuleLoader>>, - extensions: Rc<RefCell<Vec<Extension>>>, - maybe_load_callback: Option<ExtModuleLoaderCb>, + extensions: &[Extension], + maybe_load_callback: Option<Rc<ExtModuleLoaderCb>>, ) -> Self { - let used_esm_sources: HashMap<String, bool> = extensions - .borrow() - .iter() - .flat_map(|e| e.get_esm_sources()) - .flatten() - .map(|file_source| (file_source.specifier.to_string(), false)) - .collect(); - + let mut sources = HashMap::new(); + sources.extend( + extensions + .iter() + .flat_map(|e| e.get_esm_sources()) + .flatten() + .map(|s| (s.specifier.to_string(), s.clone())), + ); ExtModuleLoader { - module_loader: module_loader.unwrap_or_else(|| Rc::new(NoopModuleLoader)), - extensions, - ext_resolution_allowed: Default::default(), - used_esm_sources: RefCell::new(used_esm_sources), maybe_load_callback, + sources: RefCell::new(sources), + used_specifiers: Default::default(), } } +} - pub fn resolve( +impl ModuleLoader for ExtModuleLoader { + fn resolve( &self, specifier: &str, referrer: &str, - kind: ResolutionKind, + _kind: ResolutionKind, ) -> Result<ModuleSpecifier, Error> { - if specifier.starts_with("ext:") { - if !referrer.starts_with("ext:") && referrer != "." - || !*self.ext_resolution_allowed.borrow() - { - return Err(generic_error( - "Cannot load extension module from external code", - )); - } - return Ok(ModuleSpecifier::parse(specifier)?); - } - self.module_loader.resolve(specifier, referrer, kind) + Ok(resolve_import(specifier, referrer)?) } - pub fn load( + fn load( &self, - module_specifier: &ModuleSpecifier, - maybe_referrer: Option<&ModuleSpecifier>, - is_dyn_import: bool, + specifier: &ModuleSpecifier, + _maybe_referrer: Option<&ModuleSpecifier>, + _is_dyn_import: bool, ) -> Pin<Box<ModuleSourceFuture>> { - if module_specifier.scheme() != "ext" { - return self.module_loader.load( - module_specifier, - maybe_referrer, - is_dyn_import, - ); - } - - let specifier = module_specifier.to_string(); - let extensions = self.extensions.borrow(); - let maybe_file_source = extensions - .iter() - .find_map(|e| e.find_esm(module_specifier.as_str())); - - if let Some(file_source) = maybe_file_source { - { - let mut used_esm_sources = self.used_esm_sources.borrow_mut(); - let used = used_esm_sources.get_mut(file_source.specifier).unwrap(); - *used = true; - } - - let result = if let Some(load_callback) = &self.maybe_load_callback { - load_callback(file_source) - } else { - file_source.load() - }; - - match result { - Ok(code) => { - let res = - ModuleSource::new(ModuleType::JavaScript, code, module_specifier); - return futures::future::ok(res).boxed_local(); - } - Err(err) => return futures::future::err(err).boxed_local(), + let sources = self.sources.borrow(); + let source = match sources.get(specifier.as_str()) { + Some(source) => source, + None => return futures::future::err(anyhow!("Specifier \"{}\" was not passed as an extension module and was not included in the snapshot.", specifier)).boxed_local(), + }; + self + .used_specifiers + .borrow_mut() + .insert(specifier.to_string()); + let result = if let Some(load_callback) = &self.maybe_load_callback { + load_callback(source) + } else { + source.load() + }; + match result { + Ok(code) => { + let res = ModuleSource::new(ModuleType::JavaScript, code, specifier); + return futures::future::ok(res).boxed_local(); } + Err(err) => return futures::future::err(err).boxed_local(), } - - async move { - Err(generic_error(format!( - "Cannot find extension module source for specifier {specifier}" - ))) - } - .boxed_local() } - pub fn prepare_load( + fn prepare_load( &self, - op_state: Rc<RefCell<OpState>>, - module_specifier: &ModuleSpecifier, - maybe_referrer: Option<String>, - is_dyn_import: bool, + _op_state: Rc<RefCell<OpState>>, + _specifier: &ModuleSpecifier, + _maybe_referrer: Option<String>, + _is_dyn_import: bool, ) -> Pin<Box<dyn Future<Output = Result<(), Error>>>> { - if module_specifier.scheme() == "ext" { - return async { Ok(()) }.boxed_local(); - } - - self.module_loader.prepare_load( - op_state, - module_specifier, - maybe_referrer, - is_dyn_import, - ) - } - - pub fn allow_ext_resolution(&self) { - *self.ext_resolution_allowed.borrow_mut() = true; - } - - pub fn disallow_ext_resolution(&self) { - *self.ext_resolution_allowed.borrow_mut() = false; + async { Ok(()) }.boxed_local() } } impl Drop for ExtModuleLoader { fn drop(&mut self) { - let used_esm_sources = self.used_esm_sources.get_mut(); - let unused_modules: Vec<_> = used_esm_sources + let sources = self.sources.get_mut(); + let used_specifiers = self.used_specifiers.get_mut(); + let unused_modules: Vec<_> = sources .iter() - .filter(|(_s, v)| !*v) - .map(|(s, _)| s) + .filter(|(k, _)| !used_specifiers.contains(k.as_str())) .collect(); if !unused_modules.is_empty() { @@ -541,7 +477,7 @@ impl Drop for ExtModuleLoader { .to_string(); for m in unused_modules { msg.push_str(" - "); - msg.push_str(m); + msg.push_str(m.0); msg.push('\n'); } panic!("{}", msg); @@ -634,7 +570,7 @@ pub(crate) struct RecursiveModuleLoad { // These three fields are copied from `module_map_rc`, but they are cloned // ahead of time to avoid already-borrowed errors. op_state: Rc<RefCell<OpState>>, - loader: Rc<ExtModuleLoader>, + loader: Rc<dyn ModuleLoader>, } impl RecursiveModuleLoad { @@ -1060,7 +996,7 @@ pub(crate) struct ModuleMap { pub(crate) next_load_id: ModuleLoadId, // Handling of futures for loading module sources - pub loader: Rc<ExtModuleLoader>, + pub loader: Rc<dyn ModuleLoader>, op_state: Rc<RefCell<OpState>>, pub(crate) dynamic_import_map: HashMap<ModuleLoadId, v8::Global<v8::PromiseResolver>>, @@ -1369,7 +1305,7 @@ impl ModuleMap { } pub(crate) fn new( - loader: Rc<ExtModuleLoader>, + loader: Rc<dyn ModuleLoader>, op_state: Rc<RefCell<OpState>>, ) -> ModuleMap { Self { @@ -1556,6 +1492,29 @@ impl ModuleMap { Ok(id) } + pub(crate) fn clear(&mut self) { + *self = Self::new(self.loader.clone(), self.op_state.clone()) + } + + pub(crate) fn get_handle_by_name( + &self, + name: impl AsRef<str>, + ) -> Option<v8::Global<v8::Module>> { + let id = self + .get_id(name.as_ref(), AssertedModuleType::JavaScriptOrWasm) + .or_else(|| self.get_id(name.as_ref(), AssertedModuleType::Json))?; + self.get_handle(id) + } + + pub(crate) fn inject_handle( + &mut self, + name: ModuleName, + module_type: ModuleType, + handle: v8::Global<v8::Module>, + ) { + self.create_module_info(name, module_type, handle, false, vec![]); + } + fn create_module_info( &mut self, name: FastString, @@ -3005,37 +2964,4 @@ if (import.meta.url != 'file:///main_with_code.js') throw Error(); ) .unwrap(); } - - #[test] - fn ext_resolution() { - let loader = ExtModuleLoader::default(); - loader.allow_ext_resolution(); - loader - .resolve("ext:core.js", "ext:referrer.js", ResolutionKind::Import) - .unwrap(); - loader - .resolve("ext:core.js", ".", ResolutionKind::Import) - .unwrap(); - } - - #[test] - fn ext_resolution_failure() { - let loader = ExtModuleLoader::default(); - loader.allow_ext_resolution(); - assert_eq!( - loader - .resolve("ext:core.js", "file://bar", ResolutionKind::Import,) - .err() - .map(|e| e.to_string()), - Some("Cannot load extension module from external code".to_string()) - ); - loader.disallow_ext_resolution(); - assert_eq!( - loader - .resolve("ext:core.js", "ext:referrer.js", ResolutionKind::Import,) - .err() - .map(|e| e.to_string()), - Some("Cannot load extension module from external code".to_string()) - ); - } } diff --git a/core/runtime.rs b/core/runtime.rs index dcadd6639..be3ae4355 100644 --- a/core/runtime.rs +++ b/core/runtime.rs @@ -9,6 +9,7 @@ use crate::extensions::OpEventLoopFn; use crate::inspector::JsRuntimeInspector; use crate::module_specifier::ModuleSpecifier; use crate::modules::AssertedModuleType; +use crate::modules::ExtModuleLoader; use crate::modules::ExtModuleLoaderCb; use crate::modules::ModuleCode; use crate::modules::ModuleError; @@ -16,6 +17,7 @@ use crate::modules::ModuleId; use crate::modules::ModuleLoadId; use crate::modules::ModuleLoader; use crate::modules::ModuleMap; +use crate::modules::ModuleName; use crate::ops::*; use crate::realm::ContextState; use crate::realm::JsRealm; @@ -24,6 +26,7 @@ use crate::snapshot_util; use crate::source_map::SourceMapCache; use crate::source_map::SourceMapGetter; use crate::Extension; +use crate::ModuleType; use crate::NoopModuleLoader; use crate::OpMiddlewareFn; use crate::OpResult; @@ -88,6 +91,7 @@ pub struct JsRuntime { // a safety issue with SnapshotCreator. See JsRuntime::drop. v8_isolate: Option<v8::OwnedIsolate>, snapshot_options: snapshot_util::SnapshotOptions, + snapshot_module_load_cb: Option<Rc<ExtModuleLoaderCb>>, allocations: IsolateAllocations, extensions: Rc<RefCell<Vec<Extension>>>, event_loop_middlewares: Vec<Box<OpEventLoopFn>>, @@ -506,13 +510,6 @@ impl JsRuntime { } } } - let num_extensions = options.extensions.len(); - let extensions = Rc::new(RefCell::new(options.extensions)); - let ext_loader = Rc::new(crate::modules::ExtModuleLoader::new( - Some(loader.clone()), - extensions.clone(), - options.snapshot_module_load_cb, - )); { let global_realm = JsRealmInner::new( @@ -530,8 +527,7 @@ impl JsRuntime { Self::STATE_DATA_OFFSET, Rc::into_raw(state_rc.clone()) as *mut c_void, ); - let module_map_rc = - Rc::new(RefCell::new(ModuleMap::new(ext_loader, op_state))); + let module_map_rc = Rc::new(RefCell::new(ModuleMap::new(loader, op_state))); if let Some(snapshotted_data) = maybe_snapshotted_data { let scope = &mut v8::HandleScope::with_context(&mut isolate, global_context); @@ -546,11 +542,12 @@ impl JsRuntime { let mut js_runtime = Self { v8_isolate: Some(isolate), snapshot_options, + snapshot_module_load_cb: options.snapshot_module_load_cb.map(Rc::new), allocations: IsolateAllocations::default(), - event_loop_middlewares: Vec::with_capacity(num_extensions), - extensions, + event_loop_middlewares: Vec::with_capacity(options.extensions.len()), + extensions: Rc::new(RefCell::new(options.extensions)), state: state_rc, - module_map: Some(module_map_rc.clone()), + module_map: Some(module_map_rc), is_main: options.is_main, }; @@ -558,9 +555,7 @@ impl JsRuntime { // available during the initialization process. js_runtime.init_extension_ops().unwrap(); let realm = js_runtime.global_realm(); - module_map_rc.borrow().loader.allow_ext_resolution(); js_runtime.init_extension_js(&realm).unwrap(); - module_map_rc.borrow().loader.disallow_ext_resolution(); js_runtime } @@ -666,21 +661,7 @@ impl JsRuntime { JsRealm::new(realm) }; - self - .module_map - .as_ref() - .unwrap() - .borrow() - .loader - .allow_ext_resolution(); self.init_extension_js(&realm)?; - self - .module_map - .as_ref() - .unwrap() - .borrow() - .loader - .disallow_ext_resolution(); Ok(realm) } @@ -735,6 +716,15 @@ impl JsRuntime { // 2. Iterate through all extensions: // a. If an extension has a `esm_entry_point`, execute it. + // TODO(nayeemrmn): Module maps should be per-realm. + let module_map = self.module_map.as_ref().unwrap(); + let loader = module_map.borrow().loader.clone(); + let ext_loader = Rc::new(ExtModuleLoader::new( + &self.extensions.borrow(), + self.snapshot_module_load_cb.clone(), + )); + module_map.borrow_mut().loader = ext_loader; + let mut esm_entrypoints = vec![]; // Take extensions to avoid double-borrow @@ -816,6 +806,7 @@ impl JsRuntime { // Restore extensions self.extensions = extensions; + self.module_map.as_ref().unwrap().borrow_mut().loader = loader; Ok(()) } @@ -1865,6 +1856,29 @@ impl JsRuntime { receiver } + /// Clear the module map, meant to be used after initializing extensions. + /// Optionally pass a list of exceptions `(old_name, new_name)` representing + /// specifiers which will be renamed and preserved in the module map. + pub fn clear_module_map( + &self, + exceptions: impl Iterator<Item = (&'static str, &'static str)>, + ) { + let mut module_map = self.module_map.as_ref().unwrap().borrow_mut(); + let handles = exceptions + .map(|(old_name, new_name)| { + (module_map.get_handle_by_name(old_name).unwrap(), new_name) + }) + .collect::<Vec<_>>(); + module_map.clear(); + for (handle, new_name) in handles { + module_map.inject_handle( + ModuleName::from_static(new_name), + ModuleType::JavaScript, + handle, + ) + } + } + fn dynamic_import_reject( &mut self, id: ModuleLoadId, @@ -4774,67 +4788,6 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { .is_ok()); } - #[tokio::test] - async fn cant_load_internal_module_when_snapshot_is_loaded_and_not_snapshotting( - ) { - #[derive(Default)] - struct ModsLoader; - - impl ModuleLoader for ModsLoader { - fn resolve( - &self, - specifier: &str, - referrer: &str, - _kind: ResolutionKind, - ) -> Result<ModuleSpecifier, Error> { - assert_eq!(specifier, "file:///main.js"); - assert_eq!(referrer, "."); - let s = crate::resolve_import(specifier, referrer).unwrap(); - Ok(s) - } - - fn load( - &self, - _module_specifier: &ModuleSpecifier, - _maybe_referrer: Option<&ModuleSpecifier>, - _is_dyn_import: bool, - ) -> Pin<Box<ModuleSourceFuture>> { - let code = r#" - // This module doesn't really exist, just verifying that we'll get - // an error when specifier starts with "ext:". - import { core } from "ext:core.js"; - "#; - - async move { Ok(ModuleSource::for_test(code, "file:///main.js")) } - .boxed_local() - } - } - - let snapshot = { - let runtime = JsRuntime::new(RuntimeOptions { - will_snapshot: true, - ..Default::default() - }); - let snap: &[u8] = &runtime.snapshot(); - Vec::from(snap).into_boxed_slice() - }; - - let mut runtime2 = JsRuntime::new(RuntimeOptions { - module_loader: Some(Rc::new(ModsLoader)), - startup_snapshot: Some(Snapshot::Boxed(snapshot)), - ..Default::default() - }); - - let err = runtime2 - .load_main_module(&crate::resolve_url("file:///main.js").unwrap(), None) - .await - .unwrap_err(); - assert_eq!( - err.to_string(), - "Cannot load extension module from external code" - ); - } - #[cfg(debug_assertions)] #[test] #[should_panic(expected = "Found ops with duplicate names:")] |