diff --git a/bench_util/js_runtime.rs b/bench_util/js_runtime.rs index 57085ef964..a12e6ca62a 100644 --- a/bench_util/js_runtime.rs +++ b/bench_util/js_runtime.rs @@ -10,9 +10,7 @@ use crate::profiling::is_profiling; pub fn create_js_runtime(setup: impl FnOnce() -> Vec) -> JsRuntime { JsRuntime::new(RuntimeOptions { extensions: setup(), - module_loader: Some( - std::rc::Rc::new(deno_core::ExtModuleLoader::default()), - ), + module_loader: None, ..Default::default() }) } diff --git a/cli/tests/testdata/run/extension_dynamic_import.ts.out b/cli/tests/testdata/run/extension_dynamic_import.ts.out index 18b05ea47d..081318960e 100644 --- a/cli/tests/testdata/run/extension_dynamic_import.ts.out +++ b/cli/tests/testdata/run/extension_dynamic_import.ts.out @@ -1,4 +1,4 @@ -error: Uncaught TypeError: Cannot load extension module from external code +error: Uncaught (in promise) TypeError: Cannot load extension module from external code await import("ext:runtime/01_errors.js"); ^ at [WILDCARD]/extension_dynamic_import.ts:1:1 diff --git a/core/bindings.rs b/core/bindings.rs index 2d9c914619..1437bc6575 100644 --- a/core/bindings.rs +++ b/core/bindings.rs @@ -10,7 +10,6 @@ use crate::error::is_instance_of_error; use crate::error::JsStackFrame; use crate::modules::get_asserted_module_type_from_assertions; use crate::modules::parse_import_assertions; -use crate::modules::resolve_helper; use crate::modules::validate_import_assertions; use crate::modules::ImportAssertionsKind; use crate::modules::ModuleMap; @@ -259,46 +258,43 @@ pub fn host_import_module_dynamically_callback<'s>( .unwrap() .to_rust_string_lossy(scope); - let is_ext_module = specifier_str.starts_with("ext:"); let resolver = v8::PromiseResolver::new(scope).unwrap(); let promise = resolver.get_promise(scope); - if !is_ext_module { - let assertions = parse_import_assertions( - scope, - import_assertions, - ImportAssertionsKind::DynamicImport, + let assertions = parse_import_assertions( + scope, + import_assertions, + ImportAssertionsKind::DynamicImport, + ); + + { + let tc_scope = &mut v8::TryCatch::new(scope); + validate_import_assertions(tc_scope, &assertions); + if tc_scope.has_caught() { + let e = tc_scope.exception().unwrap(); + resolver.reject(tc_scope, e); + } + } + let asserted_module_type = + get_asserted_module_type_from_assertions(&assertions); + + let resolver_handle = v8::Global::new(scope, resolver); + { + let state_rc = JsRuntime::state(scope); + let module_map_rc = JsRuntime::module_map(scope); + + debug!( + "dyn_import specifier {} referrer {} ", + specifier_str, referrer_name_str ); - - { - let tc_scope = &mut v8::TryCatch::new(scope); - validate_import_assertions(tc_scope, &assertions); - if tc_scope.has_caught() { - let e = tc_scope.exception().unwrap(); - resolver.reject(tc_scope, e); - } - } - let asserted_module_type = - get_asserted_module_type_from_assertions(&assertions); - - let resolver_handle = v8::Global::new(scope, resolver); - { - let state_rc = JsRuntime::state(scope); - let module_map_rc = JsRuntime::module_map(scope); - - debug!( - "dyn_import specifier {} referrer {} ", - specifier_str, referrer_name_str - ); - ModuleMap::load_dynamic_import( - module_map_rc, - &specifier_str, - &referrer_name_str, - asserted_module_type, - resolver_handle, - ); - state_rc.borrow_mut().notify_new_dynamic_import(); - } + ModuleMap::load_dynamic_import( + module_map_rc, + &specifier_str, + &referrer_name_str, + asserted_module_type, + resolver_handle, + ); + state_rc.borrow_mut().notify_new_dynamic_import(); } // Map errors from module resolution (not JS errors from module execution) to // ones rethrown from this scope, so they include the call stack of the @@ -311,16 +307,6 @@ pub fn host_import_module_dynamically_callback<'s>( let promise = promise.catch(scope, map_err).unwrap(); - if is_ext_module { - let message = v8::String::new_external_onebyte_static( - scope, - b"Cannot load extension module from external code", - ) - .unwrap(); - let exception = v8::Exception::type_error(scope, message); - resolver.reject(scope, exception); - } - Some(promise) } @@ -375,13 +361,7 @@ fn import_meta_resolve( url_prop.to_rust_string_lossy(scope) }; let module_map_rc = JsRuntime::module_map(scope); - let (loader, snapshot_loaded_and_not_snapshotting) = { - let module_map = module_map_rc.borrow(); - ( - module_map.loader.clone(), - module_map.snapshot_loaded_and_not_snapshotting, - ) - }; + let loader = module_map_rc.borrow().loader.clone(); let specifier_str = specifier.to_rust_string_lossy(scope); if specifier_str.starts_with("npm:") { @@ -389,13 +369,8 @@ fn import_meta_resolve( return; } - match resolve_helper( - snapshot_loaded_and_not_snapshotting, - loader, - &specifier_str, - &referrer, - ResolutionKind::DynamicImport, - ) { + match loader.resolve(&specifier_str, &referrer, ResolutionKind::DynamicImport) + { Ok(resolved) => { let resolved_val = serde_v8::to_v8(scope, resolved.as_str()).unwrap(); rv.set(resolved_val); diff --git a/core/extensions.rs b/core/extensions.rs index a8b52eb3b6..ba151da3d3 100644 --- a/core/extensions.rs +++ b/core/extensions.rs @@ -471,6 +471,16 @@ 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/modules.rs b/core/modules.rs index c63c4dd30b..bc795de5cf 100644 --- a/core/modules.rs +++ b/core/modules.rs @@ -9,6 +9,7 @@ use crate::module_specifier::ModuleSpecifier; use crate::resolve_import; use crate::resolve_url; use crate::snapshot_util::SnapshottedData; +use crate::Extension; use crate::JsRuntime; use crate::OpState; use anyhow::Error; @@ -379,25 +380,6 @@ impl ModuleLoader for NoopModuleLoader { } } -/// Helper function, that calls into `loader.resolve()`, but denies resolution -/// of `ext` scheme if we are running with a snapshot loaded and not -/// creating a snapshot -pub(crate) fn resolve_helper( - snapshot_loaded_and_not_snapshotting: bool, - loader: Rc, - specifier: &str, - referrer: &str, - kind: ResolutionKind, -) -> Result { - if snapshot_loaded_and_not_snapshotting && specifier.starts_with("ext:") { - return Err(generic_error( - "Cannot load extension module from external code", - )); - } - - loader.resolve(specifier, referrer, kind) -} - /// Function that can be passed to the `ExtModuleLoader` that allows to /// transpile sources before passing to V8. pub type ExtModuleLoaderCb = @@ -405,7 +387,8 @@ pub type ExtModuleLoaderCb = pub struct ExtModuleLoader { module_loader: Rc, - esm_sources: Vec, + extensions: Rc>>, + ext_resolution_allowed: RefCell, used_esm_sources: RefCell>, maybe_load_callback: Option, } @@ -414,7 +397,8 @@ impl Default for ExtModuleLoader { fn default() -> Self { Self { module_loader: Rc::new(NoopModuleLoader), - esm_sources: vec![], + extensions: Default::default(), + ext_resolution_allowed: Default::default(), used_esm_sources: RefCell::new(HashMap::default()), maybe_load_callback: None, } @@ -424,70 +408,46 @@ impl Default for ExtModuleLoader { impl ExtModuleLoader { pub fn new( module_loader: Option>, - esm_sources: Vec, + extensions: Rc>>, maybe_load_callback: Option, ) -> Self { - let used_esm_sources: HashMap = esm_sources + let used_esm_sources: HashMap = extensions + .borrow() .iter() + .flat_map(|e| e.get_esm_sources()) + .flatten() .map(|file_source| (file_source.specifier.to_string(), false)) .collect(); ExtModuleLoader { module_loader: module_loader.unwrap_or_else(|| Rc::new(NoopModuleLoader)), - esm_sources, + extensions, + ext_resolution_allowed: Default::default(), used_esm_sources: RefCell::new(used_esm_sources), maybe_load_callback, } } -} -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 - .iter() - .filter(|(_s, v)| !*v) - .map(|(s, _)| s) - .collect(); - - if !unused_modules.is_empty() { - let mut msg = - "Following modules were passed to ExtModuleLoader but never used:\n" - .to_string(); - for m in unused_modules { - msg.push_str(" - "); - msg.push_str(m); - msg.push('\n'); - } - panic!("{}", msg); - } - } -} - -impl ModuleLoader for ExtModuleLoader { - fn resolve( + pub fn resolve( &self, specifier: &str, referrer: &str, kind: ResolutionKind, ) -> Result { - if let Ok(url_specifier) = ModuleSpecifier::parse(specifier) { - if url_specifier.scheme() == "ext" { - let referrer_specifier = ModuleSpecifier::parse(referrer).ok(); - if referrer == "." || referrer_specifier.unwrap().scheme() == "ext" { - return Ok(url_specifier); - } else { - return Err(generic_error( - "Cannot load extension module from external code", - )); - }; + 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) } - fn load( + pub fn load( &self, module_specifier: &ModuleSpecifier, maybe_referrer: Option<&ModuleSpecifier>, @@ -502,10 +462,10 @@ impl ModuleLoader for ExtModuleLoader { } let specifier = module_specifier.to_string(); - let maybe_file_source = self - .esm_sources + let extensions = self.extensions.borrow(); + let maybe_file_source = extensions .iter() - .find(|file_source| file_source.specifier == module_specifier.as_str()); + .find_map(|e| e.find_esm(module_specifier.as_str())); if let Some(file_source) = maybe_file_source { { @@ -538,7 +498,7 @@ impl ModuleLoader for ExtModuleLoader { .boxed_local() } - fn prepare_load( + pub fn prepare_load( &self, op_state: Rc>, module_specifier: &ModuleSpecifier, @@ -556,6 +516,37 @@ impl ModuleLoader for ExtModuleLoader { 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; + } +} + +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 + .iter() + .filter(|(_s, v)| !*v) + .map(|(s, _)| s) + .collect(); + + if !unused_modules.is_empty() { + let mut msg = + "Following modules were passed to ExtModuleLoader but never used:\n" + .to_string(); + for m in unused_modules { + msg.push_str(" - "); + msg.push_str(m); + msg.push('\n'); + } + panic!("{}", msg); + } + } } /// Basic file system module loader. @@ -643,8 +634,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>, - loader: Rc, - snapshot_loaded_and_not_snapshotting: bool, + loader: Rc, } impl RecursiveModuleLoad { @@ -700,9 +690,6 @@ impl RecursiveModuleLoad { init, state: LoadState::Init, module_map_rc: module_map_rc.clone(), - snapshot_loaded_and_not_snapshotting: module_map_rc - .borrow() - .snapshot_loaded_and_not_snapshotting, op_state, loader, pending: FuturesUnordered::new(), @@ -731,29 +718,17 @@ impl RecursiveModuleLoad { fn resolve_root(&self) -> Result { match self.init { - LoadInit::Main(ref specifier) => resolve_helper( - self.snapshot_loaded_and_not_snapshotting, - self.loader.clone(), - specifier, - ".", - ResolutionKind::MainModule, - ), - LoadInit::Side(ref specifier) => resolve_helper( - self.snapshot_loaded_and_not_snapshotting, - self.loader.clone(), - specifier, - ".", - ResolutionKind::Import, - ), - LoadInit::DynamicImport(ref specifier, ref referrer, _) => { - resolve_helper( - self.snapshot_loaded_and_not_snapshotting, - self.loader.clone(), - specifier, - referrer, - ResolutionKind::DynamicImport, - ) + LoadInit::Main(ref specifier) => { + self + .loader + .resolve(specifier, ".", ResolutionKind::MainModule) } + LoadInit::Side(ref specifier) => { + self.loader.resolve(specifier, ".", ResolutionKind::Import) + } + LoadInit::DynamicImport(ref specifier, ref referrer, _) => self + .loader + .resolve(specifier, referrer, ResolutionKind::DynamicImport), } } @@ -762,29 +737,21 @@ impl RecursiveModuleLoad { let (module_specifier, maybe_referrer) = match self.init { LoadInit::Main(ref specifier) => { - let spec = resolve_helper( - self.snapshot_loaded_and_not_snapshotting, - self.loader.clone(), - specifier, - ".", - ResolutionKind::MainModule, - )?; + let spec = + self + .loader + .resolve(specifier, ".", ResolutionKind::MainModule)?; (spec, None) } LoadInit::Side(ref specifier) => { - let spec = resolve_helper( - self.snapshot_loaded_and_not_snapshotting, - self.loader.clone(), - specifier, - ".", - ResolutionKind::Import, - )?; + let spec = + self + .loader + .resolve(specifier, ".", ResolutionKind::Import)?; (spec, None) } LoadInit::DynamicImport(ref specifier, ref referrer, _) => { - let spec = resolve_helper( - self.snapshot_loaded_and_not_snapshotting, - self.loader.clone(), + let spec = self.loader.resolve( specifier, referrer, ResolutionKind::DynamicImport, @@ -1093,7 +1060,7 @@ pub(crate) struct ModuleMap { pub(crate) next_load_id: ModuleLoadId, // Handling of futures for loading module sources - pub loader: Rc, + pub loader: Rc, op_state: Rc>, pub(crate) dynamic_import_map: HashMap>, @@ -1105,8 +1072,6 @@ pub(crate) struct ModuleMap { // This store is used temporarly, to forward parsed JSON // value from `new_json_module` to `json_module_evaluation_steps` json_value_store: HashMap, v8::Global>, - - pub(crate) snapshot_loaded_and_not_snapshotting: bool, } impl ModuleMap { @@ -1381,9 +1346,8 @@ impl ModuleMap { } pub(crate) fn new( - loader: Rc, + loader: Rc, op_state: Rc>, - snapshot_loaded_and_not_snapshotting: bool, ) -> ModuleMap { Self { handles: vec![], @@ -1397,7 +1361,6 @@ impl ModuleMap { preparing_dynamic_imports: FuturesUnordered::new(), pending_dynamic_imports: FuturesUnordered::new(), json_value_store: HashMap::new(), - snapshot_loaded_and_not_snapshotting, } } @@ -1526,9 +1489,7 @@ impl ModuleMap { return Err(ModuleError::Exception(exception)); } - let module_specifier = match resolve_helper( - self.snapshot_loaded_and_not_snapshotting, - self.loader.clone(), + let module_specifier = match self.loader.resolve( &import_specifier, name.as_ref(), if is_dynamic_import { @@ -1717,20 +1678,9 @@ impl ModuleMap { .dynamic_import_map .insert(load.id, resolver_handle); - let (loader, snapshot_loaded_and_not_snapshotting) = { - let module_map = module_map_rc.borrow(); - ( - module_map.loader.clone(), - module_map.snapshot_loaded_and_not_snapshotting, - ) - }; - let resolve_result = resolve_helper( - snapshot_loaded_and_not_snapshotting, - loader, - specifier, - referrer, - ResolutionKind::DynamicImport, - ); + 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 @@ -1764,14 +1714,10 @@ impl ModuleMap { referrer: &str, import_assertions: HashMap, ) -> Option> { - let resolved_specifier = resolve_helper( - self.snapshot_loaded_and_not_snapshotting, - self.loader.clone(), - specifier, - referrer, - ResolutionKind::Import, - ) - .expect("Module should have been already resolved"); + 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); @@ -3042,48 +2988,34 @@ if (import.meta.url != 'file:///main_with_code.js') throw Error(); } #[test] - fn ext_module_loader() { + fn ext_resolution() { let loader = ExtModuleLoader::default(); - assert!(loader - .resolve("ext:foo", "ext:bar", ResolutionKind::Import) - .is_ok()); + 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:foo", "file://bar", ResolutionKind::Import) + .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("file://foo", "file://bar", ResolutionKind::Import) + .resolve("ext:core.js", "ext:referrer.js", ResolutionKind::Import,) .err() .map(|e| e.to_string()), - Some( - "Module loading is not supported; attempted to resolve: \"file://foo\" from \"file://bar\"" - .to_string() - ) - ); - assert_eq!( - loader - .resolve("file://foo", "ext:bar", ResolutionKind::Import) - .err() - .map(|e| e.to_string()), - Some( - "Module loading is not supported; attempted to resolve: \"file://foo\" from \"ext:bar\"" - .to_string() - ) - ); - assert_eq!( - resolve_helper( - true, - Rc::new(loader), - "ext:core.js", - "file://bar", - 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 9676ce571f..1cbefb6fe9 100644 --- a/core/runtime.rs +++ b/core/runtime.rs @@ -132,7 +132,7 @@ pub struct JsRuntime { v8_isolate: Option, snapshot_options: snapshot_util::SnapshotOptions, allocations: IsolateAllocations, - extensions: Vec, + extensions: Rc>>, event_loop_middlewares: Vec>, // Marks if this is considered the top-level runtime. Used only be inspector. is_main: bool, @@ -416,7 +416,7 @@ impl JsRuntime { let global_context; let mut maybe_snapshotted_data = None; - let (mut isolate, snapshot_options) = if snapshot_options.will_snapshot() { + let mut isolate = if snapshot_options.will_snapshot() { let snapshot_creator = snapshot_util::create_snapshot_creator(refs, options.startup_snapshot); let mut isolate = JsRuntime::setup_isolate(snapshot_creator); @@ -433,7 +433,7 @@ impl JsRuntime { global_context = v8::Global::new(scope, context); } - (isolate, snapshot_options) + isolate } else { #[cfg(not(target_env = "msvc"))] let vtable: &'static v8::RustAllocatorVtable< @@ -492,7 +492,7 @@ impl JsRuntime { global_context = v8::Global::new(scope, context); } - (isolate, snapshot_options) + isolate }; // SAFETY: this is first use of `isolate_ptr` so we are sure we're @@ -521,61 +521,33 @@ impl JsRuntime { None }; - let loader = if snapshot_options != snapshot_util::SnapshotOptions::Load { - let esm_sources = options + let loader = options + .module_loader + .unwrap_or_else(|| Rc::new(NoopModuleLoader)); + #[cfg(feature = "include_js_files_for_snapshotting")] + if snapshot_options.will_snapshot() { + for source in options .extensions .iter() - .flat_map(|ext| match ext.get_esm_sources() { - Some(s) => s.to_owned(), - None => vec![], - }) - .collect::>(); - - #[cfg(feature = "include_js_files_for_snapshotting")] - if snapshot_options != snapshot_util::SnapshotOptions::None { - for source in &esm_sources { - use crate::ExtensionFileSourceCode; - if let ExtensionFileSourceCode::LoadedFromFsDuringSnapshot(path) = - &source.code - { - println!("cargo:rerun-if-changed={}", path.display()) - } - } - } - - #[cfg(feature = "include_js_files_for_snapshotting")] + .flat_map(|e| vec![e.get_esm_sources(), e.get_js_sources()]) + .flatten() + .flatten() { - let js_sources = options - .extensions - .iter() - .flat_map(|ext| match ext.get_js_sources() { - Some(s) => s.to_owned(), - None => vec![], - }) - .collect::>(); - - if snapshot_options != snapshot_util::SnapshotOptions::None { - for source in &js_sources { - use crate::ExtensionFileSourceCode; - if let ExtensionFileSourceCode::LoadedFromFsDuringSnapshot(path) = - &source.code - { - println!("cargo:rerun-if-changed={}", path.display()) - } - } + use crate::ExtensionFileSourceCode; + if let ExtensionFileSourceCode::LoadedFromFsDuringSnapshot(path) = + &source.code + { + println!("cargo:rerun-if-changed={}", path.display()) } } - - Rc::new(crate::modules::ExtModuleLoader::new( - options.module_loader, - esm_sources, - options.snapshot_module_load_cb, - )) - } else { - options - .module_loader - .unwrap_or_else(|| Rc::new(NoopModuleLoader)) - }; + } + 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 mut state = state_rc.borrow_mut(); @@ -589,12 +561,8 @@ 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( - loader, - op_state, - snapshot_options == snapshot_util::SnapshotOptions::Load, - ))); + let module_map_rc = + Rc::new(RefCell::new(ModuleMap::new(ext_loader, op_state))); if let Some(snapshotted_data) = maybe_snapshotted_data { let scope = &mut v8::HandleScope::with_context(&mut isolate, global_context); @@ -610,10 +578,10 @@ impl JsRuntime { v8_isolate: Some(isolate), snapshot_options, allocations: IsolateAllocations::default(), - event_loop_middlewares: Vec::with_capacity(options.extensions.len()), - extensions: options.extensions, + event_loop_middlewares: Vec::with_capacity(num_extensions), + extensions, state: state_rc, - module_map: Some(module_map_rc), + module_map: Some(module_map_rc.clone()), is_main: options.is_main, }; @@ -621,7 +589,9 @@ 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 } @@ -722,7 +692,21 @@ impl JsRuntime { JsRealm::new(v8::Global::new(scope, context)) }; + 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) } @@ -790,7 +774,7 @@ impl JsRuntime { // Take extensions to avoid double-borrow let extensions = std::mem::take(&mut self.extensions); - for ext in &extensions { + for ext in extensions.borrow().iter() { { if let Some(esm_files) = ext.get_esm_sources() { if let Some(entry_point) = ext.get_esm_entry_point() { @@ -863,23 +847,15 @@ impl JsRuntime { /// Initializes ops of provided Extensions fn init_extension_ops(&mut self) -> Result<(), Error> { let op_state = self.op_state(); - // Take extensions to avoid double-borrow - { - let mut extensions: Vec = std::mem::take(&mut self.extensions); + // Setup state + for e in self.extensions.borrow_mut().iter_mut() { + // ops are already registered during in bindings::initialize_context(); + e.init_state(&mut op_state.borrow_mut()); - // Setup state - for e in extensions.iter_mut() { - // ops are already registered during in bindings::initialize_context(); - e.init_state(&mut op_state.borrow_mut()); - - // Setup event-loop middleware - if let Some(middleware) = e.init_event_loop_middleware() { - self.event_loop_middlewares.push(middleware); - } + // Setup event-loop middleware + if let Some(middleware) = e.init_event_loop_middleware() { + self.event_loop_middlewares.push(middleware); } - - // Restore extensions - self.extensions = extensions; } Ok(()) } diff --git a/runtime/examples/hello_runtime.js b/runtime/examples/hello_runtime.js index 066fa21d66..5b079d8d89 100644 --- a/runtime/examples/hello_runtime.js +++ b/runtime/examples/hello_runtime.js @@ -1,3 +1,4 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. console.log("Hello world!"); console.log(Deno); +Extension.hello(); diff --git a/runtime/examples/hello_runtime.rs b/runtime/examples/hello_runtime.rs index 2e930a03f2..157a200f4c 100644 --- a/runtime/examples/hello_runtime.rs +++ b/runtime/examples/hello_runtime.rs @@ -1,72 +1,31 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use deno_core::anyhow::Context; use deno_core::error::AnyError; use deno_core::FsModuleLoader; -use deno_runtime::deno_broadcast_channel::InMemoryBroadcastChannel; -use deno_runtime::deno_web::BlobStore; use deno_runtime::permissions::PermissionsContainer; use deno_runtime::worker::MainWorker; use deno_runtime::worker::WorkerOptions; -use deno_runtime::BootstrapOptions; use std::path::Path; use std::rc::Rc; -use std::sync::Arc; -fn get_error_class_name(e: &AnyError) -> &'static str { - deno_runtime::errors::get_error_class_name(e).unwrap_or("Error") -} +deno_core::extension!(hello_runtime, esm = ["hello_runtime_bootstrap.js"]); #[tokio::main] async fn main() -> Result<(), AnyError> { - let module_loader = Rc::new(FsModuleLoader); - let create_web_worker_cb = Arc::new(|_| { - todo!("Web workers are not supported in the example"); - }); - let web_worker_event_cb = Arc::new(|_| { - todo!("Web workers are not supported in the example"); - }); - - let options = WorkerOptions { - bootstrap: BootstrapOptions::default(), - extensions: vec![], - startup_snapshot: None, - unsafely_ignore_certificate_errors: None, - root_cert_store_provider: None, - seed: None, - source_map_getter: None, - format_js_error_fn: None, - web_worker_preload_module_cb: web_worker_event_cb.clone(), - web_worker_pre_execute_module_cb: web_worker_event_cb, - create_web_worker_cb, - maybe_inspector_server: None, - should_break_on_first_statement: false, - should_wait_for_inspector_session: false, - module_loader, - node_fs: None, - npm_resolver: None, - get_error_class_fn: Some(&get_error_class_name), - cache_storage_dir: None, - origin_storage_dir: None, - blob_store: BlobStore::default(), - broadcast_channel: InMemoryBroadcastChannel::default(), - shared_array_buffer_store: None, - compiled_wasm_module_store: None, - stdio: Default::default(), - }; - let js_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("examples/hello_runtime.js"); let main_module = deno_core::resolve_path( &js_path.to_string_lossy(), - &std::env::current_dir().context("Unable to get CWD")?, + &std::env::current_dir()?, )?; - let permissions = PermissionsContainer::allow_all(); - let mut worker = MainWorker::bootstrap_from_options( main_module.clone(), - permissions, - options, + PermissionsContainer::allow_all(), + WorkerOptions { + module_loader: Rc::new(FsModuleLoader), + extensions: vec![hello_runtime::init_ops_and_esm()], + ..Default::default() + }, ); worker.execute_main_module(&main_module).await?; worker.run_event_loop(false).await?; diff --git a/runtime/examples/hello_runtime_bootstrap.js b/runtime/examples/hello_runtime_bootstrap.js new file mode 100644 index 0000000000..759dde9395 --- /dev/null +++ b/runtime/examples/hello_runtime_bootstrap.js @@ -0,0 +1,5 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +function hello() { + console.log("Hello from extension!"); +} +globalThis.Extension = { hello };