1use std::borrow::Cow;
3use std::collections::HashMap;
4use std::rc::Rc;
5use std::sync::Arc;
6#[cfg(target_os = "linux")]
7use std::sync::LazyLock;
8use std::sync::atomic::AtomicBool;
9use std::sync::atomic::Ordering;
10use std::time::Duration;
11use std::time::Instant;
12
13use deno_cache::CacheImpl;
14use deno_cache::CreateCache;
15use deno_cache::SqliteBackedCache;
16use deno_core::CompiledWasmModuleStore;
17use deno_core::Extension;
18use deno_core::InspectorSessionKind;
19use deno_core::JsRuntime;
20use deno_core::JsRuntimeInspector;
21use deno_core::LocalInspectorSession;
22use deno_core::ModuleCodeString;
23use deno_core::ModuleId;
24use deno_core::ModuleLoadOptions;
25use deno_core::ModuleLoadReferrer;
26use deno_core::ModuleLoader;
27use deno_core::ModuleSpecifier;
28use deno_core::OpMetricsFactoryFn;
29use deno_core::OpMetricsSummaryTracker;
30use deno_core::PollEventLoopOptions;
31use deno_core::RuntimeOptions;
32use deno_core::SharedArrayBufferStore;
33use deno_core::SourceCodeCacheInfo;
34use deno_core::error::CoreError;
35use deno_core::error::JsError;
36use deno_core::merge_op_metrics;
37use deno_core::v8;
38use deno_cron::local::LocalCronHandler;
39use deno_fs::FileSystem;
40use deno_io::Stdio;
41use deno_kv::dynamic::MultiBackendDbHandler;
42use deno_napi::DenoRtNativeAddonLoaderRc;
43use deno_node::ExtNodeSys;
44use deno_node::NodeExtInitServices;
45use deno_os::ExitCode;
46use deno_permissions::PermissionsContainer;
47use deno_process::NpmProcessStateProviderRc;
48use deno_tls::RootCertStoreProvider;
49use deno_tls::TlsKeys;
50use deno_web::BlobStore;
51use deno_web::InMemoryBroadcastChannel;
52use log::debug;
53use node_resolver::InNpmPackageChecker;
54use node_resolver::NpmPackageFolderResolver;
55
56use crate::BootstrapOptions;
57use crate::FeatureChecker;
58use crate::code_cache::CodeCache;
59use crate::code_cache::CodeCacheType;
60use crate::inspector_server::InspectorServer;
61use crate::ops;
62use crate::shared::runtime;
63
64pub type FormatJsErrorFn = dyn Fn(&JsError) -> String + Sync + Send;
65
66#[cfg(target_os = "linux")]
67pub(crate) static MEMORY_TRIM_HANDLER_ENABLED: LazyLock<bool> =
68 LazyLock::new(|| std::env::var_os("DENO_USR2_MEMORY_TRIM").is_some());
69
70#[cfg(target_os = "linux")]
71pub(crate) static SIGUSR2_RX: LazyLock<tokio::sync::watch::Receiver<()>> =
72 LazyLock::new(|| {
73 let (tx, rx) = tokio::sync::watch::channel(());
74
75 tokio::spawn(async move {
76 let mut sigusr2 = deno_signals::signal_stream(libc::SIGUSR2).unwrap();
77
78 loop {
79 sigusr2.recv().await;
80
81 unsafe {
83 libc::malloc_trim(0);
84 }
85
86 if tx.send(()).is_err() {
87 break;
88 }
89 }
90 });
91
92 rx
93 });
94
95pub fn create_validate_import_attributes_callback(
98 enable_raw_imports: Arc<AtomicBool>,
99) -> deno_core::ValidateImportAttributesCb {
100 Box::new(
101 move |scope: &mut v8::PinScope<'_, '_>,
102 attributes: &HashMap<String, String>| {
103 let valid_attribute = |kind: &str| {
104 enable_raw_imports.load(Ordering::Relaxed)
105 && matches!(kind, "bytes" | "text")
106 || matches!(kind, "json")
107 };
108 for (key, value) in attributes {
109 let msg = if key != "type" {
110 Some(format!("\"{key}\" attribute is not supported."))
111 } else if !valid_attribute(value.as_str()) {
112 Some(format!("\"{value}\" is not a valid module type."))
113 } else {
114 None
115 };
116
117 let Some(msg) = msg else {
118 continue;
119 };
120
121 let message = v8::String::new(scope, &msg).unwrap();
122 let exception = v8::Exception::type_error(scope, message);
123 scope.throw_exception(exception);
124 return;
125 }
126 },
127 )
128}
129
130pub fn make_wait_for_inspector_disconnect_callback() -> Box<dyn Fn()> {
131 let has_notified_of_inspector_disconnect = AtomicBool::new(false);
132 Box::new(move || {
133 if !has_notified_of_inspector_disconnect
134 .swap(true, std::sync::atomic::Ordering::SeqCst)
135 {
136 log::info!(
137 "Program finished. Waiting for inspector to disconnect to exit the process..."
138 );
139 }
140 })
141}
142
143pub struct MainWorker {
151 pub js_runtime: JsRuntime,
152 should_break_on_first_statement: bool,
153 should_wait_for_inspector_session: bool,
154 exit_code: ExitCode,
155 bootstrap_fn_global: Option<v8::Global<v8::Function>>,
156 dispatch_load_event_fn_global: v8::Global<v8::Function>,
157 dispatch_beforeunload_event_fn_global: v8::Global<v8::Function>,
158 dispatch_unload_event_fn_global: v8::Global<v8::Function>,
159 dispatch_process_beforeexit_event_fn_global: v8::Global<v8::Function>,
160 dispatch_process_exit_event_fn_global: v8::Global<v8::Function>,
161 memory_trim_handle: Option<tokio::task::JoinHandle<()>>,
162}
163
164impl Drop for MainWorker {
165 fn drop(&mut self) {
166 if let Some(memory_trim_handle) = self.memory_trim_handle.take() {
167 memory_trim_handle.abort();
168 }
169 }
170}
171
172pub struct WorkerServiceOptions<
173 TInNpmPackageChecker: InNpmPackageChecker,
174 TNpmPackageFolderResolver: NpmPackageFolderResolver,
175 TExtNodeSys: ExtNodeSys,
176> {
177 pub blob_store: Arc<BlobStore>,
178 pub broadcast_channel: InMemoryBroadcastChannel,
179 pub deno_rt_native_addon_loader: Option<DenoRtNativeAddonLoaderRc>,
180 pub feature_checker: Arc<FeatureChecker>,
181 pub fs: Arc<dyn FileSystem>,
182 pub module_loader: Rc<dyn ModuleLoader>,
188 pub node_services: Option<
189 NodeExtInitServices<
190 TInNpmPackageChecker,
191 TNpmPackageFolderResolver,
192 TExtNodeSys,
193 >,
194 >,
195 pub npm_process_state_provider: Option<NpmProcessStateProviderRc>,
196 pub permissions: PermissionsContainer,
197 pub root_cert_store_provider: Option<Arc<dyn RootCertStoreProvider>>,
198 pub fetch_dns_resolver: deno_fetch::dns::Resolver,
199
200 pub shared_array_buffer_store: Option<SharedArrayBufferStore>,
206
207 pub compiled_wasm_module_store: Option<CompiledWasmModuleStore>,
214
215 pub v8_code_cache: Option<Arc<dyn CodeCache>>,
217
218 pub bundle_provider: Option<Arc<dyn deno_bundle_runtime::BundleProvider>>,
219}
220
221pub struct WorkerOptions {
222 pub bootstrap: BootstrapOptions,
223
224 pub extensions: Vec<Extension>,
230
231 pub startup_snapshot: Option<&'static [u8]>,
233
234 pub skip_op_registration: bool,
236
237 pub create_params: Option<v8::CreateParams>,
239
240 pub unsafely_ignore_certificate_errors: Option<Vec<String>>,
241 pub seed: Option<u64>,
242
243 pub create_web_worker_cb: Arc<ops::worker_host::CreateWebWorkerCb>,
245 pub format_js_error_fn: Option<Arc<FormatJsErrorFn>>,
246
247 pub maybe_inspector_server: Option<Arc<InspectorServer>>,
248 pub should_break_on_first_statement: bool,
252 pub should_wait_for_inspector_session: bool,
255 pub trace_ops: Option<Vec<String>>,
257
258 pub cache_storage_dir: Option<std::path::PathBuf>,
259 pub origin_storage_dir: Option<std::path::PathBuf>,
260 pub stdio: Stdio,
261 pub enable_raw_imports: bool,
262 pub enable_stack_trace_arg_in_ops: bool,
263
264 pub unconfigured_runtime: Option<UnconfiguredRuntime>,
265}
266
267impl Default for WorkerOptions {
268 fn default() -> Self {
269 Self {
270 create_web_worker_cb: Arc::new(|_| {
271 unimplemented!("web workers are not supported")
272 }),
273 skip_op_registration: false,
274 seed: None,
275 unsafely_ignore_certificate_errors: Default::default(),
276 should_break_on_first_statement: Default::default(),
277 should_wait_for_inspector_session: Default::default(),
278 trace_ops: Default::default(),
279 maybe_inspector_server: Default::default(),
280 format_js_error_fn: Default::default(),
281 origin_storage_dir: Default::default(),
282 cache_storage_dir: Default::default(),
283 extensions: Default::default(),
284 startup_snapshot: Default::default(),
285 create_params: Default::default(),
286 bootstrap: Default::default(),
287 stdio: Default::default(),
288 enable_raw_imports: false,
289 enable_stack_trace_arg_in_ops: false,
290 unconfigured_runtime: None,
291 }
292 }
293}
294
295pub fn create_op_metrics(
296 enable_op_summary_metrics: bool,
297 trace_ops: Option<Vec<String>>,
298) -> (
299 Option<Rc<OpMetricsSummaryTracker>>,
300 Option<OpMetricsFactoryFn>,
301) {
302 let mut op_summary_metrics = None;
303 let mut op_metrics_factory_fn: Option<OpMetricsFactoryFn> = None;
304 let now = Instant::now();
305 let max_len: Rc<std::cell::Cell<usize>> = Default::default();
306 if let Some(patterns) = trace_ops {
307 fn matches_pattern(patterns: &[String], name: &str) -> bool {
309 let mut found_match = false;
310 let mut found_nomatch = false;
311 for pattern in patterns.iter() {
312 if let Some(pattern) = pattern.strip_prefix('-') {
313 if name.contains(pattern) {
314 return false;
315 }
316 } else if name.contains(pattern.as_str()) {
317 found_match = true;
318 } else {
319 found_nomatch = true;
320 }
321 }
322
323 found_match || !found_nomatch
324 }
325
326 op_metrics_factory_fn = Some(Box::new(move |_, _, decl| {
327 if !matches_pattern(&patterns, decl.name) {
329 return None;
330 }
331
332 max_len.set(max_len.get().max(decl.name.len()));
333 let max_len = max_len.clone();
334 Some(Rc::new(
335 #[allow(clippy::print_stderr)]
336 move |op: &deno_core::_ops::OpCtx, event, source| {
337 eprintln!(
338 "[{: >10.3}] {name:max_len$}: {event:?} {source:?}",
339 now.elapsed().as_secs_f64(),
340 name = op.decl().name,
341 max_len = max_len.get()
342 );
343 },
344 ))
345 }));
346 }
347
348 if enable_op_summary_metrics {
349 let summary = Rc::new(OpMetricsSummaryTracker::default());
350 let summary_metrics = summary.clone().op_metrics_factory_fn(|_| true);
351 op_metrics_factory_fn = Some(match op_metrics_factory_fn {
352 Some(f) => merge_op_metrics(f, summary_metrics),
353 None => summary_metrics,
354 });
355 op_summary_metrics = Some(summary);
356 }
357
358 (op_summary_metrics, op_metrics_factory_fn)
359}
360
361impl MainWorker {
362 pub fn bootstrap_from_options<
363 TInNpmPackageChecker: InNpmPackageChecker + 'static,
364 TNpmPackageFolderResolver: NpmPackageFolderResolver + 'static,
365 TExtNodeSys: ExtNodeSys + 'static,
366 >(
367 main_module: &ModuleSpecifier,
368 services: WorkerServiceOptions<
369 TInNpmPackageChecker,
370 TNpmPackageFolderResolver,
371 TExtNodeSys,
372 >,
373 options: WorkerOptions,
374 ) -> Self {
375 let (mut worker, bootstrap_options) =
376 Self::from_options(main_module, services, options);
377 worker.bootstrap(bootstrap_options);
378 worker
379 }
380
381 fn from_options<
382 TInNpmPackageChecker: InNpmPackageChecker + 'static,
383 TNpmPackageFolderResolver: NpmPackageFolderResolver + 'static,
384 TExtNodeSys: ExtNodeSys + 'static,
385 >(
386 main_module: &ModuleSpecifier,
387 services: WorkerServiceOptions<
388 TInNpmPackageChecker,
389 TNpmPackageFolderResolver,
390 TExtNodeSys,
391 >,
392 mut options: WorkerOptions,
393 ) -> (Self, BootstrapOptions) {
394 fn create_cache_inner(options: &WorkerOptions) -> Option<CreateCache> {
395 if let Ok(var) = std::env::var("DENO_CACHE_LSC_ENDPOINT") {
396 let elems: Vec<_> = var.split(",").collect();
397 if elems.len() == 2 {
398 let endpoint = elems[0];
399 let token = elems[1];
400 use deno_cache::CacheShard;
401
402 let shard =
403 Rc::new(CacheShard::new(endpoint.to_string(), token.to_string()));
404 let create_cache_fn = move || {
405 let x = deno_cache::LscBackend::default();
406 x.set_shard(shard.clone());
407
408 Ok(CacheImpl::Lsc(x))
409 };
410 #[allow(clippy::arc_with_non_send_sync)]
411 return Some(CreateCache(Arc::new(create_cache_fn)));
412 }
413 }
414
415 if let Some(storage_dir) = &options.cache_storage_dir {
416 let storage_dir = storage_dir.clone();
417 let create_cache_fn = move || {
418 let s = SqliteBackedCache::new(storage_dir.clone())?;
419 Ok(CacheImpl::Sqlite(s))
420 };
421 return Some(CreateCache(Arc::new(create_cache_fn)));
422 }
423
424 None
425 }
426 let create_cache = create_cache_inner(&options);
427
428 let (op_summary_metrics, op_metrics_factory_fn) = create_op_metrics(
430 options.bootstrap.enable_op_summary_metrics,
431 options.trace_ops,
432 );
433
434 let enable_testing_features = options.bootstrap.enable_testing_features;
436 let exit_code = ExitCode::default();
437
438 if options.unconfigured_runtime.is_some()
440 && (options.enable_stack_trace_arg_in_ops
441 || op_metrics_factory_fn.is_some())
442 {
443 options.unconfigured_runtime = None;
444 }
445
446 #[cfg(feature = "hmr")]
447 assert!(
448 cfg!(not(feature = "only_snapshotted_js_sources")),
449 "'hmr' is incompatible with 'only_snapshotted_js_sources'."
450 );
451
452 #[cfg(feature = "only_snapshotted_js_sources")]
453 options.startup_snapshot.as_ref().expect("A user snapshot was not provided, even though 'only_snapshotted_js_sources' is used.");
454
455 let mut js_runtime = if let Some(u) = options.unconfigured_runtime {
456 u.hydrate(services.module_loader)
457 } else {
458 let mut extensions = common_extensions::<
459 TInNpmPackageChecker,
460 TNpmPackageFolderResolver,
461 TExtNodeSys,
462 >(options.startup_snapshot.is_some(), false);
463
464 extensions.extend(std::mem::take(&mut options.extensions));
465
466 common_runtime(CommonRuntimeOptions {
467 module_loader: services.module_loader.clone(),
468 startup_snapshot: options.startup_snapshot,
469 create_params: options.create_params,
470 skip_op_registration: options.skip_op_registration,
471 shared_array_buffer_store: services.shared_array_buffer_store,
472 compiled_wasm_module_store: services.compiled_wasm_module_store,
473 extensions,
474 op_metrics_factory_fn,
475 enable_stack_trace_arg_in_ops: options.enable_stack_trace_arg_in_ops,
476 })
477 };
478
479 js_runtime
480 .set_eval_context_code_cache_cbs(services.v8_code_cache.map(|cache| {
481 let cache_clone = cache.clone();
482 (
483 Box::new(move |specifier: &ModuleSpecifier, code: &v8::String| {
484 let source_hash = {
485 use std::hash::Hash;
486 use std::hash::Hasher;
487 let mut hasher = twox_hash::XxHash64::default();
488 code.hash(&mut hasher);
489 hasher.finish()
490 };
491 let data = cache
492 .get_sync(specifier, CodeCacheType::Script, source_hash)
493 .inspect(|_| {
494 log::debug!(
496 "V8 code cache hit for script: {specifier}, [{source_hash}]"
497 );
498 })
499 .map(Cow::Owned);
500 Ok(SourceCodeCacheInfo {
501 data,
502 hash: source_hash,
503 })
504 }) as Box<dyn Fn(&_, &_) -> _>,
505 Box::new(
506 move |specifier: ModuleSpecifier, source_hash: u64, data: &[u8]| {
507 log::debug!(
509 "Updating V8 code cache for script: {specifier}, [{source_hash}]"
510 );
511 cache_clone.set_sync(
512 specifier,
513 CodeCacheType::Script,
514 source_hash,
515 data,
516 );
517 },
518 ) as Box<dyn Fn(_, _, &_)>,
519 )
520 }));
521
522 js_runtime
523 .op_state()
524 .borrow_mut()
525 .borrow::<EnableRawImports>()
526 .0
527 .store(options.enable_raw_imports, Ordering::Relaxed);
528
529 js_runtime
530 .lazy_init_extensions(vec![
531 deno_web::deno_web::args(
532 services.blob_store.clone(),
533 options.bootstrap.location.clone(),
534 services.broadcast_channel.clone(),
535 ),
536 deno_fetch::deno_fetch::args(deno_fetch::Options {
537 user_agent: options.bootstrap.user_agent.clone(),
538 root_cert_store_provider: services.root_cert_store_provider.clone(),
539 unsafely_ignore_certificate_errors: options
540 .unsafely_ignore_certificate_errors
541 .clone(),
542 file_fetch_handler: Rc::new(deno_fetch::FsFetchHandler),
543 resolver: services.fetch_dns_resolver,
544 ..Default::default()
545 }),
546 deno_cache::deno_cache::args(create_cache),
547 deno_websocket::deno_websocket::args(),
548 deno_webstorage::deno_webstorage::args(
549 options.origin_storage_dir.clone(),
550 ),
551 deno_crypto::deno_crypto::args(options.seed),
552 deno_ffi::deno_ffi::args(services.deno_rt_native_addon_loader.clone()),
553 deno_net::deno_net::args(
554 services.root_cert_store_provider.clone(),
555 options.unsafely_ignore_certificate_errors.clone(),
556 ),
557 deno_kv::deno_kv::args(
558 MultiBackendDbHandler::remote_or_sqlite(
559 options.origin_storage_dir.clone(),
560 options.seed,
561 deno_kv::remote::HttpOptions {
562 user_agent: options.bootstrap.user_agent.clone(),
563 root_cert_store_provider: services
564 .root_cert_store_provider
565 .clone(),
566 unsafely_ignore_certificate_errors: options
567 .unsafely_ignore_certificate_errors
568 .clone(),
569 client_cert_chain_and_key: TlsKeys::Null,
570 proxy: None,
571 },
572 ),
573 deno_kv::KvConfig::builder().build(),
574 ),
575 deno_napi::deno_napi::args(
576 services.deno_rt_native_addon_loader.clone(),
577 ),
578 deno_http::deno_http::args(deno_http::Options {
579 no_legacy_abort: options.bootstrap.no_legacy_abort,
580 ..Default::default()
581 }),
582 deno_io::deno_io::args(Some(options.stdio)),
583 deno_fs::deno_fs::args(services.fs.clone()),
584 deno_os::deno_os::args(Some(exit_code.clone())),
585 deno_process::deno_process::args(services.npm_process_state_provider),
586 deno_node::deno_node::args::<
587 TInNpmPackageChecker,
588 TNpmPackageFolderResolver,
589 TExtNodeSys,
590 >(services.node_services, services.fs.clone()),
591 ops::runtime::deno_runtime::args(main_module.clone()),
592 ops::worker_host::deno_worker_host::args(
593 options.create_web_worker_cb.clone(),
594 options.format_js_error_fn.clone(),
595 ),
596 deno_bundle_runtime::deno_bundle_runtime::args(
597 services.bundle_provider.clone(),
598 ),
599 ])
600 .unwrap();
601
602 if let Some(op_summary_metrics) = op_summary_metrics {
603 js_runtime.op_state().borrow_mut().put(op_summary_metrics);
604 }
605
606 {
607 let state = js_runtime.op_state();
608 let mut state = state.borrow_mut();
609
610 state.put(js_runtime.inspector());
613
614 state.put::<PermissionsContainer>(services.permissions);
615 state.put(ops::TestingFeaturesEnabled(enable_testing_features));
616 state.put(services.feature_checker);
617 }
618
619 if let Some(server) = options.maybe_inspector_server.clone() {
620 server.register_inspector(
621 main_module.to_string(),
622 js_runtime.inspector(),
623 options.should_break_on_first_statement
624 || options.should_wait_for_inspector_session,
625 );
626 }
627
628 let (
629 bootstrap_fn_global,
630 dispatch_load_event_fn_global,
631 dispatch_beforeunload_event_fn_global,
632 dispatch_unload_event_fn_global,
633 dispatch_process_beforeexit_event_fn_global,
634 dispatch_process_exit_event_fn_global,
635 ) = {
636 let context = js_runtime.main_context();
637 deno_core::scope!(scope, &mut js_runtime);
638 let context_local = v8::Local::new(scope, context);
639 let global_obj = context_local.global(scope);
640 let bootstrap_str =
641 v8::String::new_external_onebyte_static(scope, b"bootstrap").unwrap();
642 let bootstrap_ns: v8::Local<v8::Object> = global_obj
643 .get(scope, bootstrap_str.into())
644 .unwrap()
645 .try_into()
646 .unwrap();
647 let main_runtime_str =
648 v8::String::new_external_onebyte_static(scope, b"mainRuntime").unwrap();
649 let bootstrap_fn =
650 bootstrap_ns.get(scope, main_runtime_str.into()).unwrap();
651 let bootstrap_fn =
652 v8::Local::<v8::Function>::try_from(bootstrap_fn).unwrap();
653 let dispatch_load_event_fn_str =
654 v8::String::new_external_onebyte_static(scope, b"dispatchLoadEvent")
655 .unwrap();
656 let dispatch_load_event_fn = bootstrap_ns
657 .get(scope, dispatch_load_event_fn_str.into())
658 .unwrap();
659 let dispatch_load_event_fn =
660 v8::Local::<v8::Function>::try_from(dispatch_load_event_fn).unwrap();
661 let dispatch_beforeunload_event_fn_str =
662 v8::String::new_external_onebyte_static(
663 scope,
664 b"dispatchBeforeUnloadEvent",
665 )
666 .unwrap();
667 let dispatch_beforeunload_event_fn = bootstrap_ns
668 .get(scope, dispatch_beforeunload_event_fn_str.into())
669 .unwrap();
670 let dispatch_beforeunload_event_fn =
671 v8::Local::<v8::Function>::try_from(dispatch_beforeunload_event_fn)
672 .unwrap();
673 let dispatch_unload_event_fn_str =
674 v8::String::new_external_onebyte_static(scope, b"dispatchUnloadEvent")
675 .unwrap();
676 let dispatch_unload_event_fn = bootstrap_ns
677 .get(scope, dispatch_unload_event_fn_str.into())
678 .unwrap();
679 let dispatch_unload_event_fn =
680 v8::Local::<v8::Function>::try_from(dispatch_unload_event_fn).unwrap();
681 let dispatch_process_beforeexit_event =
682 v8::String::new_external_onebyte_static(
683 scope,
684 b"dispatchProcessBeforeExitEvent",
685 )
686 .unwrap();
687 let dispatch_process_beforeexit_event_fn = bootstrap_ns
688 .get(scope, dispatch_process_beforeexit_event.into())
689 .unwrap();
690 let dispatch_process_beforeexit_event_fn =
691 v8::Local::<v8::Function>::try_from(
692 dispatch_process_beforeexit_event_fn,
693 )
694 .unwrap();
695 let dispatch_process_exit_event =
696 v8::String::new_external_onebyte_static(
697 scope,
698 b"dispatchProcessExitEvent",
699 )
700 .unwrap();
701 let dispatch_process_exit_event_fn = bootstrap_ns
702 .get(scope, dispatch_process_exit_event.into())
703 .unwrap();
704 let dispatch_process_exit_event_fn =
705 v8::Local::<v8::Function>::try_from(dispatch_process_exit_event_fn)
706 .unwrap();
707 (
708 v8::Global::new(scope, bootstrap_fn),
709 v8::Global::new(scope, dispatch_load_event_fn),
710 v8::Global::new(scope, dispatch_beforeunload_event_fn),
711 v8::Global::new(scope, dispatch_unload_event_fn),
712 v8::Global::new(scope, dispatch_process_beforeexit_event_fn),
713 v8::Global::new(scope, dispatch_process_exit_event_fn),
714 )
715 };
716
717 let worker = Self {
718 js_runtime,
719 should_break_on_first_statement: options.should_break_on_first_statement,
720 should_wait_for_inspector_session: options
721 .should_wait_for_inspector_session,
722 exit_code,
723 bootstrap_fn_global: Some(bootstrap_fn_global),
724 dispatch_load_event_fn_global,
725 dispatch_beforeunload_event_fn_global,
726 dispatch_unload_event_fn_global,
727 dispatch_process_beforeexit_event_fn_global,
728 dispatch_process_exit_event_fn_global,
729 memory_trim_handle: None,
730 };
731 (worker, options.bootstrap)
732 }
733
734 pub fn bootstrap(&mut self, options: BootstrapOptions) {
735 {
737 let op_state = self.js_runtime.op_state();
738 let mut state = op_state.borrow_mut();
739 state.put(options.clone());
740 if let Some((fd, serialization)) = options.node_ipc_init {
741 state.put(deno_node::ChildPipeFd(fd, serialization));
742 }
743 }
744
745 deno_core::scope!(scope, &mut self.js_runtime);
746 v8::tc_scope!(scope, scope);
747 let args = options.as_v8(scope);
748 let bootstrap_fn = self.bootstrap_fn_global.take().unwrap();
749 let bootstrap_fn = v8::Local::new(scope, bootstrap_fn);
750 let undefined = v8::undefined(scope);
751 bootstrap_fn.call(scope, undefined.into(), &[args]);
752 if let Some(exception) = scope.exception() {
753 let error = JsError::from_v8_exception(scope, exception);
754 panic!("Bootstrap exception: {error}");
755 }
756 }
757
758 #[cfg(not(target_os = "linux"))]
759 pub fn setup_memory_trim_handler(&mut self) {
760 }
762
763 #[cfg(target_os = "linux")]
768 pub fn setup_memory_trim_handler(&mut self) {
769 if self.memory_trim_handle.is_some() {
770 return;
771 }
772
773 if !*MEMORY_TRIM_HANDLER_ENABLED {
774 return;
775 }
776
777 let mut sigusr2_rx = SIGUSR2_RX.clone();
778
779 let spawner = self
780 .js_runtime
781 .op_state()
782 .borrow()
783 .borrow::<deno_core::V8CrossThreadTaskSpawner>()
784 .clone();
785
786 let memory_trim_handle = tokio::spawn(async move {
787 loop {
788 if sigusr2_rx.changed().await.is_err() {
789 break;
790 }
791
792 spawner.spawn(move |isolate| {
793 isolate.low_memory_notification();
794 });
795 }
796 });
797
798 self.memory_trim_handle = Some(memory_trim_handle);
799 }
800
801 pub fn execute_script(
803 &mut self,
804 script_name: &'static str,
805 source_code: ModuleCodeString,
806 ) -> Result<v8::Global<v8::Value>, Box<JsError>> {
807 self.js_runtime.execute_script(script_name, source_code)
808 }
809
810 pub async fn preload_main_module(
812 &mut self,
813 module_specifier: &ModuleSpecifier,
814 ) -> Result<ModuleId, CoreError> {
815 self.js_runtime.load_main_es_module(module_specifier).await
816 }
817
818 pub async fn preload_side_module(
820 &mut self,
821 module_specifier: &ModuleSpecifier,
822 ) -> Result<ModuleId, CoreError> {
823 self.js_runtime.load_side_es_module(module_specifier).await
824 }
825
826 pub async fn evaluate_module(
828 &mut self,
829 id: ModuleId,
830 ) -> Result<(), CoreError> {
831 self.wait_for_inspector_session();
832 let mut receiver = self.js_runtime.mod_evaluate(id);
833 tokio::select! {
834 biased;
837
838 maybe_result = &mut receiver => {
839 debug!("received module evaluate {:#?}", maybe_result);
840 maybe_result
841 }
842
843 event_loop_result = self.run_event_loop(false) => {
844 event_loop_result?;
845 receiver.await
846 }
847 }
848 }
849
850 pub async fn run_up_to_duration(
853 &mut self,
854 duration: Duration,
855 ) -> Result<(), CoreError> {
856 match tokio::time::timeout(
857 duration,
858 self
859 .js_runtime
860 .run_event_loop(PollEventLoopOptions::default()),
861 )
862 .await
863 {
864 Ok(Ok(_)) => Ok(()),
865 Err(_) => Ok(()),
866 Ok(Err(e)) => Err(e),
867 }
868 }
869
870 pub async fn execute_side_module(
872 &mut self,
873 module_specifier: &ModuleSpecifier,
874 ) -> Result<(), CoreError> {
875 let id = self.preload_side_module(module_specifier).await?;
876 self.evaluate_module(id).await
877 }
878
879 pub async fn execute_main_module(
883 &mut self,
884 module_specifier: &ModuleSpecifier,
885 ) -> Result<(), CoreError> {
886 let id = self.preload_main_module(module_specifier).await?;
887 self.evaluate_module(id).await
888 }
889
890 fn wait_for_inspector_session(&mut self) {
891 if self.should_break_on_first_statement {
892 self
893 .js_runtime
894 .inspector()
895 .wait_for_session_and_break_on_next_statement();
896 } else if self.should_wait_for_inspector_session {
897 self.js_runtime.inspector().wait_for_session();
898 }
899 }
900
901 pub fn create_inspector_session(
904 &mut self,
905 cb: deno_core::InspectorSessionSend,
906 ) -> LocalInspectorSession {
907 self.js_runtime.maybe_init_inspector();
908 let insp = self.js_runtime.inspector();
909
910 JsRuntimeInspector::create_local_session(
911 insp,
912 cb,
913 InspectorSessionKind::Blocking,
914 )
915 }
916
917 pub async fn run_event_loop(
918 &mut self,
919 wait_for_inspector: bool,
920 ) -> Result<(), CoreError> {
921 self
922 .js_runtime
923 .run_event_loop(PollEventLoopOptions {
924 wait_for_inspector,
925 ..Default::default()
926 })
927 .await
928 }
929
930 pub fn exit_code(&self) -> i32 {
933 self.exit_code.get()
934 }
935
936 pub fn dispatch_load_event(&mut self) -> Result<(), Box<JsError>> {
940 deno_core::scope!(scope, &mut self.js_runtime);
941 v8::tc_scope!(tc_scope, scope);
942 let dispatch_load_event_fn =
943 v8::Local::new(tc_scope, &self.dispatch_load_event_fn_global);
944 let undefined = v8::undefined(tc_scope);
945 dispatch_load_event_fn.call(tc_scope, undefined.into(), &[]);
946 if let Some(exception) = tc_scope.exception() {
947 let error = JsError::from_v8_exception(tc_scope, exception);
948 return Err(error);
949 }
950 Ok(())
951 }
952
953 pub fn dispatch_unload_event(&mut self) -> Result<(), Box<JsError>> {
957 deno_core::scope!(scope, &mut self.js_runtime);
958 v8::tc_scope!(tc_scope, scope);
959 let dispatch_unload_event_fn =
960 v8::Local::new(tc_scope, &self.dispatch_unload_event_fn_global);
961 let undefined = v8::undefined(tc_scope);
962 dispatch_unload_event_fn.call(tc_scope, undefined.into(), &[]);
963 if let Some(exception) = tc_scope.exception() {
964 let error = JsError::from_v8_exception(tc_scope, exception);
965 return Err(error);
966 }
967 Ok(())
968 }
969
970 pub fn dispatch_process_exit_event(&mut self) -> Result<(), Box<JsError>> {
972 deno_core::scope!(scope, &mut self.js_runtime);
973 v8::tc_scope!(tc_scope, scope);
974 let dispatch_process_exit_event_fn =
975 v8::Local::new(tc_scope, &self.dispatch_process_exit_event_fn_global);
976 let undefined = v8::undefined(tc_scope);
977 dispatch_process_exit_event_fn.call(tc_scope, undefined.into(), &[]);
978 if let Some(exception) = tc_scope.exception() {
979 let error = JsError::from_v8_exception(tc_scope, exception);
980 return Err(error);
981 }
982 Ok(())
983 }
984
985 pub fn dispatch_beforeunload_event(&mut self) -> Result<bool, Box<JsError>> {
989 deno_core::scope!(scope, &mut self.js_runtime);
990 v8::tc_scope!(tc_scope, scope);
991 let dispatch_beforeunload_event_fn =
992 v8::Local::new(tc_scope, &self.dispatch_beforeunload_event_fn_global);
993 let undefined = v8::undefined(tc_scope);
994 let ret_val =
995 dispatch_beforeunload_event_fn.call(tc_scope, undefined.into(), &[]);
996 if let Some(exception) = tc_scope.exception() {
997 let error = JsError::from_v8_exception(tc_scope, exception);
998 return Err(error);
999 }
1000 let ret_val = ret_val.unwrap();
1001 Ok(ret_val.is_false())
1002 }
1003
1004 pub fn dispatch_process_beforeexit_event(
1006 &mut self,
1007 ) -> Result<bool, Box<JsError>> {
1008 deno_core::scope!(scope, &mut self.js_runtime);
1009 v8::tc_scope!(tc_scope, scope);
1010 let dispatch_process_beforeexit_event_fn = v8::Local::new(
1011 tc_scope,
1012 &self.dispatch_process_beforeexit_event_fn_global,
1013 );
1014 let undefined = v8::undefined(tc_scope);
1015 let ret_val = dispatch_process_beforeexit_event_fn.call(
1016 tc_scope,
1017 undefined.into(),
1018 &[],
1019 );
1020 if let Some(exception) = tc_scope.exception() {
1021 let error = JsError::from_v8_exception(tc_scope, exception);
1022 return Err(error);
1023 }
1024 let ret_val = ret_val.unwrap();
1025 Ok(ret_val.is_true())
1026 }
1027}
1028
1029fn common_extensions<
1030 TInNpmPackageChecker: InNpmPackageChecker + 'static,
1031 TNpmPackageFolderResolver: NpmPackageFolderResolver + 'static,
1032 TExtNodeSys: ExtNodeSys + 'static,
1033>(
1034 has_snapshot: bool,
1035 unconfigured_runtime: bool,
1036) -> Vec<Extension> {
1037 vec![
1041 deno_telemetry::deno_telemetry::init(),
1042 deno_webidl::deno_webidl::init(),
1044 deno_web::deno_web::lazy_init(),
1045 deno_webgpu::deno_webgpu::init(),
1046 deno_canvas::deno_canvas::init(),
1047 deno_fetch::deno_fetch::lazy_init(),
1048 deno_cache::deno_cache::lazy_init(),
1049 deno_websocket::deno_websocket::lazy_init(),
1050 deno_webstorage::deno_webstorage::lazy_init(),
1051 deno_crypto::deno_crypto::lazy_init(),
1052 deno_ffi::deno_ffi::lazy_init(),
1053 deno_net::deno_net::lazy_init(),
1054 deno_tls::deno_tls::init(),
1055 deno_kv::deno_kv::lazy_init::<MultiBackendDbHandler>(),
1056 deno_cron::deno_cron::init(LocalCronHandler::new()),
1057 deno_napi::deno_napi::lazy_init(),
1058 deno_http::deno_http::lazy_init(),
1059 deno_io::deno_io::lazy_init(),
1060 deno_fs::deno_fs::lazy_init(),
1061 deno_os::deno_os::lazy_init(),
1062 deno_process::deno_process::lazy_init(),
1063 deno_node::deno_node::lazy_init::<
1064 TInNpmPackageChecker,
1065 TNpmPackageFolderResolver,
1066 TExtNodeSys,
1067 >(),
1068 ops::runtime::deno_runtime::lazy_init(),
1070 ops::worker_host::deno_worker_host::lazy_init(),
1071 ops::fs_events::deno_fs_events::init(),
1072 ops::permissions::deno_permissions::init(),
1073 ops::tty::deno_tty::init(),
1074 ops::http::deno_http_runtime::init(),
1075 deno_bundle_runtime::deno_bundle_runtime::lazy_init(),
1076 ops::bootstrap::deno_bootstrap::init(
1077 has_snapshot.then(Default::default),
1078 unconfigured_runtime,
1079 ),
1080 runtime::init(),
1081 ops::web_worker::deno_web_worker::init().disable(),
1086 ]
1087}
1088
1089struct CommonRuntimeOptions {
1090 module_loader: Rc<dyn ModuleLoader>,
1091 startup_snapshot: Option<&'static [u8]>,
1092 create_params: Option<v8::CreateParams>,
1093 skip_op_registration: bool,
1094 shared_array_buffer_store: Option<SharedArrayBufferStore>,
1095 compiled_wasm_module_store: Option<CompiledWasmModuleStore>,
1096 extensions: Vec<Extension>,
1097 op_metrics_factory_fn: Option<OpMetricsFactoryFn>,
1098 enable_stack_trace_arg_in_ops: bool,
1099}
1100
1101struct EnableRawImports(Arc<AtomicBool>);
1102
1103#[allow(clippy::too_many_arguments)]
1104fn common_runtime(opts: CommonRuntimeOptions) -> JsRuntime {
1105 let enable_raw_imports = Arc::new(AtomicBool::new(false));
1106
1107 let js_runtime = JsRuntime::new(RuntimeOptions {
1108 module_loader: Some(opts.module_loader),
1109 startup_snapshot: opts.startup_snapshot,
1110 create_params: opts.create_params,
1111 skip_op_registration: opts.skip_op_registration,
1112 shared_array_buffer_store: opts.shared_array_buffer_store,
1113 compiled_wasm_module_store: opts.compiled_wasm_module_store,
1114 extensions: opts.extensions,
1115 #[cfg(feature = "transpile")]
1116 extension_transpiler: Some(Rc::new(|specifier, source| {
1117 crate::transpile::maybe_transpile_source(specifier, source)
1118 })),
1119 #[cfg(not(feature = "transpile"))]
1120 extension_transpiler: None,
1121 inspector: true,
1122 is_main: true,
1123 op_metrics_factory_fn: opts.op_metrics_factory_fn,
1124 wait_for_inspector_disconnect_callback: Some(
1125 make_wait_for_inspector_disconnect_callback(),
1126 ),
1127 validate_import_attributes_cb: Some(
1128 create_validate_import_attributes_callback(enable_raw_imports.clone()),
1129 ),
1130 import_assertions_support: deno_core::ImportAssertionsSupport::Error,
1131 maybe_op_stack_trace_callback: opts
1132 .enable_stack_trace_arg_in_ops
1133 .then(create_permissions_stack_trace_callback),
1134 extension_code_cache: None,
1135 v8_platform: None,
1136 custom_module_evaluation_cb: None,
1137 eval_context_code_cache_cbs: None,
1138 });
1139
1140 js_runtime
1141 .op_state()
1142 .borrow_mut()
1143 .put(EnableRawImports(enable_raw_imports));
1144
1145 js_runtime
1146}
1147
1148pub fn create_permissions_stack_trace_callback()
1149-> deno_core::OpStackTraceCallback {
1150 Box::new(|stack: Vec<deno_core::error::JsStackFrame>| {
1151 deno_permissions::prompter::set_current_stacktrace(Box::new(move || {
1152 stack
1153 .iter()
1154 .map(|frame| {
1155 deno_core::error::format_frame::<deno_core::error::NoAnsiColors>(
1156 frame, None,
1157 )
1158 })
1159 .collect()
1160 }))
1161 }) as _
1162}
1163
1164pub struct UnconfiguredRuntimeOptions {
1165 pub startup_snapshot: &'static [u8],
1166 pub create_params: Option<v8::CreateParams>,
1167 pub shared_array_buffer_store: Option<SharedArrayBufferStore>,
1168 pub compiled_wasm_module_store: Option<CompiledWasmModuleStore>,
1169 pub additional_extensions: Vec<Extension>,
1170}
1171
1172pub struct UnconfiguredRuntime {
1173 module_loader: Rc<PlaceholderModuleLoader>,
1174 js_runtime: JsRuntime,
1175}
1176
1177impl UnconfiguredRuntime {
1178 pub fn new<
1179 TInNpmPackageChecker: InNpmPackageChecker + 'static,
1180 TNpmPackageFolderResolver: NpmPackageFolderResolver + 'static,
1181 TExtNodeSys: ExtNodeSys + 'static,
1182 >(
1183 options: UnconfiguredRuntimeOptions,
1184 ) -> Self {
1185 let mut extensions = common_extensions::<
1186 TInNpmPackageChecker,
1187 TNpmPackageFolderResolver,
1188 TExtNodeSys,
1189 >(true, true);
1190
1191 extensions.extend(options.additional_extensions);
1192
1193 let module_loader =
1194 Rc::new(PlaceholderModuleLoader(std::cell::RefCell::new(None)));
1195
1196 let js_runtime = common_runtime(CommonRuntimeOptions {
1197 module_loader: module_loader.clone(),
1198 startup_snapshot: Some(options.startup_snapshot),
1199 create_params: options.create_params,
1200 skip_op_registration: true,
1201 shared_array_buffer_store: options.shared_array_buffer_store,
1202 compiled_wasm_module_store: options.compiled_wasm_module_store,
1203 extensions,
1204 op_metrics_factory_fn: None,
1205 enable_stack_trace_arg_in_ops: false,
1206 });
1207
1208 UnconfiguredRuntime {
1209 module_loader,
1210 js_runtime,
1211 }
1212 }
1213
1214 fn hydrate(self, module_loader: Rc<dyn ModuleLoader>) -> JsRuntime {
1215 let _ = self.module_loader.0.borrow_mut().insert(module_loader);
1216 self.js_runtime
1217 }
1218}
1219
1220struct PlaceholderModuleLoader(
1221 std::cell::RefCell<Option<Rc<dyn ModuleLoader>>>,
1222);
1223
1224impl ModuleLoader for PlaceholderModuleLoader {
1225 fn resolve(
1226 &self,
1227 specifier: &str,
1228 referrer: &str,
1229 kind: deno_core::ResolutionKind,
1230 ) -> Result<ModuleSpecifier, deno_core::error::ModuleLoaderError> {
1231 self
1232 .0
1233 .borrow_mut()
1234 .clone()
1235 .unwrap()
1236 .resolve(specifier, referrer, kind)
1237 }
1238
1239 fn load(
1240 &self,
1241 module_specifier: &ModuleSpecifier,
1242 maybe_referrer: Option<&ModuleLoadReferrer>,
1243 options: ModuleLoadOptions,
1244 ) -> deno_core::ModuleLoadResponse {
1245 self.0.borrow_mut().clone().unwrap().load(
1246 module_specifier,
1247 maybe_referrer,
1248 options,
1249 )
1250 }
1251
1252 fn prepare_load(
1253 &self,
1254 module_specifier: &ModuleSpecifier,
1255 maybe_referrer: Option<String>,
1256 options: ModuleLoadOptions,
1257 ) -> std::pin::Pin<
1258 Box<
1259 dyn std::prelude::rust_2024::Future<
1260 Output = Result<(), deno_core::error::ModuleLoaderError>,
1261 >,
1262 >,
1263 > {
1264 self.0.borrow_mut().clone().unwrap().prepare_load(
1265 module_specifier,
1266 maybe_referrer,
1267 options,
1268 )
1269 }
1270
1271 fn finish_load(&self) {
1272 self.0.borrow_mut().clone().unwrap().finish_load()
1273 }
1274
1275 fn purge_and_prevent_code_cache(&self, module_specifier: &str) {
1276 self
1277 .0
1278 .borrow_mut()
1279 .clone()
1280 .unwrap()
1281 .purge_and_prevent_code_cache(module_specifier)
1282 }
1283
1284 fn get_source_map(&self, file_name: &str) -> Option<Cow<'_, [u8]>> {
1285 let v = self.0.borrow_mut().clone().unwrap();
1286 let v = v.get_source_map(file_name);
1287 v.map(|c| Cow::from(c.into_owned()))
1288 }
1289
1290 fn get_source_mapped_source_line(
1291 &self,
1292 file_name: &str,
1293 line_number: usize,
1294 ) -> Option<String> {
1295 self
1296 .0
1297 .borrow_mut()
1298 .clone()
1299 .unwrap()
1300 .get_source_mapped_source_line(file_name, line_number)
1301 }
1302
1303 fn get_host_defined_options<'s>(
1304 &self,
1305 scope: &mut v8::PinScope<'s, '_>,
1306 name: &str,
1307 ) -> Option<v8::Local<'s, v8::Data>> {
1308 self
1309 .0
1310 .borrow_mut()
1311 .clone()
1312 .unwrap()
1313 .get_host_defined_options(scope, name)
1314 }
1315}