deno_runtime/
worker.rs

1// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
2use std::borrow::Cow;
3use std::collections::HashMap;
4use std::rc::Rc;
5use std::sync::atomic::AtomicBool;
6use std::sync::atomic::AtomicI32;
7use std::sync::atomic::Ordering::Relaxed;
8use std::sync::Arc;
9use std::time::Duration;
10use std::time::Instant;
11
12use deno_broadcast_channel::InMemoryBroadcastChannel;
13use deno_cache::CreateCache;
14use deno_cache::SqliteBackedCache;
15use deno_core::error::AnyError;
16use deno_core::error::JsError;
17use deno_core::merge_op_metrics;
18use deno_core::v8;
19use deno_core::CompiledWasmModuleStore;
20use deno_core::Extension;
21use deno_core::FeatureChecker;
22use deno_core::GetErrorClassFn;
23use deno_core::InspectorSessionKind;
24use deno_core::InspectorSessionOptions;
25use deno_core::JsRuntime;
26use deno_core::LocalInspectorSession;
27use deno_core::ModuleCodeString;
28use deno_core::ModuleId;
29use deno_core::ModuleLoader;
30use deno_core::ModuleSpecifier;
31use deno_core::OpMetricsFactoryFn;
32use deno_core::OpMetricsSummaryTracker;
33use deno_core::PollEventLoopOptions;
34use deno_core::RuntimeOptions;
35use deno_core::SharedArrayBufferStore;
36use deno_core::SourceCodeCacheInfo;
37use deno_cron::local::LocalCronHandler;
38use deno_fs::FileSystem;
39use deno_http::DefaultHttpPropertyExtractor;
40use deno_io::Stdio;
41use deno_kv::dynamic::MultiBackendDbHandler;
42use deno_node::NodeExtInitServices;
43use deno_permissions::PermissionsContainer;
44use deno_tls::RootCertStoreProvider;
45use deno_tls::TlsKeys;
46use deno_web::BlobStore;
47use log::debug;
48
49use crate::code_cache::CodeCache;
50use crate::code_cache::CodeCacheType;
51use crate::inspector_server::InspectorServer;
52use crate::ops;
53use crate::ops::process::NpmProcessStateProviderRc;
54use crate::shared::maybe_transpile_source;
55use crate::shared::runtime;
56use crate::BootstrapOptions;
57
58pub type FormatJsErrorFn = dyn Fn(&JsError) -> String + Sync + Send;
59
60pub fn import_meta_resolve_callback(
61  loader: &dyn deno_core::ModuleLoader,
62  specifier: String,
63  referrer: String,
64) -> Result<ModuleSpecifier, AnyError> {
65  loader.resolve(
66    &specifier,
67    &referrer,
68    deno_core::ResolutionKind::DynamicImport,
69  )
70}
71
72// TODO(bartlomieju): temporary measurement until we start supporting more
73// module types
74pub fn validate_import_attributes_callback(
75  scope: &mut v8::HandleScope,
76  attributes: &HashMap<String, String>,
77) {
78  for (key, value) in attributes {
79    let msg = if key != "type" {
80      Some(format!("\"{key}\" attribute is not supported."))
81    } else if value != "json" && value != "$$deno-core-internal-wasm-module" {
82      Some(format!("\"{value}\" is not a valid module type."))
83    } else {
84      None
85    };
86
87    let Some(msg) = msg else {
88      continue;
89    };
90
91    let message = v8::String::new(scope, &msg).unwrap();
92    let exception = v8::Exception::type_error(scope, message);
93    scope.throw_exception(exception);
94    return;
95  }
96}
97
98#[derive(Clone, Default)]
99pub struct ExitCode(Arc<AtomicI32>);
100
101impl ExitCode {
102  pub fn get(&self) -> i32 {
103    self.0.load(Relaxed)
104  }
105
106  pub fn set(&mut self, code: i32) {
107    self.0.store(code, Relaxed);
108  }
109}
110
111/// This worker is created and used by almost all
112/// subcommands in Deno executable.
113///
114/// It provides ops available in the `Deno` namespace.
115///
116/// All `WebWorker`s created during program execution
117/// are descendants of this worker.
118pub struct MainWorker {
119  pub js_runtime: JsRuntime,
120  should_break_on_first_statement: bool,
121  should_wait_for_inspector_session: bool,
122  exit_code: ExitCode,
123  bootstrap_fn_global: Option<v8::Global<v8::Function>>,
124  dispatch_load_event_fn_global: v8::Global<v8::Function>,
125  dispatch_beforeunload_event_fn_global: v8::Global<v8::Function>,
126  dispatch_unload_event_fn_global: v8::Global<v8::Function>,
127  dispatch_process_beforeexit_event_fn_global: v8::Global<v8::Function>,
128  dispatch_process_exit_event_fn_global: v8::Global<v8::Function>,
129}
130
131pub struct WorkerServiceOptions {
132  pub blob_store: Arc<BlobStore>,
133  pub broadcast_channel: InMemoryBroadcastChannel,
134  pub feature_checker: Arc<FeatureChecker>,
135  pub fs: Arc<dyn FileSystem>,
136  /// Implementation of `ModuleLoader` which will be
137  /// called when V8 requests to load ES modules.
138  ///
139  /// If not provided runtime will error if code being
140  /// executed tries to load modules.
141  pub module_loader: Rc<dyn ModuleLoader>,
142  pub node_services: Option<NodeExtInitServices>,
143  pub npm_process_state_provider: Option<NpmProcessStateProviderRc>,
144  pub permissions: PermissionsContainer,
145  pub root_cert_store_provider: Option<Arc<dyn RootCertStoreProvider>>,
146  pub fetch_dns_resolver: deno_fetch::dns::Resolver,
147
148  /// The store to use for transferring SharedArrayBuffers between isolates.
149  /// If multiple isolates should have the possibility of sharing
150  /// SharedArrayBuffers, they should use the same [SharedArrayBufferStore]. If
151  /// no [SharedArrayBufferStore] is specified, SharedArrayBuffer can not be
152  /// serialized.
153  pub shared_array_buffer_store: Option<SharedArrayBufferStore>,
154
155  /// The store to use for transferring `WebAssembly.Module` objects between
156  /// isolates.
157  /// If multiple isolates should have the possibility of sharing
158  /// `WebAssembly.Module` objects, they should use the same
159  /// [CompiledWasmModuleStore]. If no [CompiledWasmModuleStore] is specified,
160  /// `WebAssembly.Module` objects cannot be serialized.
161  pub compiled_wasm_module_store: Option<CompiledWasmModuleStore>,
162
163  /// V8 code cache for module and script source code.
164  pub v8_code_cache: Option<Arc<dyn CodeCache>>,
165}
166
167pub struct WorkerOptions {
168  pub bootstrap: BootstrapOptions,
169
170  /// JsRuntime extensions, not to be confused with ES modules.
171  ///
172  /// Extensions register "ops" and JavaScript sources provided in `js` or `esm`
173  /// configuration. If you are using a snapshot, then extensions shouldn't
174  /// provide JavaScript sources that were already snapshotted.
175  pub extensions: Vec<Extension>,
176
177  /// V8 snapshot that should be loaded on startup.
178  pub startup_snapshot: Option<&'static [u8]>,
179
180  /// Should op registration be skipped?
181  pub skip_op_registration: bool,
182
183  /// Optional isolate creation parameters, such as heap limits.
184  pub create_params: Option<v8::CreateParams>,
185
186  pub unsafely_ignore_certificate_errors: Option<Vec<String>>,
187  pub seed: Option<u64>,
188
189  // Callbacks invoked when creating new instance of WebWorker
190  pub create_web_worker_cb: Arc<ops::worker_host::CreateWebWorkerCb>,
191  pub format_js_error_fn: Option<Arc<FormatJsErrorFn>>,
192
193  pub maybe_inspector_server: Option<Arc<InspectorServer>>,
194  // If true, the worker will wait for inspector session and break on first
195  // statement of user code. Takes higher precedence than
196  // `should_wait_for_inspector_session`.
197  pub should_break_on_first_statement: bool,
198  // If true, the worker will wait for inspector session before executing
199  // user code.
200  pub should_wait_for_inspector_session: bool,
201  /// If Some, print a low-level trace output for ops matching the given patterns.
202  pub strace_ops: Option<Vec<String>>,
203
204  /// Allows to map error type to a string "class" used to represent
205  /// error in JavaScript.
206  pub get_error_class_fn: Option<GetErrorClassFn>,
207  pub cache_storage_dir: Option<std::path::PathBuf>,
208  pub origin_storage_dir: Option<std::path::PathBuf>,
209  pub stdio: Stdio,
210  pub enable_stack_trace_arg_in_ops: bool,
211}
212
213impl Default for WorkerOptions {
214  fn default() -> Self {
215    Self {
216      create_web_worker_cb: Arc::new(|_| {
217        unimplemented!("web workers are not supported")
218      }),
219      skip_op_registration: false,
220      seed: None,
221      unsafely_ignore_certificate_errors: Default::default(),
222      should_break_on_first_statement: Default::default(),
223      should_wait_for_inspector_session: Default::default(),
224      strace_ops: Default::default(),
225      maybe_inspector_server: Default::default(),
226      format_js_error_fn: Default::default(),
227      get_error_class_fn: Default::default(),
228      origin_storage_dir: Default::default(),
229      cache_storage_dir: Default::default(),
230      extensions: Default::default(),
231      startup_snapshot: Default::default(),
232      create_params: Default::default(),
233      bootstrap: Default::default(),
234      stdio: Default::default(),
235      enable_stack_trace_arg_in_ops: false,
236    }
237  }
238}
239
240pub fn create_op_metrics(
241  enable_op_summary_metrics: bool,
242  strace_ops: Option<Vec<String>>,
243) -> (
244  Option<Rc<OpMetricsSummaryTracker>>,
245  Option<OpMetricsFactoryFn>,
246) {
247  let mut op_summary_metrics = None;
248  let mut op_metrics_factory_fn: Option<OpMetricsFactoryFn> = None;
249  let now = Instant::now();
250  let max_len: Rc<std::cell::Cell<usize>> = Default::default();
251  if let Some(patterns) = strace_ops {
252    /// Match an op name against a list of patterns
253    fn matches_pattern(patterns: &[String], name: &str) -> bool {
254      let mut found_match = false;
255      let mut found_nomatch = false;
256      for pattern in patterns.iter() {
257        if let Some(pattern) = pattern.strip_prefix('-') {
258          if name.contains(pattern) {
259            return false;
260          }
261        } else if name.contains(pattern.as_str()) {
262          found_match = true;
263        } else {
264          found_nomatch = true;
265        }
266      }
267
268      found_match || !found_nomatch
269    }
270
271    op_metrics_factory_fn = Some(Box::new(move |_, _, decl| {
272      // If we don't match a requested pattern, or we match a negative pattern, bail
273      if !matches_pattern(&patterns, decl.name) {
274        return None;
275      }
276
277      max_len.set(max_len.get().max(decl.name.len()));
278      let max_len = max_len.clone();
279      Some(Rc::new(
280        #[allow(clippy::print_stderr)]
281        move |op: &deno_core::_ops::OpCtx, event, source| {
282          eprintln!(
283            "[{: >10.3}] {name:max_len$}: {event:?} {source:?}",
284            now.elapsed().as_secs_f64(),
285            name = op.decl().name,
286            max_len = max_len.get()
287          );
288        },
289      ))
290    }));
291  }
292
293  if enable_op_summary_metrics {
294    let summary = Rc::new(OpMetricsSummaryTracker::default());
295    let summary_metrics = summary.clone().op_metrics_factory_fn(|_| true);
296    op_metrics_factory_fn = Some(match op_metrics_factory_fn {
297      Some(f) => merge_op_metrics(f, summary_metrics),
298      None => summary_metrics,
299    });
300    op_summary_metrics = Some(summary);
301  }
302
303  (op_summary_metrics, op_metrics_factory_fn)
304}
305
306impl MainWorker {
307  pub fn bootstrap_from_options(
308    main_module: ModuleSpecifier,
309    services: WorkerServiceOptions,
310    options: WorkerOptions,
311  ) -> Self {
312    let (mut worker, bootstrap_options) =
313      Self::from_options(main_module, services, options);
314    worker.bootstrap(bootstrap_options);
315    worker
316  }
317
318  fn from_options(
319    main_module: ModuleSpecifier,
320    services: WorkerServiceOptions,
321    mut options: WorkerOptions,
322  ) -> (Self, BootstrapOptions) {
323    deno_core::extension!(deno_permissions_worker,
324      options = {
325        permissions: PermissionsContainer,
326        enable_testing_features: bool,
327      },
328      state = |state, options| {
329        state.put::<PermissionsContainer>(options.permissions);
330        state.put(ops::TestingFeaturesEnabled(options.enable_testing_features));
331      },
332    );
333
334    // Get our op metrics
335    let (op_summary_metrics, op_metrics_factory_fn) = create_op_metrics(
336      options.bootstrap.enable_op_summary_metrics,
337      options.strace_ops,
338    );
339
340    // Permissions: many ops depend on this
341    let enable_testing_features = options.bootstrap.enable_testing_features;
342    let exit_code = ExitCode(Arc::new(AtomicI32::new(0)));
343    let create_cache = options.cache_storage_dir.map(|storage_dir| {
344      let create_cache_fn = move || SqliteBackedCache::new(storage_dir.clone());
345      CreateCache(Arc::new(create_cache_fn))
346    });
347
348    // NOTE(bartlomieju): ordering is important here, keep it in sync with
349    // `runtime/web_worker.rs` and `runtime/snapshot.rs`!
350    let mut extensions = vec![
351      deno_telemetry::deno_telemetry::init_ops_and_esm(),
352      // Web APIs
353      deno_webidl::deno_webidl::init_ops_and_esm(),
354      deno_console::deno_console::init_ops_and_esm(),
355      deno_url::deno_url::init_ops_and_esm(),
356      deno_web::deno_web::init_ops_and_esm::<PermissionsContainer>(
357        services.blob_store.clone(),
358        options.bootstrap.location.clone(),
359      ),
360      deno_webgpu::deno_webgpu::init_ops_and_esm(),
361      deno_canvas::deno_canvas::init_ops_and_esm(),
362      deno_fetch::deno_fetch::init_ops_and_esm::<PermissionsContainer>(
363        deno_fetch::Options {
364          user_agent: options.bootstrap.user_agent.clone(),
365          root_cert_store_provider: services.root_cert_store_provider.clone(),
366          unsafely_ignore_certificate_errors: options
367            .unsafely_ignore_certificate_errors
368            .clone(),
369          file_fetch_handler: Rc::new(deno_fetch::FsFetchHandler),
370          resolver: services.fetch_dns_resolver,
371          ..Default::default()
372        },
373      ),
374      deno_cache::deno_cache::init_ops_and_esm::<SqliteBackedCache>(
375        create_cache,
376      ),
377      deno_websocket::deno_websocket::init_ops_and_esm::<PermissionsContainer>(
378        options.bootstrap.user_agent.clone(),
379        services.root_cert_store_provider.clone(),
380        options.unsafely_ignore_certificate_errors.clone(),
381      ),
382      deno_webstorage::deno_webstorage::init_ops_and_esm(
383        options.origin_storage_dir.clone(),
384      ),
385      deno_crypto::deno_crypto::init_ops_and_esm(options.seed),
386      deno_broadcast_channel::deno_broadcast_channel::init_ops_and_esm(
387        services.broadcast_channel.clone(),
388      ),
389      deno_ffi::deno_ffi::init_ops_and_esm::<PermissionsContainer>(),
390      deno_net::deno_net::init_ops_and_esm::<PermissionsContainer>(
391        services.root_cert_store_provider.clone(),
392        options.unsafely_ignore_certificate_errors.clone(),
393      ),
394      deno_tls::deno_tls::init_ops_and_esm(),
395      deno_kv::deno_kv::init_ops_and_esm(
396        MultiBackendDbHandler::remote_or_sqlite::<PermissionsContainer>(
397          options.origin_storage_dir.clone(),
398          options.seed,
399          deno_kv::remote::HttpOptions {
400            user_agent: options.bootstrap.user_agent.clone(),
401            root_cert_store_provider: services.root_cert_store_provider.clone(),
402            unsafely_ignore_certificate_errors: options
403              .unsafely_ignore_certificate_errors
404              .clone(),
405            client_cert_chain_and_key: TlsKeys::Null,
406            proxy: None,
407          },
408        ),
409        deno_kv::KvConfig::builder().build(),
410      ),
411      deno_cron::deno_cron::init_ops_and_esm(LocalCronHandler::new()),
412      deno_napi::deno_napi::init_ops_and_esm::<PermissionsContainer>(),
413      deno_http::deno_http::init_ops_and_esm::<DefaultHttpPropertyExtractor>(
414        deno_http::Options::default(),
415      ),
416      deno_io::deno_io::init_ops_and_esm(Some(options.stdio)),
417      deno_fs::deno_fs::init_ops_and_esm::<PermissionsContainer>(
418        services.fs.clone(),
419      ),
420      deno_node::deno_node::init_ops_and_esm::<PermissionsContainer>(
421        services.node_services,
422        services.fs,
423      ),
424      // Ops from this crate
425      ops::runtime::deno_runtime::init_ops_and_esm(main_module.clone()),
426      ops::worker_host::deno_worker_host::init_ops_and_esm(
427        options.create_web_worker_cb.clone(),
428        options.format_js_error_fn.clone(),
429      ),
430      ops::fs_events::deno_fs_events::init_ops_and_esm(),
431      ops::os::deno_os::init_ops_and_esm(exit_code.clone()),
432      ops::permissions::deno_permissions::init_ops_and_esm(),
433      ops::process::deno_process::init_ops_and_esm(
434        services.npm_process_state_provider,
435      ),
436      ops::signal::deno_signal::init_ops_and_esm(),
437      ops::tty::deno_tty::init_ops_and_esm(),
438      ops::http::deno_http_runtime::init_ops_and_esm(),
439      ops::bootstrap::deno_bootstrap::init_ops_and_esm(
440        if options.startup_snapshot.is_some() {
441          None
442        } else {
443          Some(Default::default())
444        },
445      ),
446      deno_permissions_worker::init_ops_and_esm(
447        services.permissions,
448        enable_testing_features,
449      ),
450      runtime::init_ops_and_esm(),
451      // NOTE(bartlomieju): this is done, just so that ops from this extension
452      // are available and importing them in `99_main.js` doesn't cause an
453      // error because they're not defined. Trying to use these ops in non-worker
454      // context will cause a panic.
455      ops::web_worker::deno_web_worker::init_ops_and_esm().disable(),
456    ];
457
458    #[cfg(feature = "hmr")]
459    assert!(
460      cfg!(not(feature = "only_snapshotted_js_sources")),
461      "'hmr' is incompatible with 'only_snapshotted_js_sources'."
462    );
463
464    for extension in &mut extensions {
465      if options.startup_snapshot.is_some() {
466        extension.js_files = std::borrow::Cow::Borrowed(&[]);
467        extension.esm_files = std::borrow::Cow::Borrowed(&[]);
468        extension.esm_entry_point = None;
469      }
470    }
471
472    extensions.extend(std::mem::take(&mut options.extensions));
473
474    #[cfg(feature = "only_snapshotted_js_sources")]
475    options.startup_snapshot.as_ref().expect("A user snapshot was not provided, even though 'only_snapshotted_js_sources' is used.");
476
477    let has_notified_of_inspector_disconnect = AtomicBool::new(false);
478    let wait_for_inspector_disconnect_callback = Box::new(move || {
479      if !has_notified_of_inspector_disconnect
480        .swap(true, std::sync::atomic::Ordering::SeqCst)
481      {
482        log::info!("Program finished. Waiting for inspector to disconnect to exit the process...");
483      }
484    });
485
486    let mut js_runtime = JsRuntime::new(RuntimeOptions {
487      module_loader: Some(services.module_loader.clone()),
488      startup_snapshot: options.startup_snapshot,
489      create_params: options.create_params,
490      skip_op_registration: options.skip_op_registration,
491      get_error_class_fn: options.get_error_class_fn,
492      shared_array_buffer_store: services.shared_array_buffer_store.clone(),
493      compiled_wasm_module_store: services.compiled_wasm_module_store.clone(),
494      extensions,
495      extension_transpiler: Some(Rc::new(|specifier, source| {
496        maybe_transpile_source(specifier, source)
497      })),
498      inspector: true,
499      is_main: true,
500      feature_checker: Some(services.feature_checker.clone()),
501      op_metrics_factory_fn,
502      wait_for_inspector_disconnect_callback: Some(
503        wait_for_inspector_disconnect_callback,
504      ),
505      import_meta_resolve_callback: Some(Box::new(
506        import_meta_resolve_callback,
507      )),
508      validate_import_attributes_cb: Some(Box::new(
509        validate_import_attributes_callback,
510      )),
511      import_assertions_support: deno_core::ImportAssertionsSupport::Error,
512      eval_context_code_cache_cbs: services.v8_code_cache.map(|cache| {
513        let cache_clone = cache.clone();
514        (
515          Box::new(move |specifier: &ModuleSpecifier, code: &v8::String| {
516            let source_hash = {
517              use std::hash::Hash;
518              use std::hash::Hasher;
519              let mut hasher = twox_hash::XxHash64::default();
520              code.hash(&mut hasher);
521              hasher.finish()
522            };
523            let data = cache
524              .get_sync(specifier, CodeCacheType::Script, source_hash)
525              .inspect(|_| {
526                // This log line is also used by tests.
527                log::debug!("V8 code cache hit for script: {specifier}, [{source_hash}]");
528              })
529              .map(Cow::Owned);
530            Ok(SourceCodeCacheInfo {
531              data,
532              hash: source_hash,
533            })
534          }) as Box<dyn Fn(&_, &_) -> _>,
535          Box::new(
536            move |specifier: ModuleSpecifier, source_hash: u64, data: &[u8]| {
537              // This log line is also used by tests.
538              log::debug!("Updating V8 code cache for script: {specifier}, [{source_hash}]");
539              cache_clone.set_sync(
540                specifier,
541                CodeCacheType::Script,
542                source_hash,
543                data,
544              );
545            },
546          ) as Box<dyn Fn(_, _, &_)>,
547        )
548      }),
549      maybe_op_stack_trace_callback: if options.enable_stack_trace_arg_in_ops {
550        Some(Box::new(|stack| {
551          deno_permissions::prompter::set_current_stacktrace(stack)
552        }))
553      } else { None },
554      ..Default::default()
555    });
556
557    if let Some(op_summary_metrics) = op_summary_metrics {
558      js_runtime.op_state().borrow_mut().put(op_summary_metrics);
559    }
560
561    // Put inspector handle into the op state so we can put a breakpoint when
562    // executing a CJS entrypoint.
563    let op_state = js_runtime.op_state();
564    let inspector = js_runtime.inspector();
565    op_state.borrow_mut().put(inspector);
566
567    if let Some(server) = options.maybe_inspector_server.clone() {
568      server.register_inspector(
569        main_module.to_string(),
570        &mut js_runtime,
571        options.should_break_on_first_statement
572          || options.should_wait_for_inspector_session,
573      );
574    }
575
576    let (
577      bootstrap_fn_global,
578      dispatch_load_event_fn_global,
579      dispatch_beforeunload_event_fn_global,
580      dispatch_unload_event_fn_global,
581      dispatch_process_beforeexit_event_fn_global,
582      dispatch_process_exit_event_fn_global,
583    ) = {
584      let context = js_runtime.main_context();
585      let scope = &mut js_runtime.handle_scope();
586      let context_local = v8::Local::new(scope, context);
587      let global_obj = context_local.global(scope);
588      let bootstrap_str =
589        v8::String::new_external_onebyte_static(scope, b"bootstrap").unwrap();
590      let bootstrap_ns: v8::Local<v8::Object> = global_obj
591        .get(scope, bootstrap_str.into())
592        .unwrap()
593        .try_into()
594        .unwrap();
595      let main_runtime_str =
596        v8::String::new_external_onebyte_static(scope, b"mainRuntime").unwrap();
597      let bootstrap_fn =
598        bootstrap_ns.get(scope, main_runtime_str.into()).unwrap();
599      let bootstrap_fn =
600        v8::Local::<v8::Function>::try_from(bootstrap_fn).unwrap();
601      let dispatch_load_event_fn_str =
602        v8::String::new_external_onebyte_static(scope, b"dispatchLoadEvent")
603          .unwrap();
604      let dispatch_load_event_fn = bootstrap_ns
605        .get(scope, dispatch_load_event_fn_str.into())
606        .unwrap();
607      let dispatch_load_event_fn =
608        v8::Local::<v8::Function>::try_from(dispatch_load_event_fn).unwrap();
609      let dispatch_beforeunload_event_fn_str =
610        v8::String::new_external_onebyte_static(
611          scope,
612          b"dispatchBeforeUnloadEvent",
613        )
614        .unwrap();
615      let dispatch_beforeunload_event_fn = bootstrap_ns
616        .get(scope, dispatch_beforeunload_event_fn_str.into())
617        .unwrap();
618      let dispatch_beforeunload_event_fn =
619        v8::Local::<v8::Function>::try_from(dispatch_beforeunload_event_fn)
620          .unwrap();
621      let dispatch_unload_event_fn_str =
622        v8::String::new_external_onebyte_static(scope, b"dispatchUnloadEvent")
623          .unwrap();
624      let dispatch_unload_event_fn = bootstrap_ns
625        .get(scope, dispatch_unload_event_fn_str.into())
626        .unwrap();
627      let dispatch_unload_event_fn =
628        v8::Local::<v8::Function>::try_from(dispatch_unload_event_fn).unwrap();
629      let dispatch_process_beforeexit_event =
630        v8::String::new_external_onebyte_static(
631          scope,
632          b"dispatchProcessBeforeExitEvent",
633        )
634        .unwrap();
635      let dispatch_process_beforeexit_event_fn = bootstrap_ns
636        .get(scope, dispatch_process_beforeexit_event.into())
637        .unwrap();
638      let dispatch_process_beforeexit_event_fn =
639        v8::Local::<v8::Function>::try_from(
640          dispatch_process_beforeexit_event_fn,
641        )
642        .unwrap();
643      let dispatch_process_exit_event =
644        v8::String::new_external_onebyte_static(
645          scope,
646          b"dispatchProcessExitEvent",
647        )
648        .unwrap();
649      let dispatch_process_exit_event_fn = bootstrap_ns
650        .get(scope, dispatch_process_exit_event.into())
651        .unwrap();
652      let dispatch_process_exit_event_fn =
653        v8::Local::<v8::Function>::try_from(dispatch_process_exit_event_fn)
654          .unwrap();
655      (
656        v8::Global::new(scope, bootstrap_fn),
657        v8::Global::new(scope, dispatch_load_event_fn),
658        v8::Global::new(scope, dispatch_beforeunload_event_fn),
659        v8::Global::new(scope, dispatch_unload_event_fn),
660        v8::Global::new(scope, dispatch_process_beforeexit_event_fn),
661        v8::Global::new(scope, dispatch_process_exit_event_fn),
662      )
663    };
664
665    let worker = Self {
666      js_runtime,
667      should_break_on_first_statement: options.should_break_on_first_statement,
668      should_wait_for_inspector_session: options
669        .should_wait_for_inspector_session,
670      exit_code,
671      bootstrap_fn_global: Some(bootstrap_fn_global),
672      dispatch_load_event_fn_global,
673      dispatch_beforeunload_event_fn_global,
674      dispatch_unload_event_fn_global,
675      dispatch_process_beforeexit_event_fn_global,
676      dispatch_process_exit_event_fn_global,
677    };
678    (worker, options.bootstrap)
679  }
680
681  pub fn bootstrap(&mut self, options: BootstrapOptions) {
682    // Setup bootstrap options for ops.
683    {
684      let op_state = self.js_runtime.op_state();
685      let mut state = op_state.borrow_mut();
686      state.put(options.clone());
687      if let Some(node_ipc_fd) = options.node_ipc_fd {
688        state.put(deno_node::ChildPipeFd(node_ipc_fd));
689      }
690    }
691
692    let scope = &mut self.js_runtime.handle_scope();
693    let scope = &mut v8::TryCatch::new(scope);
694    let args = options.as_v8(scope);
695    let bootstrap_fn = self.bootstrap_fn_global.take().unwrap();
696    let bootstrap_fn = v8::Local::new(scope, bootstrap_fn);
697    let undefined = v8::undefined(scope);
698    bootstrap_fn.call(scope, undefined.into(), &[args]);
699    if let Some(exception) = scope.exception() {
700      let error = JsError::from_v8_exception(scope, exception);
701      panic!("Bootstrap exception: {error}");
702    }
703  }
704
705  /// See [JsRuntime::execute_script](deno_core::JsRuntime::execute_script)
706  pub fn execute_script(
707    &mut self,
708    script_name: &'static str,
709    source_code: ModuleCodeString,
710  ) -> Result<v8::Global<v8::Value>, AnyError> {
711    self.js_runtime.execute_script(script_name, source_code)
712  }
713
714  /// Loads and instantiates specified JavaScript module as "main" module.
715  pub async fn preload_main_module(
716    &mut self,
717    module_specifier: &ModuleSpecifier,
718  ) -> Result<ModuleId, AnyError> {
719    self.js_runtime.load_main_es_module(module_specifier).await
720  }
721
722  /// Loads and instantiates specified JavaScript module as "side" module.
723  pub async fn preload_side_module(
724    &mut self,
725    module_specifier: &ModuleSpecifier,
726  ) -> Result<ModuleId, AnyError> {
727    self.js_runtime.load_side_es_module(module_specifier).await
728  }
729
730  /// Executes specified JavaScript module.
731  pub async fn evaluate_module(
732    &mut self,
733    id: ModuleId,
734  ) -> Result<(), AnyError> {
735    self.wait_for_inspector_session();
736    let mut receiver = self.js_runtime.mod_evaluate(id);
737    tokio::select! {
738      // Not using biased mode leads to non-determinism for relatively simple
739      // programs.
740      biased;
741
742      maybe_result = &mut receiver => {
743        debug!("received module evaluate {:#?}", maybe_result);
744        maybe_result
745      }
746
747      event_loop_result = self.run_event_loop(false) => {
748        event_loop_result?;
749        receiver.await
750      }
751    }
752  }
753
754  /// Run the event loop up to a given duration. If the runtime resolves early, returns
755  /// early. Will always poll the runtime at least once.
756  pub async fn run_up_to_duration(
757    &mut self,
758    duration: Duration,
759  ) -> Result<(), AnyError> {
760    match tokio::time::timeout(
761      duration,
762      self
763        .js_runtime
764        .run_event_loop(PollEventLoopOptions::default()),
765    )
766    .await
767    {
768      Ok(Ok(_)) => Ok(()),
769      Err(_) => Ok(()),
770      Ok(Err(e)) => Err(e),
771    }
772  }
773
774  /// Loads, instantiates and executes specified JavaScript module.
775  pub async fn execute_side_module(
776    &mut self,
777    module_specifier: &ModuleSpecifier,
778  ) -> Result<(), AnyError> {
779    let id = self.preload_side_module(module_specifier).await?;
780    self.evaluate_module(id).await
781  }
782
783  /// Loads, instantiates and executes specified JavaScript module.
784  ///
785  /// This module will have "import.meta.main" equal to true.
786  pub async fn execute_main_module(
787    &mut self,
788    module_specifier: &ModuleSpecifier,
789  ) -> Result<(), AnyError> {
790    let id = self.preload_main_module(module_specifier).await?;
791    self.evaluate_module(id).await
792  }
793
794  fn wait_for_inspector_session(&mut self) {
795    if self.should_break_on_first_statement {
796      self
797        .js_runtime
798        .inspector()
799        .borrow_mut()
800        .wait_for_session_and_break_on_next_statement();
801    } else if self.should_wait_for_inspector_session {
802      self.js_runtime.inspector().borrow_mut().wait_for_session();
803    }
804  }
805
806  /// Create new inspector session. This function panics if Worker
807  /// was not configured to create inspector.
808  pub fn create_inspector_session(&mut self) -> LocalInspectorSession {
809    self.js_runtime.maybe_init_inspector();
810    self.js_runtime.inspector().borrow().create_local_session(
811      InspectorSessionOptions {
812        kind: InspectorSessionKind::Blocking,
813      },
814    )
815  }
816
817  pub async fn run_event_loop(
818    &mut self,
819    wait_for_inspector: bool,
820  ) -> Result<(), AnyError> {
821    self
822      .js_runtime
823      .run_event_loop(deno_core::PollEventLoopOptions {
824        wait_for_inspector,
825        ..Default::default()
826      })
827      .await
828  }
829
830  /// Return exit code set by the executed code (either in main worker
831  /// or one of child web workers).
832  pub fn exit_code(&self) -> i32 {
833    self.exit_code.get()
834  }
835
836  /// Dispatches "load" event to the JavaScript runtime.
837  ///
838  /// Does not poll event loop, and thus not await any of the "load" event handlers.
839  pub fn dispatch_load_event(&mut self) -> Result<(), AnyError> {
840    let scope = &mut self.js_runtime.handle_scope();
841    let tc_scope = &mut v8::TryCatch::new(scope);
842    let dispatch_load_event_fn =
843      v8::Local::new(tc_scope, &self.dispatch_load_event_fn_global);
844    let undefined = v8::undefined(tc_scope);
845    dispatch_load_event_fn.call(tc_scope, undefined.into(), &[]);
846    if let Some(exception) = tc_scope.exception() {
847      let error = JsError::from_v8_exception(tc_scope, exception);
848      return Err(error.into());
849    }
850    Ok(())
851  }
852
853  /// Dispatches "unload" event to the JavaScript runtime.
854  ///
855  /// Does not poll event loop, and thus not await any of the "unload" event handlers.
856  pub fn dispatch_unload_event(&mut self) -> Result<(), AnyError> {
857    let scope = &mut self.js_runtime.handle_scope();
858    let tc_scope = &mut v8::TryCatch::new(scope);
859    let dispatch_unload_event_fn =
860      v8::Local::new(tc_scope, &self.dispatch_unload_event_fn_global);
861    let undefined = v8::undefined(tc_scope);
862    dispatch_unload_event_fn.call(tc_scope, undefined.into(), &[]);
863    if let Some(exception) = tc_scope.exception() {
864      let error = JsError::from_v8_exception(tc_scope, exception);
865      return Err(error.into());
866    }
867    Ok(())
868  }
869
870  /// Dispatches process.emit("exit") event for node compat.
871  pub fn dispatch_process_exit_event(&mut self) -> Result<(), AnyError> {
872    let scope = &mut self.js_runtime.handle_scope();
873    let tc_scope = &mut v8::TryCatch::new(scope);
874    let dispatch_process_exit_event_fn =
875      v8::Local::new(tc_scope, &self.dispatch_process_exit_event_fn_global);
876    let undefined = v8::undefined(tc_scope);
877    dispatch_process_exit_event_fn.call(tc_scope, undefined.into(), &[]);
878    if let Some(exception) = tc_scope.exception() {
879      let error = JsError::from_v8_exception(tc_scope, exception);
880      return Err(error.into());
881    }
882    Ok(())
883  }
884
885  /// Dispatches "beforeunload" event to the JavaScript runtime. Returns a boolean
886  /// indicating if the event was prevented and thus event loop should continue
887  /// running.
888  pub fn dispatch_beforeunload_event(&mut self) -> Result<bool, AnyError> {
889    let scope = &mut self.js_runtime.handle_scope();
890    let tc_scope = &mut v8::TryCatch::new(scope);
891    let dispatch_beforeunload_event_fn =
892      v8::Local::new(tc_scope, &self.dispatch_beforeunload_event_fn_global);
893    let undefined = v8::undefined(tc_scope);
894    let ret_val =
895      dispatch_beforeunload_event_fn.call(tc_scope, undefined.into(), &[]);
896    if let Some(exception) = tc_scope.exception() {
897      let error = JsError::from_v8_exception(tc_scope, exception);
898      return Err(error.into());
899    }
900    let ret_val = ret_val.unwrap();
901    Ok(ret_val.is_false())
902  }
903
904  /// Dispatches process.emit("beforeExit") event for node compat.
905  pub fn dispatch_process_beforeexit_event(
906    &mut self,
907  ) -> Result<bool, AnyError> {
908    let scope = &mut self.js_runtime.handle_scope();
909    let tc_scope = &mut v8::TryCatch::new(scope);
910    let dispatch_process_beforeexit_event_fn = v8::Local::new(
911      tc_scope,
912      &self.dispatch_process_beforeexit_event_fn_global,
913    );
914    let undefined = v8::undefined(tc_scope);
915    let ret_val = dispatch_process_beforeexit_event_fn.call(
916      tc_scope,
917      undefined.into(),
918      &[],
919    );
920    if let Some(exception) = tc_scope.exception() {
921      let error = JsError::from_v8_exception(tc_scope, exception);
922      return Err(error.into());
923    }
924    let ret_val = ret_val.unwrap();
925    Ok(ret_val.is_true())
926  }
927}