diff options
author | Matt Mastracci <matthew@mastracci.com> | 2023-07-01 18:00:14 -0600 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-07-02 00:00:14 +0000 |
commit | e746b6d80654ba4e4e26370fe6e4f784ce841d92 (patch) | |
tree | 153ffad92a96126b9ab8e906dcdabf7648755931 /core/modules/map.rs | |
parent | b9c0e7cd550ab14fa7da7e33ed87cbeeeb9785a0 (diff) |
refactor(core): Extract deno_core (#19658)
`deno_core` is moving out! You'll find it at
https://github.com/denoland/deno_core/ once this PR lands.
Diffstat (limited to 'core/modules/map.rs')
-rw-r--r-- | core/modules/map.rs | 1014 |
1 files changed, 0 insertions, 1014 deletions
diff --git a/core/modules/map.rs b/core/modules/map.rs deleted file mode 100644 index 786772e5b..000000000 --- a/core/modules/map.rs +++ /dev/null @@ -1,1014 +0,0 @@ -// 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; -use crate::modules::validate_import_assertions; -use crate::modules::ImportAssertionsKind; -use crate::modules::ModuleCode; -use crate::modules::ModuleError; -use crate::modules::ModuleId; -use crate::modules::ModuleInfo; -use crate::modules::ModuleLoadId; -use crate::modules::ModuleLoader; -use crate::modules::ModuleName; -use crate::modules::ModuleRequest; -use crate::modules::ModuleType; -use crate::modules::NoopModuleLoader; -use crate::modules::PrepareLoadFuture; -use crate::modules::RecursiveModuleLoad; -use crate::modules::ResolutionKind; -use crate::runtime::JsRuntime; -use crate::runtime::SnapshottedData; -use anyhow::Error; -use futures::future::FutureExt; -use futures::stream::FuturesUnordered; -use futures::stream::StreamFuture; -use std::cell::RefCell; -use std::collections::HashMap; -use std::pin::Pin; -use std::rc::Rc; - -use super::AssertedModuleType; - -pub const BOM_CHAR: &[u8] = &[0xef, 0xbb, 0xbf]; - -/// Strips the byte order mark from the provided text if it exists. -fn strip_bom(source_code: &[u8]) -> &[u8] { - if source_code.starts_with(BOM_CHAR) { - &source_code[BOM_CHAR.len()..] - } else { - source_code - } -} - -/// A symbolic module entity. -#[derive(Debug, PartialEq)] -pub(crate) enum SymbolicModule { - /// This module is an alias to another module. - /// This is useful such that multiple names could point to - /// the same underlying module (particularly due to redirects). - Alias(ModuleName), - /// This module associates with a V8 module by id. - Mod(ModuleId), -} - -/// A collection of JS modules. -pub(crate) struct ModuleMap { - // Handling of specifiers and v8 objects - pub handles: Vec<v8::Global<v8::Module>>, - pub info: Vec<ModuleInfo>, - pub(crate) by_name_js: HashMap<ModuleName, SymbolicModule>, - pub(crate) by_name_json: HashMap<ModuleName, SymbolicModule>, - pub(crate) next_load_id: ModuleLoadId, - - // Handling of futures for loading module sources - pub loader: Rc<dyn ModuleLoader>, - pub(crate) dynamic_import_map: - HashMap<ModuleLoadId, v8::Global<v8::PromiseResolver>>, - pub(crate) preparing_dynamic_imports: - FuturesUnordered<Pin<Box<PrepareLoadFuture>>>, - pub(crate) pending_dynamic_imports: - FuturesUnordered<StreamFuture<RecursiveModuleLoad>>, - - // This store is used temporarily, to forward parsed JSON - // value from `new_json_module` to `json_module_evaluation_steps` - json_value_store: HashMap<v8::Global<v8::Module>, v8::Global<v8::Value>>, -} - -impl ModuleMap { - pub fn collect_modules( - &self, - ) -> Vec<(AssertedModuleType, &ModuleName, &SymbolicModule)> { - let mut output = vec![]; - for module_type in [ - AssertedModuleType::JavaScriptOrWasm, - AssertedModuleType::Json, - ] { - output.extend( - self - .by_name(module_type) - .iter() - .map(|x| (module_type, x.0, x.1)), - ) - } - output - } - - #[cfg(debug_assertions)] - pub(crate) fn assert_all_modules_evaluated( - &self, - scope: &mut v8::HandleScope, - ) { - let mut not_evaluated = vec![]; - - for (i, handle) in self.handles.iter().enumerate() { - let module = v8::Local::new(scope, handle); - if !matches!(module.get_status(), v8::ModuleStatus::Evaluated) { - not_evaluated.push(self.info[i].name.as_str().to_string()); - } - } - - if !not_evaluated.is_empty() { - let mut msg = "Following modules were not evaluated; make sure they are imported from other code:\n".to_string(); - for m in not_evaluated { - msg.push_str(&format!(" - {}\n", m)); - } - panic!("{}", msg); - } - } - - pub fn serialize_for_snapshotting( - &self, - scope: &mut v8::HandleScope, - ) -> SnapshottedData { - let array = v8::Array::new(scope, 3); - - let next_load_id = v8::Integer::new(scope, self.next_load_id); - array.set_index(scope, 0, next_load_id.into()); - - let info_arr = v8::Array::new(scope, self.info.len() as i32); - for (i, info) in self.info.iter().enumerate() { - let module_info_arr = v8::Array::new(scope, 5); - - let id = v8::Integer::new(scope, info.id as i32); - module_info_arr.set_index(scope, 0, id.into()); - - let main = v8::Boolean::new(scope, info.main); - module_info_arr.set_index(scope, 1, main.into()); - - let name = info.name.v8(scope); - module_info_arr.set_index(scope, 2, name.into()); - - let array_len = 2 * info.requests.len() as i32; - let requests_arr = v8::Array::new(scope, array_len); - for (i, request) in info.requests.iter().enumerate() { - let specifier = v8::String::new_from_one_byte( - scope, - request.specifier.as_bytes(), - v8::NewStringType::Normal, - ) - .unwrap(); - requests_arr.set_index(scope, 2 * i as u32, specifier.into()); - - let asserted_module_type = - v8::Integer::new(scope, request.asserted_module_type as i32); - requests_arr.set_index( - scope, - (2 * i) as u32 + 1, - asserted_module_type.into(), - ); - } - module_info_arr.set_index(scope, 3, requests_arr.into()); - - let module_type = v8::Integer::new(scope, info.module_type as i32); - module_info_arr.set_index(scope, 4, module_type.into()); - - info_arr.set_index(scope, i as u32, module_info_arr.into()); - } - array.set_index(scope, 1, info_arr.into()); - - let by_name = self.collect_modules(); - let by_name_array = v8::Array::new(scope, by_name.len() as i32); - { - for (i, (module_type, name, module)) in by_name.into_iter().enumerate() { - let arr = v8::Array::new(scope, 3); - - let specifier = name.v8(scope); - arr.set_index(scope, 0, specifier.into()); - - let asserted_module_type = v8::Integer::new(scope, module_type as i32); - arr.set_index(scope, 1, asserted_module_type.into()); - - let symbolic_module: v8::Local<v8::Value> = match module { - SymbolicModule::Alias(alias) => { - let alias = v8::String::new_from_one_byte( - scope, - alias.as_bytes(), - v8::NewStringType::Normal, - ) - .unwrap(); - alias.into() - } - SymbolicModule::Mod(id) => { - let id = v8::Integer::new(scope, *id as i32); - id.into() - } - }; - arr.set_index(scope, 2, symbolic_module); - - by_name_array.set_index(scope, i as u32, arr.into()); - } - } - array.set_index(scope, 2, by_name_array.into()); - - let array_global = v8::Global::new(scope, array); - - let handles = self.handles.clone(); - SnapshottedData { - module_map_data: array_global, - module_handles: handles, - } - } - - pub fn update_with_snapshotted_data( - &mut self, - scope: &mut v8::HandleScope, - snapshotted_data: SnapshottedData, - ) { - let local_data: v8::Local<v8::Array> = - v8::Local::new(scope, snapshotted_data.module_map_data); - - { - let next_load_id = local_data.get_index(scope, 0).unwrap(); - assert!(next_load_id.is_int32()); - let integer = next_load_id.to_integer(scope).unwrap(); - let val = integer.int32_value(scope).unwrap(); - self.next_load_id = val; - } - - { - let info_val = local_data.get_index(scope, 1).unwrap(); - - let info_arr: v8::Local<v8::Array> = info_val.try_into().unwrap(); - let len = info_arr.length() as usize; - // Over allocate so executing a few scripts doesn't have to resize this vec. - let mut info = Vec::with_capacity(len + 16); - - for i in 0..len { - let module_info_arr: v8::Local<v8::Array> = info_arr - .get_index(scope, i as u32) - .unwrap() - .try_into() - .unwrap(); - let id = module_info_arr - .get_index(scope, 0) - .unwrap() - .to_integer(scope) - .unwrap() - .value() as ModuleId; - - let main = module_info_arr - .get_index(scope, 1) - .unwrap() - .to_boolean(scope) - .is_true(); - - let name = module_info_arr - .get_index(scope, 2) - .unwrap() - .to_rust_string_lossy(scope) - .into(); - - let requests_arr: v8::Local<v8::Array> = module_info_arr - .get_index(scope, 3) - .unwrap() - .try_into() - .unwrap(); - let len = (requests_arr.length() as usize) / 2; - let mut requests = Vec::with_capacity(len); - for i in 0..len { - let specifier = requests_arr - .get_index(scope, (2 * i) as u32) - .unwrap() - .to_rust_string_lossy(scope); - let asserted_module_type_no = requests_arr - .get_index(scope, (2 * i + 1) as u32) - .unwrap() - .to_integer(scope) - .unwrap() - .value(); - let asserted_module_type = match asserted_module_type_no { - 0 => AssertedModuleType::JavaScriptOrWasm, - 1 => AssertedModuleType::Json, - _ => unreachable!(), - }; - requests.push(ModuleRequest { - specifier, - asserted_module_type, - }); - } - - let module_type_no = module_info_arr - .get_index(scope, 4) - .unwrap() - .to_integer(scope) - .unwrap() - .value(); - let module_type = match module_type_no { - 0 => ModuleType::JavaScript, - 1 => ModuleType::Json, - _ => unreachable!(), - }; - - let module_info = ModuleInfo { - id, - main, - name, - requests, - module_type, - }; - info.push(module_info); - } - - self.info = info; - } - - self - .by_name_mut(AssertedModuleType::JavaScriptOrWasm) - .clear(); - self.by_name_mut(AssertedModuleType::Json).clear(); - - { - let by_name_arr: v8::Local<v8::Array> = - local_data.get_index(scope, 2).unwrap().try_into().unwrap(); - let len = by_name_arr.length() as usize; - - for i in 0..len { - let arr: v8::Local<v8::Array> = by_name_arr - .get_index(scope, i as u32) - .unwrap() - .try_into() - .unwrap(); - - let specifier = - arr.get_index(scope, 0).unwrap().to_rust_string_lossy(scope); - let asserted_module_type = match arr - .get_index(scope, 1) - .unwrap() - .to_integer(scope) - .unwrap() - .value() - { - 0 => AssertedModuleType::JavaScriptOrWasm, - 1 => AssertedModuleType::Json, - _ => unreachable!(), - }; - - let symbolic_module_val = arr.get_index(scope, 2).unwrap(); - let val = if symbolic_module_val.is_number() { - SymbolicModule::Mod( - symbolic_module_val - .to_integer(scope) - .unwrap() - .value() - .try_into() - .unwrap(), - ) - } else { - SymbolicModule::Alias( - symbolic_module_val.to_rust_string_lossy(scope).into(), - ) - }; - - self - .by_name_mut(asserted_module_type) - .insert(specifier.into(), val); - } - } - - self.handles = snapshotted_data.module_handles; - } - - pub(crate) fn new(loader: Rc<dyn ModuleLoader>) -> ModuleMap { - Self { - handles: vec![], - info: vec![], - by_name_js: HashMap::new(), - by_name_json: HashMap::new(), - next_load_id: 1, - loader, - dynamic_import_map: HashMap::new(), - preparing_dynamic_imports: FuturesUnordered::new(), - pending_dynamic_imports: FuturesUnordered::new(), - json_value_store: HashMap::new(), - } - } - - /// Get module id, following all aliases in case of module specifier - /// that had been redirected. - pub(crate) fn get_id( - &self, - name: impl AsRef<str>, - asserted_module_type: AssertedModuleType, - ) -> Option<ModuleId> { - let map = self.by_name(asserted_module_type); - let first_symbolic_module = map.get(name.as_ref())?; - let mut mod_name = match first_symbolic_module { - SymbolicModule::Mod(mod_id) => return Some(*mod_id), - SymbolicModule::Alias(target) => target, - }; - loop { - let symbolic_module = map.get(mod_name.as_ref())?; - match symbolic_module { - SymbolicModule::Alias(target) => { - debug_assert!(mod_name != target); - mod_name = target; - } - SymbolicModule::Mod(mod_id) => return Some(*mod_id), - } - } - } - - pub(crate) fn new_json_module( - &mut self, - scope: &mut v8::HandleScope, - name: ModuleName, - source: ModuleCode, - ) -> Result<ModuleId, ModuleError> { - let name_str = name.v8(scope); - let source_str = v8::String::new_from_utf8( - scope, - strip_bom(source.as_bytes()), - v8::NewStringType::Normal, - ) - .unwrap(); - - let tc_scope = &mut v8::TryCatch::new(scope); - - let parsed_json = match v8::json::parse(tc_scope, source_str) { - Some(parsed_json) => parsed_json, - None => { - assert!(tc_scope.has_caught()); - let exception = tc_scope.exception().unwrap(); - let exception = v8::Global::new(tc_scope, exception); - return Err(ModuleError::Exception(exception)); - } - }; - - let export_names = [v8::String::new(tc_scope, "default").unwrap()]; - let module = v8::Module::create_synthetic_module( - tc_scope, - name_str, - &export_names, - json_module_evaluation_steps, - ); - - let handle = v8::Global::<v8::Module>::new(tc_scope, module); - let value_handle = v8::Global::<v8::Value>::new(tc_scope, parsed_json); - self.json_value_store.insert(handle.clone(), value_handle); - - let id = - self.create_module_info(name, ModuleType::Json, handle, false, vec![]); - - Ok(id) - } - - /// Create and compile an ES module. - pub(crate) fn new_es_module( - &mut self, - scope: &mut v8::HandleScope, - main: bool, - name: ModuleName, - source: ModuleCode, - is_dynamic_import: bool, - ) -> Result<ModuleId, ModuleError> { - let name_str = name.v8(scope); - let source_str = source.v8(scope); - - let origin = module_origin(scope, name_str); - let source = v8::script_compiler::Source::new(source_str, Some(&origin)); - - let tc_scope = &mut v8::TryCatch::new(scope); - - let maybe_module = v8::script_compiler::compile_module(tc_scope, source); - - if tc_scope.has_caught() { - assert!(maybe_module.is_none()); - let exception = tc_scope.exception().unwrap(); - let exception = v8::Global::new(tc_scope, exception); - return Err(ModuleError::Exception(exception)); - } - - let module = maybe_module.unwrap(); - - let mut requests: Vec<ModuleRequest> = vec![]; - let module_requests = module.get_module_requests(); - for i in 0..module_requests.length() { - let module_request = v8::Local::<v8::ModuleRequest>::try_from( - module_requests.get(tc_scope, i).unwrap(), - ) - .unwrap(); - let import_specifier = module_request - .get_specifier() - .to_rust_string_lossy(tc_scope); - - let import_assertions = module_request.get_import_assertions(); - - let assertions = parse_import_assertions( - tc_scope, - import_assertions, - ImportAssertionsKind::StaticImport, - ); - - // FIXME(bartomieju): there are no stack frames if exception - // is thrown here - validate_import_assertions(tc_scope, &assertions); - if tc_scope.has_caught() { - let exception = tc_scope.exception().unwrap(); - let exception = v8::Global::new(tc_scope, exception); - return Err(ModuleError::Exception(exception)); - } - - let module_specifier = match self.loader.resolve( - &import_specifier, - name.as_ref(), - if is_dynamic_import { - ResolutionKind::DynamicImport - } else { - ResolutionKind::Import - }, - ) { - Ok(s) => s, - Err(e) => return Err(ModuleError::Other(e)), - }; - let asserted_module_type = - get_asserted_module_type_from_assertions(&assertions); - let request = ModuleRequest { - specifier: module_specifier.to_string(), - asserted_module_type, - }; - requests.push(request); - } - - if main { - let maybe_main_module = self.info.iter().find(|module| module.main); - if let Some(main_module) = maybe_main_module { - return Err(ModuleError::Other(generic_error( - format!("Trying to create \"main\" module ({:?}), when one already exists ({:?})", - name.as_ref(), - main_module.name, - )))); - } - } - - let handle = v8::Global::<v8::Module>::new(tc_scope, module); - let id = self.create_module_info( - name, - ModuleType::JavaScript, - handle, - main, - requests, - ); - - 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()) - } - - 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, - module_type: ModuleType, - handle: v8::Global<v8::Module>, - main: bool, - requests: Vec<ModuleRequest>, - ) -> ModuleId { - let id = self.handles.len(); - let (name1, name2) = name.into_cheap_copy(); - self - .by_name_mut(module_type.into()) - .insert(name1, SymbolicModule::Mod(id)); - self.handles.push(handle); - self.info.push(ModuleInfo { - id, - main, - name: name2, - requests, - module_type, - }); - - id - } - - pub(crate) fn get_requested_modules( - &self, - id: ModuleId, - ) -> Option<&Vec<ModuleRequest>> { - self.info.get(id).map(|i| &i.requests) - } - - fn is_registered( - &self, - specifier: impl AsRef<str>, - asserted_module_type: AssertedModuleType, - ) -> bool { - if let Some(id) = self.get_id(specifier.as_ref(), asserted_module_type) { - let info = self.get_info_by_id(id).unwrap(); - return asserted_module_type == info.module_type.into(); - } - - false - } - - pub(crate) fn by_name( - &self, - asserted_module_type: AssertedModuleType, - ) -> &HashMap<ModuleName, SymbolicModule> { - match asserted_module_type { - AssertedModuleType::Json => &self.by_name_json, - AssertedModuleType::JavaScriptOrWasm => &self.by_name_js, - } - } - - pub(crate) fn by_name_mut( - &mut self, - asserted_module_type: AssertedModuleType, - ) -> &mut HashMap<ModuleName, SymbolicModule> { - match asserted_module_type { - AssertedModuleType::Json => &mut self.by_name_json, - AssertedModuleType::JavaScriptOrWasm => &mut self.by_name_js, - } - } - - pub(crate) fn alias( - &mut self, - name: FastString, - asserted_module_type: AssertedModuleType, - target: FastString, - ) { - debug_assert_ne!(name, target); - self - .by_name_mut(asserted_module_type) - .insert(name, SymbolicModule::Alias(target)); - } - - #[cfg(test)] - pub(crate) fn is_alias( - &self, - name: &str, - asserted_module_type: AssertedModuleType, - ) -> bool { - let cond = self.by_name(asserted_module_type).get(name); - matches!(cond, Some(SymbolicModule::Alias(_))) - } - - pub(crate) fn get_handle( - &self, - id: ModuleId, - ) -> Option<v8::Global<v8::Module>> { - self.handles.get(id).cloned() - } - - pub(crate) fn get_info( - &self, - global: &v8::Global<v8::Module>, - ) -> Option<&ModuleInfo> { - if let Some(id) = self.handles.iter().position(|module| module == global) { - return self.info.get(id); - } - - None - } - - pub(crate) fn get_info_by_id(&self, id: ModuleId) -> Option<&ModuleInfo> { - self.info.get(id) - } - - pub(crate) async fn load_main( - module_map_rc: Rc<RefCell<ModuleMap>>, - specifier: impl AsRef<str>, - ) -> Result<RecursiveModuleLoad, Error> { - let load = - RecursiveModuleLoad::main(specifier.as_ref(), module_map_rc.clone()); - load.prepare().await?; - Ok(load) - } - - pub(crate) async fn load_side( - module_map_rc: Rc<RefCell<ModuleMap>>, - specifier: impl AsRef<str>, - ) -> Result<RecursiveModuleLoad, Error> { - let load = - RecursiveModuleLoad::side(specifier.as_ref(), module_map_rc.clone()); - load.prepare().await?; - Ok(load) - } - - // Initiate loading of a module graph imported using `import()`. - pub(crate) fn load_dynamic_import( - module_map_rc: Rc<RefCell<ModuleMap>>, - specifier: &str, - referrer: &str, - asserted_module_type: AssertedModuleType, - resolver_handle: v8::Global<v8::PromiseResolver>, - ) { - let load = RecursiveModuleLoad::dynamic_import( - specifier, - referrer, - asserted_module_type, - module_map_rc.clone(), - ); - module_map_rc - .borrow_mut() - .dynamic_import_map - .insert(load.id, resolver_handle); - - let loader = module_map_rc.borrow().loader.clone(); - let resolve_result = - loader.resolve(specifier, referrer, ResolutionKind::DynamicImport); - let fut = match resolve_result { - Ok(module_specifier) => { - if module_map_rc - .borrow() - .is_registered(module_specifier, asserted_module_type) - { - async move { (load.id, Ok(load)) }.boxed_local() - } else { - async move { (load.id, load.prepare().await.map(|()| load)) } - .boxed_local() - } - } - Err(error) => async move { (load.id, Err(error)) }.boxed_local(), - }; - module_map_rc - .borrow_mut() - .preparing_dynamic_imports - .push(fut); - } - - pub(crate) fn has_pending_dynamic_imports(&self) -> bool { - !(self.preparing_dynamic_imports.is_empty() - && self.pending_dynamic_imports.is_empty()) - } - - /// 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, - module_id: ModuleId, - ) -> Result<v8::Global<v8::Object>, Error> { - let module_handle = - self.get_handle(module_id).expect("ModuleInfo not found"); - - 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)) - } - - /// 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; - } - } - - // 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!() - } -} - -impl Default for ModuleMap { - fn default() -> Self { - Self::new(Rc::new(NoopModuleLoader)) - } -} - -// Clippy thinks the return value doesn't need to be an Option, it's unaware -// of the mapping that MapFnFrom<F> does for ResolveModuleCallback. -#[allow(clippy::unnecessary_wraps)] -fn json_module_evaluation_steps<'a>( - context: v8::Local<'a, v8::Context>, - module: v8::Local<v8::Module>, -) -> Option<v8::Local<'a, v8::Value>> { - // SAFETY: `CallbackScope` can be safely constructed from `Local<Context>` - let scope = &mut unsafe { v8::CallbackScope::new(context) }; - let tc_scope = &mut v8::TryCatch::new(scope); - let module_map = JsRuntime::module_map_from(tc_scope); - - let handle = v8::Global::<v8::Module>::new(tc_scope, module); - let value_handle = module_map - .borrow_mut() - .json_value_store - .remove(&handle) - .unwrap(); - let value_local = v8::Local::new(tc_scope, value_handle); - - let name = v8::String::new(tc_scope, "default").unwrap(); - // This should never fail - assert!( - module.set_synthetic_module_export(tc_scope, name, value_local) - == Some(true) - ); - assert!(!tc_scope.has_caught()); - - // Since TLA is active we need to return a promise. - let resolver = v8::PromiseResolver::new(tc_scope).unwrap(); - let undefined = v8::undefined(tc_scope); - resolver.resolve(tc_scope, undefined.into()); - Some(resolver.get_promise(tc_scope).into()) -} - -pub fn module_origin<'a>( - s: &mut v8::HandleScope<'a>, - resource_name: v8::Local<'a, v8::String>, -) -> v8::ScriptOrigin<'a> { - let source_map_url = v8::String::empty(s); - v8::ScriptOrigin::new( - s, - resource_name.into(), - 0, - 0, - false, - 123, - source_map_url.into(), - true, - false, - true, - ) -} |