1use 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
72pub 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
111pub 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 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 pub shared_array_buffer_store: Option<SharedArrayBufferStore>,
154
155 pub compiled_wasm_module_store: Option<CompiledWasmModuleStore>,
162
163 pub v8_code_cache: Option<Arc<dyn CodeCache>>,
165}
166
167pub struct WorkerOptions {
168 pub bootstrap: BootstrapOptions,
169
170 pub extensions: Vec<Extension>,
176
177 pub startup_snapshot: Option<&'static [u8]>,
179
180 pub skip_op_registration: bool,
182
183 pub create_params: Option<v8::CreateParams>,
185
186 pub unsafely_ignore_certificate_errors: Option<Vec<String>>,
187 pub seed: Option<u64>,
188
189 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 pub should_break_on_first_statement: bool,
198 pub should_wait_for_inspector_session: bool,
201 pub strace_ops: Option<Vec<String>>,
203
204 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 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 !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 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 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 let mut extensions = vec![
351 deno_telemetry::deno_telemetry::init_ops_and_esm(),
352 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::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 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 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 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 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 {
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 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 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 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 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 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 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 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 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 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 pub fn exit_code(&self) -> i32 {
833 self.exit_code.get()
834 }
835
836 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 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 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 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 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}