summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/extensions.rs10
-rw-r--r--core/lib.rs1
-rw-r--r--core/modules.rs238
-rw-r--r--core/runtime.rs131
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:")]