summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/error.rs6
-rw-r--r--core/lib.rs2
-rw-r--r--core/modules/map.rs208
-rw-r--r--core/runtime/bindings.rs55
-rw-r--r--core/runtime/jsruntime.rs153
-rw-r--r--core/runtime/mod.rs1
-rw-r--r--runtime/web_worker.rs12
-rw-r--r--runtime/worker.rs21
8 files changed, 244 insertions, 214 deletions
diff --git a/core/error.rs b/core/error.rs
index 07dc98a22..55fdcaa7c 100644
--- a/core/error.rs
+++ b/core/error.rs
@@ -695,6 +695,12 @@ pub(crate) fn exception_to_err_result<T>(
Err(js_error.into())
}
+pub fn throw_type_error(scope: &mut v8::HandleScope, message: impl AsRef<str>) {
+ let message = v8::String::new(scope, message.as_ref()).unwrap();
+ let exception = v8::Exception::type_error(scope, message);
+ scope.throw_exception(exception);
+}
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/core/lib.rs b/core/lib.rs
index 82cd1dd43..ddc4d6d91 100644
--- a/core/lib.rs
+++ b/core/lib.rs
@@ -127,6 +127,7 @@ pub fn v8_version() -> &'static str {
/// An internal module re-exporting functions used by the #[op] (`deno_ops`) macro
#[doc(hidden)]
pub mod _ops {
+ pub use super::error::throw_type_error;
pub use super::error_codes::get_error_code;
pub use super::ops::to_op_result;
pub use super::ops::OpCtx;
@@ -137,7 +138,6 @@ pub mod _ops {
pub use super::runtime::ops::map_async_op4;
pub use super::runtime::ops::queue_async_op;
pub use super::runtime::ops::queue_fast_async_op;
- pub use super::runtime::throw_type_error;
pub use super::runtime::V8_WRAPPER_OBJECT_INDEX;
pub use super::runtime::V8_WRAPPER_TYPE_INDEX;
}
diff --git a/core/modules/map.rs b/core/modules/map.rs
index 828d5888b..837099d2f 100644
--- a/core/modules/map.rs
+++ b/core/modules/map.rs
@@ -1,5 +1,7 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+use crate::error::exception_to_err_result;
use crate::error::generic_error;
+use crate::error::throw_type_error;
use crate::fast_string::FastString;
use crate::modules::get_asserted_module_type_from_assertions;
use crate::modules::parse_import_assertions;
@@ -554,6 +556,105 @@ impl ModuleMap {
Ok(id)
}
+ pub(crate) fn instantiate_module(
+ &mut self,
+ scope: &mut v8::HandleScope,
+ id: ModuleId,
+ ) -> Result<(), v8::Global<v8::Value>> {
+ let tc_scope = &mut v8::TryCatch::new(scope);
+
+ let module = self
+ .get_handle(id)
+ .map(|handle| v8::Local::new(tc_scope, handle))
+ .expect("ModuleInfo not found");
+
+ if module.get_status() == v8::ModuleStatus::Errored {
+ return Err(v8::Global::new(tc_scope, module.get_exception()));
+ }
+
+ tc_scope.set_slot(self as *const _);
+ let instantiate_result =
+ module.instantiate_module(tc_scope, Self::module_resolve_callback);
+ tc_scope.remove_slot::<*const Self>();
+ if instantiate_result.is_none() {
+ let exception = tc_scope.exception().unwrap();
+ return Err(v8::Global::new(tc_scope, exception));
+ }
+
+ Ok(())
+ }
+
+ /// Called by V8 during `JsRuntime::instantiate_module`. This is only used internally, so we use the Isolate's annex
+ /// to propagate a &Self.
+ fn module_resolve_callback<'s>(
+ context: v8::Local<'s, v8::Context>,
+ specifier: v8::Local<'s, v8::String>,
+ import_assertions: v8::Local<'s, v8::FixedArray>,
+ referrer: v8::Local<'s, v8::Module>,
+ ) -> Option<v8::Local<'s, v8::Module>> {
+ // SAFETY: `CallbackScope` can be safely constructed from `Local<Context>`
+ let scope = &mut unsafe { v8::CallbackScope::new(context) };
+
+ let module_map =
+ // SAFETY: We retrieve the pointer from the slot, having just set it a few stack frames up
+ unsafe { scope.get_slot::<*const Self>().unwrap().as_ref().unwrap() };
+
+ let referrer_global = v8::Global::new(scope, referrer);
+
+ let referrer_info = module_map
+ .get_info(&referrer_global)
+ .expect("ModuleInfo not found");
+ let referrer_name = referrer_info.name.as_str();
+
+ let specifier_str = specifier.to_rust_string_lossy(scope);
+
+ let assertions = parse_import_assertions(
+ scope,
+ import_assertions,
+ ImportAssertionsKind::StaticImport,
+ );
+ let maybe_module = module_map.resolve_callback(
+ scope,
+ &specifier_str,
+ referrer_name,
+ assertions,
+ );
+ if let Some(module) = maybe_module {
+ return Some(module);
+ }
+
+ let msg = format!(
+ r#"Cannot resolve module "{specifier_str}" from "{referrer_name}""#
+ );
+ throw_type_error(scope, msg);
+ None
+ }
+
+ /// Called by `module_resolve_callback` during module instantiation.
+ fn resolve_callback<'s>(
+ &self,
+ scope: &mut v8::HandleScope<'s>,
+ specifier: &str,
+ referrer: &str,
+ import_assertions: HashMap<String, String>,
+ ) -> Option<v8::Local<'s, v8::Module>> {
+ let resolved_specifier = self
+ .loader
+ .resolve(specifier, referrer, ResolutionKind::Import)
+ .expect("Module should have been already resolved");
+
+ let module_type =
+ get_asserted_module_type_from_assertions(&import_assertions);
+
+ if let Some(id) = self.get_id(resolved_specifier.as_str(), module_type) {
+ if let Some(handle) = self.get_handle(id) {
+ return Some(v8::Local::new(scope, handle));
+ }
+ }
+
+ None
+ }
+
pub(crate) fn clear(&mut self) {
*self = Self::new(self.loader.clone())
}
@@ -753,29 +854,102 @@ impl ModuleMap {
&& self.pending_dynamic_imports.is_empty())
}
- /// Called by `module_resolve_callback` during module instantiation.
- pub(crate) fn resolve_callback<'s>(
+ /// Returns the namespace object of a module.
+ ///
+ /// This is only available after module evaluation has completed.
+ /// This function panics if module has not been instantiated.
+ pub fn get_module_namespace(
&self,
- scope: &mut v8::HandleScope<'s>,
- specifier: &str,
- referrer: &str,
- import_assertions: HashMap<String, String>,
- ) -> Option<v8::Local<'s, v8::Module>> {
- let resolved_specifier = self
- .loader
- .resolve(specifier, referrer, ResolutionKind::Import)
- .expect("Module should have been already resolved");
+ scope: &mut v8::HandleScope,
+ module_id: ModuleId,
+ ) -> Result<v8::Global<v8::Object>, Error> {
+ let module_handle =
+ self.get_handle(module_id).expect("ModuleInfo not found");
- let module_type =
- get_asserted_module_type_from_assertions(&import_assertions);
+ let module = module_handle.open(scope);
- if let Some(id) = self.get_id(resolved_specifier.as_str(), module_type) {
- if let Some(handle) = self.get_handle(id) {
- return Some(v8::Local::new(scope, handle));
+ if module.get_status() == v8::ModuleStatus::Errored {
+ let exception = module.get_exception();
+ return exception_to_err_result(scope, exception, false);
+ }
+
+ assert!(matches!(
+ module.get_status(),
+ v8::ModuleStatus::Instantiated | v8::ModuleStatus::Evaluated
+ ));
+
+ let module_namespace: v8::Local<v8::Object> =
+ v8::Local::try_from(module.get_module_namespace())
+ .map_err(|err: v8::DataError| generic_error(err.to_string()))?;
+
+ Ok(v8::Global::new(scope, module_namespace))
+ }
+
+ /// 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(
+ &mut self,
+ exceptions: impl Iterator<Item = (&'static str, &'static str)>,
+ ) {
+ let handles = exceptions
+ .map(|(old_name, new_name)| {
+ (self.get_handle_by_name(old_name).unwrap(), new_name)
+ })
+ .collect::<Vec<_>>();
+ self.clear();
+ for (handle, new_name) in handles {
+ self.inject_handle(
+ ModuleName::from_static(new_name),
+ ModuleType::JavaScript,
+ handle,
+ )
+ }
+ }
+
+ fn get_stalled_top_level_await_message_for_module(
+ &self,
+ scope: &mut v8::HandleScope,
+ module_id: ModuleId,
+ ) -> Vec<v8::Global<v8::Message>> {
+ let module_handle = self.handles.get(module_id).unwrap();
+
+ let module = v8::Local::new(scope, module_handle);
+ let stalled = module.get_stalled_top_level_await_message(scope);
+ let mut messages = vec![];
+ for (_, message) in stalled {
+ messages.push(v8::Global::new(scope, message));
+ }
+ messages
+ }
+
+ pub(crate) fn find_stalled_top_level_await(
+ &self,
+ scope: &mut v8::HandleScope,
+ ) -> Vec<v8::Global<v8::Message>> {
+ // First check if that's root module
+ let root_module_id =
+ self.info.iter().filter(|m| m.main).map(|m| m.id).next();
+
+ if let Some(root_module_id) = root_module_id {
+ let messages = self
+ .get_stalled_top_level_await_message_for_module(scope, root_module_id);
+ if !messages.is_empty() {
+ return messages;
}
}
- None
+ // It wasn't a top module, so iterate over all modules and try to find
+ // any with stalled top level await
+ for module_id in 0..self.handles.len() {
+ let messages =
+ self.get_stalled_top_level_await_message_for_module(scope, module_id);
+ if !messages.is_empty() {
+ return messages;
+ }
+ }
+
+ unreachable!()
}
}
diff --git a/core/runtime/bindings.rs b/core/runtime/bindings.rs
index 4cc27592f..2eb804e73 100644
--- a/core/runtime/bindings.rs
+++ b/core/runtime/bindings.rs
@@ -7,6 +7,7 @@ use std::os::raw::c_void;
use v8::MapFnTo;
use crate::error::is_instance_of_error;
+use crate::error::throw_type_error;
use crate::error::JsStackFrame;
use crate::modules::get_asserted_module_type_from_assertions;
use crate::modules::parse_import_assertions;
@@ -542,57 +543,3 @@ fn call_console(
inspector_console_method.call(scope, receiver.into(), &call_args);
deno_console_method.call(scope, receiver.into(), &call_args);
}
-
-/// Called by V8 during `JsRuntime::instantiate_module`.
-///
-/// This function borrows `ModuleMap` from the isolate slot,
-/// so it is crucial to ensure there are no existing borrows
-/// of `ModuleMap` when `JsRuntime::instantiate_module` is called.
-pub fn module_resolve_callback<'s>(
- context: v8::Local<'s, v8::Context>,
- specifier: v8::Local<'s, v8::String>,
- import_assertions: v8::Local<'s, v8::FixedArray>,
- referrer: v8::Local<'s, v8::Module>,
-) -> Option<v8::Local<'s, v8::Module>> {
- // SAFETY: `CallbackScope` can be safely constructed from `Local<Context>`
- let scope = &mut unsafe { v8::CallbackScope::new(context) };
-
- let module_map_rc = JsRuntime::module_map_from(scope);
- let module_map = module_map_rc.borrow();
-
- let referrer_global = v8::Global::new(scope, referrer);
-
- let referrer_info = module_map
- .get_info(&referrer_global)
- .expect("ModuleInfo not found");
- let referrer_name = referrer_info.name.as_str();
-
- let specifier_str = specifier.to_rust_string_lossy(scope);
-
- let assertions = parse_import_assertions(
- scope,
- import_assertions,
- ImportAssertionsKind::StaticImport,
- );
- let maybe_module = module_map.resolve_callback(
- scope,
- &specifier_str,
- referrer_name,
- assertions,
- );
- if let Some(module) = maybe_module {
- return Some(module);
- }
-
- let msg = format!(
- r#"Cannot resolve module "{specifier_str}" from "{referrer_name}""#
- );
- throw_type_error(scope, msg);
- None
-}
-
-pub fn throw_type_error(scope: &mut v8::HandleScope, message: impl AsRef<str>) {
- let message = v8::String::new(scope, message.as_ref()).unwrap();
- let exception = v8::Exception::type_error(scope, message);
- scope.throw_exception(exception);
-}
diff --git a/core/runtime/jsruntime.rs b/core/runtime/jsruntime.rs
index 3b41a90f1..2e473e7c9 100644
--- a/core/runtime/jsruntime.rs
+++ b/core/runtime/jsruntime.rs
@@ -21,14 +21,12 @@ 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::runtime::ContextState;
use crate::runtime::JsRealm;
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;
@@ -391,6 +389,11 @@ pub struct RuntimeOptions {
/// JavaScript sources in the extensions.
pub extensions: Vec<Extension>,
+ /// If provided, the module map will be cleared and left only with the specifiers
+ /// in this list, with the new names provided. If not provided, the module map is
+ /// left intact.
+ pub rename_modules: Option<Vec<(&'static str, &'static str)>>,
+
/// V8 snapshot that should be loaded on startup.
pub startup_snapshot: Option<Snapshot>,
@@ -694,6 +697,15 @@ impl JsRuntime {
js_runtime
.init_extension_js(&realm, maybe_load_callback)
.unwrap();
+
+ // If the user has requested that we rename modules
+ if let Some(rename_modules) = options.rename_modules {
+ js_runtime
+ .module_map
+ .borrow_mut()
+ .clear_module_map(rename_modules.into_iter());
+ }
+
js_runtime
}
@@ -1143,31 +1155,11 @@ impl JsRuntime {
&mut self,
module_id: ModuleId,
) -> Result<v8::Global<v8::Object>, Error> {
- let module_handle = self
+ self
.module_map
+ .clone()
.borrow()
- .get_handle(module_id)
- .expect("ModuleInfo not found");
-
- let scope = &mut self.handle_scope();
-
- let module = module_handle.open(scope);
-
- if module.get_status() == v8::ModuleStatus::Errored {
- let exception = module.get_exception();
- return exception_to_err_result(scope, exception, false);
- }
-
- assert!(matches!(
- module.get_status(),
- v8::ModuleStatus::Instantiated | v8::ModuleStatus::Evaluated
- ));
-
- let module_namespace: v8::Local<v8::Object> =
- v8::Local::try_from(module.get_module_namespace())
- .map_err(|err: v8::DataError| generic_error(err.to_string()))?;
-
- Ok(v8::Global::new(scope, module_namespace))
+ .get_module_namespace(&mut self.handle_scope(), module_id)
}
/// Registers a callback on the isolate when the memory limits are approached.
@@ -1323,6 +1315,7 @@ impl JsRuntime {
let _ = self.inspector().borrow().poll_sessions(Some(cx)).unwrap();
}
+ let module_map = self.module_map.clone();
self.pump_v8_message_loop()?;
// Dynamic module loading - ie. modules loaded using "import()"
@@ -1426,7 +1419,7 @@ impl JsRuntime {
// pass, will be polled again
} else {
let scope = &mut self.handle_scope();
- let messages = find_stalled_top_level_await(scope);
+ let messages = module_map.borrow().find_stalled_top_level_await(scope);
// We are gonna print only a single message to provide a nice formatting
// with source line of offending promise shown. Once user fixed it, then
// they will get another error message for the next promise (but this
@@ -1448,7 +1441,7 @@ impl JsRuntime {
} else if self.inner.state.borrow().dyn_module_evaluate_idle_counter >= 1
{
let scope = &mut self.handle_scope();
- let messages = find_stalled_top_level_await(scope);
+ let messages = module_map.borrow().find_stalled_top_level_await(scope);
// We are gonna print only a single message to provide a nice formatting
// with source line of offending promise shown. Once user fixed it, then
// they will get another error message for the next promise (but this
@@ -1537,58 +1530,6 @@ impl JsRuntimeForSnapshot {
}
}
-fn get_stalled_top_level_await_message_for_module(
- scope: &mut v8::HandleScope,
- module_id: ModuleId,
-) -> Vec<v8::Global<v8::Message>> {
- let module_map = JsRuntime::module_map_from(scope);
- let module_map = module_map.borrow();
- let module_handle = module_map.handles.get(module_id).unwrap();
-
- let module = v8::Local::new(scope, module_handle);
- let stalled = module.get_stalled_top_level_await_message(scope);
- let mut messages = vec![];
- for (_, message) in stalled {
- messages.push(v8::Global::new(scope, message));
- }
- messages
-}
-
-fn find_stalled_top_level_await(
- scope: &mut v8::HandleScope,
-) -> Vec<v8::Global<v8::Message>> {
- let module_map = JsRuntime::module_map_from(scope);
- let module_map = module_map.borrow();
-
- // First check if that's root module
- let root_module_id = module_map
- .info
- .iter()
- .filter(|m| m.main)
- .map(|m| m.id)
- .next();
-
- if let Some(root_module_id) = root_module_id {
- let messages =
- get_stalled_top_level_await_message_for_module(scope, root_module_id);
- if !messages.is_empty() {
- return messages;
- }
- }
-
- // It wasn't a top module, so iterate over all modules and try to find
- // any with stalled top level await
- for module_id in 0..module_map.handles.len() {
- let messages =
- get_stalled_top_level_await_message_for_module(scope, module_id);
- if !messages.is_empty() {
- return messages;
- }
- }
-
- unreachable!()
-}
-
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub(crate) struct EventLoopPendingState {
has_pending_refed_ops: bool,
@@ -1666,32 +1607,11 @@ impl JsRuntime {
&mut self,
id: ModuleId,
) -> Result<(), v8::Global<v8::Value>> {
- let module_map_rc = self.module_map.clone();
- let scope = &mut self.handle_scope();
- let tc_scope = &mut v8::TryCatch::new(scope);
-
- let module = module_map_rc
- .borrow()
- .get_handle(id)
- .map(|handle| v8::Local::new(tc_scope, handle))
- .expect("ModuleInfo not found");
-
- if module.get_status() == v8::ModuleStatus::Errored {
- return Err(v8::Global::new(tc_scope, module.get_exception()));
- }
-
- // IMPORTANT: No borrows to `ModuleMap` can be held at this point because
- // `module_resolve_callback` will be calling into `ModuleMap` from within
- // the isolate.
- let instantiate_result =
- module.instantiate_module(tc_scope, bindings::module_resolve_callback);
-
- if instantiate_result.is_none() {
- let exception = tc_scope.exception().unwrap();
- return Err(v8::Global::new(tc_scope, exception));
- }
-
- Ok(())
+ self
+ .module_map
+ .clone()
+ .borrow_mut()
+ .instantiate_module(&mut self.handle_scope(), id)
}
fn dynamic_import_module_evaluate(
@@ -1903,29 +1823,6 @@ 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.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,
diff --git a/core/runtime/mod.rs b/core/runtime/mod.rs
index 2bd3ea9fe..aa546b8c7 100644
--- a/core/runtime/mod.rs
+++ b/core/runtime/mod.rs
@@ -32,4 +32,3 @@ pub use snapshot_util::FilterFn;
pub(crate) use snapshot_util::SnapshottedData;
pub use bindings::script_origin;
-pub use bindings::throw_type_error;
diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs
index 36f9718b5..2dde5a369 100644
--- a/runtime/web_worker.rs
+++ b/runtime/web_worker.rs
@@ -4,7 +4,6 @@ use crate::inspector_server::InspectorServer;
use crate::ops;
use crate::permissions::PermissionsContainer;
use crate::tokio_util::create_and_run_current_thread;
-use crate::worker::init_runtime_module_map;
use crate::worker::FormatJsErrorFn;
use crate::BootstrapOptions;
use deno_broadcast_channel::InMemoryBroadcastChannel;
@@ -485,6 +484,15 @@ impl WebWorker {
let startup_snapshot = options.startup_snapshot
.expect("deno_runtime startup snapshot is not available with 'create_runtime_snapshot' Cargo feature.");
+ // Clear extension modules from the module map, except preserve `ext:deno_node`
+ // modules as `node:` specifiers.
+ let rename_modules = Some(
+ deno_node::SUPPORTED_BUILTIN_NODE_MODULES
+ .iter()
+ .map(|p| (p.ext_specifier, p.specifier))
+ .collect(),
+ );
+
let mut js_runtime = JsRuntime::new(RuntimeOptions {
module_loader: Some(options.module_loader.clone()),
startup_snapshot: Some(startup_snapshot),
@@ -494,9 +502,9 @@ impl WebWorker {
compiled_wasm_module_store: options.compiled_wasm_module_store.clone(),
extensions,
inspector: options.maybe_inspector_server.is_some(),
+ rename_modules,
..Default::default()
});
- init_runtime_module_map(&mut js_runtime);
if let Some(server) = options.maybe_inspector_server.clone() {
server.register_inspector(
diff --git a/runtime/worker.rs b/runtime/worker.rs
index 10375818d..0293c332a 100644
--- a/runtime/worker.rs
+++ b/runtime/worker.rs
@@ -58,16 +58,6 @@ impl ExitCode {
}
}
-/// Clear extension modules from the module map, except preserve `ext:deno_node`
-/// modules as `node:` specifiers.
-pub fn init_runtime_module_map(js_runtime: &mut JsRuntime) {
- js_runtime.clear_module_map(
- deno_node::SUPPORTED_BUILTIN_NODE_MODULES
- .iter()
- .map(|p| (p.ext_specifier, p.specifier)),
- );
-}
-
/// This worker is created and used by almost all
/// subcommands in Deno executable.
///
@@ -323,6 +313,15 @@ impl MainWorker {
let startup_snapshot = options.startup_snapshot
.expect("deno_runtime startup snapshot is not available with 'create_runtime_snapshot' Cargo feature.");
+ // Clear extension modules from the module map, except preserve `ext:deno_node`
+ // modules as `node:` specifiers.
+ let rename_modules = Some(
+ deno_node::SUPPORTED_BUILTIN_NODE_MODULES
+ .iter()
+ .map(|p| (p.ext_specifier, p.specifier))
+ .collect(),
+ );
+
let mut js_runtime = JsRuntime::new(RuntimeOptions {
module_loader: Some(options.module_loader.clone()),
startup_snapshot: Some(startup_snapshot),
@@ -332,11 +331,11 @@ impl MainWorker {
shared_array_buffer_store: options.shared_array_buffer_store.clone(),
compiled_wasm_module_store: options.compiled_wasm_module_store.clone(),
extensions,
+ rename_modules,
inspector: options.maybe_inspector_server.is_some(),
is_main: true,
..Default::default()
});
- init_runtime_module_map(&mut js_runtime);
if let Some(server) = options.maybe_inspector_server.clone() {
server.register_inspector(