use crate::inspector_server::InspectorServer;use crate::js;use crate::ops;use crate::ops::io::Stdio;use crate::permissions::Permissions;use crate::BootstrapOptions;use deno_broadcast_channel::InMemoryBroadcastChannel;use deno_core::error::AnyError;use deno_core::error::JsError;use deno_core::futures::Future;use deno_core::located_script_name;use deno_core::CompiledWasmModuleStore;use deno_core::Extension;use deno_core::GetErrorClassFn;use deno_core::JsRuntime;use deno_core::LocalInspectorSession;use deno_core::ModuleId;use deno_core::ModuleLoader;use deno_core::ModuleSpecifier;use deno_core::RuntimeOptions;use deno_core::SharedArrayBufferStore;use deno_core::SourceMapGetter;use deno_tls::rustls::RootCertStore;use deno_web::BlobStore;use log::debug;use std::pin::Pin;use std::rc::Rc;use std::sync::atomic::AtomicI32;use std::sync::atomic::Ordering::Relaxed;use std::sync::Arc;use std::task::Context;use std::task::Poll;
pub type FormatJsErrorFn = dyn Fn(&JsError) -> String + Sync + Send;
#[derive(Clone, Default)]pub struct ExitCode(Arc<AtomicI32>);
impl ExitCode { pub fn get(&self) -> i32 { self.0.load(Relaxed) }
pub fn set(&mut self, code: i32) { self.0.store(code, Relaxed); }}pub struct MainWorker { pub js_runtime: JsRuntime, should_break_on_first_statement: bool, exit_code: ExitCode,}
pub struct WorkerOptions { pub bootstrap: BootstrapOptions, pub extensions: Vec<Extension>, pub unsafely_ignore_certificate_errors: Option<Vec<String>>, pub root_cert_store: Option<RootCertStore>, pub seed: Option<u64>, pub module_loader: Rc<dyn ModuleLoader>, pub create_web_worker_cb: Arc<ops::worker_host::CreateWebWorkerCb>, pub web_worker_preload_module_cb: Arc<ops::worker_host::PreloadModuleCb>, pub format_js_error_fn: Option<Arc<FormatJsErrorFn>>, pub source_map_getter: Option<Box<dyn SourceMapGetter>>, pub maybe_inspector_server: Option<Arc<InspectorServer>>, pub should_break_on_first_statement: bool, pub get_error_class_fn: Option<GetErrorClassFn>, pub origin_storage_dir: Option<std::path::PathBuf>, pub blob_store: BlobStore, pub broadcast_channel: InMemoryBroadcastChannel, pub shared_array_buffer_store: Option<SharedArrayBufferStore>, pub compiled_wasm_module_store: Option<CompiledWasmModuleStore>, pub stdio: Stdio, pub startup_snapshot: Option<deno_core::Snapshot>,}
impl MainWorker { pub fn bootstrap_from_options( main_module: ModuleSpecifier, permissions: Permissions, options: WorkerOptions, ) -> Self { let bootstrap_options = options.bootstrap.clone(); let mut worker = Self::from_options(main_module, permissions, options); worker.bootstrap(&bootstrap_options); worker }
pub fn from_options( main_module: ModuleSpecifier, permissions: Permissions, mut options: WorkerOptions, ) -> Self { let unstable = options.bootstrap.unstable; let enable_testing_features = options.bootstrap.enable_testing_features; let perm_ext = Extension::builder() .state(move |state| { state.put::<Permissions>(permissions.clone()); state.put(ops::UnstableChecker { unstable }); state.put(ops::TestingFeaturesEnabled(enable_testing_features)); Ok(()) }) .build(); let exit_code = ExitCode(Arc::new(AtomicI32::new(0)));
let mut extensions: Vec<Extension> = vec![ deno_webidl::init(), deno_console::init(), deno_url::init(), deno_web::init::<Permissions>( options.blob_store.clone(), options.bootstrap.location.clone(), ), deno_fetch::init::<Permissions>(deno_fetch::Options { user_agent: options.bootstrap.user_agent.clone(), root_cert_store: options.root_cert_store.clone(), unsafely_ignore_certificate_errors: options .unsafely_ignore_certificate_errors .clone(), file_fetch_handler: Rc::new(deno_fetch::FsFetchHandler), ..Default::default() }), deno_websocket::init::<Permissions>( options.bootstrap.user_agent.clone(), options.root_cert_store.clone(), options.unsafely_ignore_certificate_errors.clone(), ), deno_webstorage::init(options.origin_storage_dir.clone()), deno_broadcast_channel::init(options.broadcast_channel.clone(), unstable), deno_crypto::init(options.seed), deno_webgpu::init(unstable), deno_ffi::init::<Permissions>(unstable), ops::runtime::init(main_module.clone()), ops::worker_host::init( options.create_web_worker_cb.clone(), options.web_worker_preload_module_cb.clone(), options.format_js_error_fn.clone(), ), ops::spawn::init(), ops::fs_events::init(), ops::fs::init(), ops::io::init(), ops::io::init_stdio(options.stdio), deno_tls::init(), deno_net::init::<Permissions>( options.root_cert_store.clone(), unstable, options.unsafely_ignore_certificate_errors.clone(), ), ops::os::init(exit_code.clone()), ops::permissions::init(), ops::process::init(), ops::signal::init(), ops::tty::init(), deno_http::init(), ops::http::init(), js::init(), perm_ext, ]; extensions.extend(std::mem::take(&mut options.extensions));
let mut js_runtime = JsRuntime::new(RuntimeOptions { module_loader: Some(options.module_loader.clone()), startup_snapshot: options.startup_snapshot.take(), source_map_getter: options.source_map_getter, get_error_class_fn: options.get_error_class_fn, shared_array_buffer_store: options.shared_array_buffer_store.clone(), compiled_wasm_module_store: options.compiled_wasm_module_store.clone(), extensions, ..Default::default() });
if let Some(server) = options.maybe_inspector_server.clone() { server.register_inspector( main_module.to_string(), &mut js_runtime, options.should_break_on_first_statement, ); }
Self { js_runtime, should_break_on_first_statement: options.should_break_on_first_statement, exit_code, } }
pub fn bootstrap(&mut self, options: &BootstrapOptions) { let script = format!("bootstrap.mainRuntime({})", options.as_json()); self .execute_script(&located_script_name!(), &script) .expect("Failed to execute bootstrap script"); }
pub fn execute_script( &mut self, script_name: &str, source_code: &str, ) -> Result<(), AnyError> { self.js_runtime.execute_script(script_name, source_code)?; Ok(()) }
pub async fn preload_module( &mut self, module_specifier: &ModuleSpecifier, main: bool, ) -> Result<ModuleId, AnyError> { if main { self .js_runtime .load_main_module(module_specifier, None) .await } else { self .js_runtime .load_side_module(module_specifier, None) .await } }
pub async fn evaluate_module( &mut self, id: ModuleId, ) -> Result<(), AnyError> { let mut receiver = self.js_runtime.mod_evaluate(id); tokio::select! { biased;
maybe_result = &mut receiver => { debug!("received module evaluate {:#?}", maybe_result); maybe_result.expect("Module evaluation result not provided.") }
event_loop_result = self.run_event_loop(false) => { event_loop_result?; let maybe_result = receiver.await; maybe_result.expect("Module evaluation result not provided.") } } }
pub async fn execute_side_module( &mut self, module_specifier: &ModuleSpecifier, ) -> Result<(), AnyError> { let id = self.preload_module(module_specifier, false).await?; self.wait_for_inspector_session(); self.evaluate_module(id).await }
pub async fn execute_main_module( &mut self, module_specifier: &ModuleSpecifier, ) -> Result<(), AnyError> { let id = self.preload_module(module_specifier, true).await?; self.wait_for_inspector_session(); self.evaluate_module(id).await }
fn wait_for_inspector_session(&mut self) { if self.should_break_on_first_statement { self .js_runtime .inspector() .wait_for_session_and_break_on_next_statement() } }
pub async fn create_inspector_session(&mut self) -> LocalInspectorSession { let inspector = self.js_runtime.inspector(); inspector.create_local_session() }
pub fn poll_event_loop( &mut self, cx: &mut Context, wait_for_inspector: bool, ) -> Poll<Result<(), AnyError>> { self.js_runtime.poll_event_loop(cx, wait_for_inspector) }
pub async fn run_event_loop( &mut self, wait_for_inspector: bool, ) -> Result<(), AnyError> { self.js_runtime.run_event_loop(wait_for_inspector).await }
pub async fn with_event_loop<'a, T>( &mut self, mut fut: Pin<Box<dyn Future<Output = T> + 'a>>, ) -> T { loop { tokio::select! { biased; result = &mut fut => { return result; } _ = self.run_event_loop(false) => {} }; } }
pub fn get_exit_code(&self) -> i32 { self.exit_code.get() }
pub fn dispatch_load_event( &mut self, script_name: &str, ) -> Result<(), AnyError> { self.execute_script( script_name, "dispatchEvent(new Event('load'))", ) }
pub fn dispatch_unload_event( &mut self, script_name: &str, ) -> Result<(), AnyError> { self.execute_script( script_name, "dispatchEvent(new Event('unload'))", ) }}
#[cfg(test)]mod tests { use super::*; use deno_core::resolve_url_or_path;
fn create_test_worker() -> MainWorker { let main_module = resolve_url_or_path("./hello.js").unwrap(); let permissions = Permissions::default();
let options = WorkerOptions { bootstrap: BootstrapOptions { args: vec![], cpu_count: 1, debug_flag: false, enable_testing_features: false, location: None, no_color: true, is_tty: false, runtime_version: "x".to_string(), ts_version: "x".to_string(), unstable: false, user_agent: "x".to_string(), }, extensions: vec![], unsafely_ignore_certificate_errors: None, root_cert_store: None, seed: None, format_js_error_fn: None, source_map_getter: None, web_worker_preload_module_cb: Arc::new(|_| unreachable!()), create_web_worker_cb: Arc::new(|_| unreachable!()), maybe_inspector_server: None, should_break_on_first_statement: false, module_loader: Rc::new(deno_core::FsModuleLoader), get_error_class_fn: 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(), startup_snapshot: None, };
MainWorker::bootstrap_from_options(main_module, permissions, options) }
#[tokio::test] async fn execute_mod_esm_imports_a() { let p = test_util::testdata_path().join("esm_imports_a.js"); let module_specifier = resolve_url_or_path(&p.to_string_lossy()).unwrap(); let mut worker = create_test_worker(); let result = worker.execute_main_module(&module_specifier).await; if let Err(err) = result { eprintln!("execute_mod err {:?}", err); } if let Err(e) = worker.run_event_loop(false).await { panic!("Future got unexpected error: {:?}", e); } }
#[tokio::test] async fn execute_mod_circular() { let p = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) .parent() .unwrap() .join("tests/circular1.js"); let module_specifier = resolve_url_or_path(&p.to_string_lossy()).unwrap(); let mut worker = create_test_worker(); let result = worker.execute_main_module(&module_specifier).await; if let Err(err) = result { eprintln!("execute_mod err {:?}", err); } if let Err(e) = worker.run_event_loop(false).await { panic!("Future got unexpected error: {:?}", e); } }
#[tokio::test] async fn execute_mod_resolve_error() { let mut worker = create_test_worker(); let module_specifier = resolve_url_or_path("does-not-exist").unwrap(); let result = worker.execute_main_module(&module_specifier).await; assert!(result.is_err()); }
#[tokio::test] async fn execute_mod_002_hello() { let mut worker = create_test_worker(); let p = test_util::testdata_path().join("001_hello.js"); let module_specifier = resolve_url_or_path(&p.to_string_lossy()).unwrap(); let result = worker.execute_main_module(&module_specifier).await; assert!(result.is_ok()); }}