diff options
Diffstat (limited to 'core/modules/map.rs')
-rw-r--r-- | core/modules/map.rs | 208 |
1 files changed, 191 insertions, 17 deletions
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!() } } |