1use super::bindings;
3use super::bindings::create_exports_for_ops_virtual_module;
4use super::bindings::watch_promise;
5use super::exception_state::ExceptionState;
6use super::jsrealm::JsRealmInner;
7use super::op_driver::OpDriver;
8use super::setup;
9use super::snapshot;
10use super::stats::RuntimeActivityStatsFactory;
11use super::v8_static_strings::*;
12use super::SnapshotStoreDataStore;
13use super::SnapshottedData;
14use crate::ascii_str;
15use crate::ascii_str_include;
16use crate::error::exception_to_err_result;
17use crate::error::AnyError;
18use crate::error::GetErrorClassFn;
19use crate::error::JsError;
20use crate::extension_set;
21use crate::extension_set::LoadedSources;
22use crate::extensions::GlobalObjectMiddlewareFn;
23use crate::extensions::GlobalTemplateMiddlewareFn;
24use crate::include_js_files;
25use crate::inspector::JsRuntimeInspector;
26use crate::module_specifier::ModuleSpecifier;
27use crate::modules::default_import_meta_resolve_cb;
28use crate::modules::CustomModuleEvaluationCb;
29use crate::modules::EvalContextCodeCacheReadyCb;
30use crate::modules::EvalContextGetCodeCacheCb;
31use crate::modules::ExtModuleLoader;
32use crate::modules::ImportMetaResolveCallback;
33use crate::modules::IntoModuleCodeString;
34use crate::modules::IntoModuleName;
35use crate::modules::ModuleId;
36use crate::modules::ModuleLoader;
37use crate::modules::ModuleMap;
38use crate::modules::ModuleName;
39use crate::modules::RequestedModuleType;
40use crate::modules::ValidateImportAttributesCb;
41use crate::ops_metrics::dispatch_metrics_async;
42use crate::ops_metrics::OpMetricsFactoryFn;
43use crate::runtime::ContextState;
44use crate::runtime::JsRealm;
45use crate::runtime::OpDriverImpl;
46use crate::source_map::SourceMapData;
47use crate::source_map::SourceMapGetter;
48use crate::source_map::SourceMapper;
49use crate::stats::RuntimeActivityType;
50use crate::Extension;
51use crate::ExtensionFileSource;
52use crate::ExtensionFileSourceCode;
53use crate::FastStaticString;
54use crate::FastString;
55use crate::FeatureChecker;
56use crate::ModuleCodeString;
57use crate::NoopModuleLoader;
58use crate::OpMetricsEvent;
59use crate::OpState;
60use anyhow::anyhow;
61use anyhow::bail;
62use anyhow::Context as _;
63use anyhow::Error;
64use futures::future::poll_fn;
65use futures::task::AtomicWaker;
66use futures::Future;
67use futures::FutureExt;
68use smallvec::SmallVec;
69use std::any::Any;
70
71use std::cell::Cell;
72use std::cell::RefCell;
73use std::collections::HashMap;
74use std::collections::VecDeque;
75use std::ffi::c_void;
76use std::mem::ManuallyDrop;
77use std::ops::Deref;
78use std::ops::DerefMut;
79use std::pin::Pin;
80use std::rc::Rc;
81use std::sync::Arc;
82use std::sync::Mutex;
83use std::task::Context;
84use std::task::Poll;
85use std::task::Waker;
86use v8::Isolate;
87
88pub type WaitForInspectorDisconnectCallback = Box<dyn Fn()>;
89const STATE_DATA_OFFSET: u32 = 0;
90
91pub type ExtensionTranspiler =
92 dyn Fn(
93 ModuleName,
94 ModuleCodeString,
95 ) -> Result<(ModuleCodeString, Option<SourceMapData>), AnyError>;
96
97#[derive(Default)]
99pub(crate) struct IsolateAllocations {
100 pub(crate) external_refs: Option<Box<v8::ExternalReferences>>,
101 pub(crate) externalized_sources: Box<[v8::OneByteConst]>,
102 pub(crate) original_sources: Box<[FastString]>,
103 pub(crate) near_heap_limit_callback_data:
104 Option<(Box<RefCell<dyn Any>>, v8::NearHeapLimitCallback)>,
105}
106
107pub(crate) struct ManuallyDropRc<T>(ManuallyDrop<Rc<T>>);
110
111impl<T> ManuallyDropRc<T> {
112 #[allow(unused)]
113 pub fn clone(&self) -> Rc<T> {
114 self.0.deref().clone()
115 }
116}
117
118impl<T> Deref for ManuallyDropRc<T> {
119 type Target = Rc<T>;
120 fn deref(&self) -> &Self::Target {
121 self.0.deref()
122 }
123}
124
125impl<T> DerefMut for ManuallyDropRc<T> {
126 fn deref_mut(&mut self) -> &mut Self::Target {
127 self.0.deref_mut()
128 }
129}
130
131pub(crate) struct InnerIsolateState {
144 will_snapshot: bool,
145 extension_count: usize,
146 op_count: usize,
147 source_count: usize,
148 addl_refs_count: usize,
149 main_realm: ManuallyDrop<JsRealm>,
150 pub(crate) state: ManuallyDropRc<JsRuntimeState>,
151 v8_isolate: ManuallyDrop<v8::OwnedIsolate>,
152 cpp_heap: ManuallyDrop<v8::UniqueRef<v8::cppgc::Heap>>,
153}
154
155impl InnerIsolateState {
156 pub fn prepare_for_cleanup(&mut self) {
160 self.main_realm.0.context_state.pending_ops.shutdown();
163 let inspector = self.state.inspector.take();
164 self.state.op_state.borrow_mut().clear();
165 if let Some(inspector) = inspector {
166 assert_eq!(
167 Rc::strong_count(&inspector),
168 1,
169 "The inspector must be dropped before the runtime"
170 );
171 }
172 }
173
174 pub fn cleanup(&mut self) {
175 self.prepare_for_cleanup();
176
177 let state_ptr = self.v8_isolate.get_data(STATE_DATA_OFFSET);
178 _ = unsafe { Rc::from_raw(state_ptr as *const JsRuntimeState) };
181
182 unsafe {
183 ManuallyDrop::take(&mut self.main_realm).0.destroy();
184 }
185
186 debug_assert_eq!(Rc::strong_count(&self.state), 1);
187 }
188
189 pub fn prepare_for_snapshot(mut self) -> v8::OwnedIsolate {
190 self.cleanup();
191 let (state, _cpp_heap, isolate) = unsafe {
193 (
194 ManuallyDrop::take(&mut self.state.0),
195 ManuallyDrop::take(&mut self.cpp_heap),
196 ManuallyDrop::take(&mut self.v8_isolate),
197 )
198 };
199 std::mem::forget(self);
200 drop(state);
201 isolate
202 }
203}
204
205impl Drop for InnerIsolateState {
206 fn drop(&mut self) {
207 self.cleanup();
208 unsafe {
210 ManuallyDrop::drop(&mut self.state.0);
211 if self.will_snapshot {
212 eprintln!("WARNING: v8::OwnedIsolate for snapshot was leaked");
214 } else {
215 ManuallyDrop::drop(&mut self.cpp_heap);
216 ManuallyDrop::drop(&mut self.v8_isolate);
217 }
218 }
219 }
220}
221
222#[derive(Copy, Clone, Debug, Eq, PartialEq)]
223pub(crate) enum InitMode {
224 New,
226 FromSnapshot {
228 skip_op_registration: bool,
230 },
231}
232
233impl InitMode {
234 fn from_options(options: &RuntimeOptions) -> Self {
235 match options.startup_snapshot {
236 None => Self::New,
237 Some(_) => Self::FromSnapshot {
238 skip_op_registration: options.skip_op_registration,
239 },
240 }
241 }
242
243 #[inline]
244 pub fn needs_ops_bindings(&self) -> bool {
245 !matches!(
246 self,
247 InitMode::FromSnapshot {
248 skip_op_registration: true
249 }
250 )
251 }
252}
253
254#[derive(Default)]
255struct PromiseFuture {
256 resolved: Cell<Option<Result<v8::Global<v8::Value>, Error>>>,
257 waker: Cell<Option<Waker>>,
258}
259
260#[derive(Clone, Default)]
261struct RcPromiseFuture(Rc<PromiseFuture>);
262
263impl RcPromiseFuture {
264 pub fn new(res: Result<v8::Global<v8::Value>, Error>) -> Self {
265 Self(Rc::new(PromiseFuture {
266 resolved: Some(res).into(),
267 ..Default::default()
268 }))
269 }
270}
271
272impl Future for RcPromiseFuture {
273 type Output = Result<v8::Global<v8::Value>, Error>;
274 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
275 let this = self.get_mut();
276 if let Some(resolved) = this.0.resolved.take() {
277 Poll::Ready(resolved)
278 } else {
279 this.0.waker.set(Some(cx.waker().clone()));
280 Poll::Pending
281 }
282 }
283}
284
285static VIRTUAL_OPS_MODULE_NAME: FastStaticString = ascii_str!("ext:core/ops");
286
287pub(crate) struct InternalSourceFile {
288 pub specifier: FastStaticString,
289 pub source: FastStaticString,
290}
291
292macro_rules! internal_source_file {
293 ($str_:literal) => {{
294 InternalSourceFile {
295 specifier: ascii_str!(concat!("ext:core/", $str_)),
296 source: ascii_str_include!(concat!("../", $str_)),
297 }
298 }};
299}
300
301pub(crate) static CONTEXT_SETUP_SOURCES: [InternalSourceFile; 2] = [
304 internal_source_file!("00_primordials.js"),
305 internal_source_file!("00_infra.js"),
306];
307
308pub(crate) static BUILTIN_SOURCES: [InternalSourceFile; 2] = [
311 internal_source_file!("01_core.js"),
312 internal_source_file!("02_error.js"),
313];
314
315pub(crate) static BUILTIN_ES_MODULES: [ExtensionFileSource; 1] =
318 include_js_files!(core "mod.js",);
319
320#[cfg(test)]
322pub(crate) const NO_OF_BUILTIN_MODULES: usize = 2;
323
324pub struct JsRuntime {
337 pub(crate) inner: InnerIsolateState,
338 pub(crate) allocations: IsolateAllocations,
339 files_loaded_from_fs_during_snapshot: Vec<&'static str>,
343 is_main_runtime: bool,
345}
346
347pub struct JsRuntimeForSnapshot(JsRuntime);
349
350impl Deref for JsRuntimeForSnapshot {
351 type Target = JsRuntime;
352
353 fn deref(&self) -> &Self::Target {
354 &self.0
355 }
356}
357
358impl DerefMut for JsRuntimeForSnapshot {
359 fn deref_mut(&mut self) -> &mut Self::Target {
360 &mut self.0
361 }
362}
363
364pub struct CrossIsolateStore<T>(Arc<Mutex<CrossIsolateStoreInner<T>>>);
365
366struct CrossIsolateStoreInner<T> {
367 map: HashMap<u32, T>,
368 last_id: u32,
369}
370
371impl<T> CrossIsolateStore<T> {
372 pub(crate) fn insert(&self, value: T) -> u32 {
373 let mut store = self.0.lock().unwrap();
374 let last_id = store.last_id;
375 store.map.insert(last_id, value);
376 store.last_id += 1;
377 last_id
378 }
379
380 pub(crate) fn take(&self, id: u32) -> Option<T> {
381 let mut store = self.0.lock().unwrap();
382 store.map.remove(&id)
383 }
384}
385
386impl<T> Default for CrossIsolateStore<T> {
387 fn default() -> Self {
388 CrossIsolateStore(Arc::new(Mutex::new(CrossIsolateStoreInner {
389 map: Default::default(),
390 last_id: 0,
391 })))
392 }
393}
394
395impl<T> Clone for CrossIsolateStore<T> {
396 fn clone(&self) -> Self {
397 Self(self.0.clone())
398 }
399}
400
401pub type SharedArrayBufferStore =
402 CrossIsolateStore<v8::SharedRef<v8::BackingStore>>;
403
404pub type CompiledWasmModuleStore = CrossIsolateStore<v8::CompiledWasmModule>;
405
406pub struct JsRuntimeState {
409 pub(crate) source_mapper: RefCell<SourceMapper<Rc<dyn SourceMapGetter>>>,
410 pub(crate) op_state: Rc<RefCell<OpState>>,
411 pub(crate) shared_array_buffer_store: Option<SharedArrayBufferStore>,
412 pub(crate) compiled_wasm_module_store: Option<CompiledWasmModuleStore>,
413 wait_for_inspector_disconnect_callback:
414 Option<WaitForInspectorDisconnectCallback>,
415 pub(crate) validate_import_attributes_cb: Option<ValidateImportAttributesCb>,
416 pub(crate) custom_module_evaluation_cb: Option<CustomModuleEvaluationCb>,
417 pub(crate) eval_context_get_code_cache_cb: Option<EvalContextGetCodeCacheCb>,
418 pub(crate) eval_context_code_cache_ready_cb:
419 Option<EvalContextCodeCacheReadyCb>,
420 waker: Arc<AtomicWaker>,
421 inspector: RefCell<Option<Rc<RefCell<JsRuntimeInspector>>>>,
423 has_inspector: Cell<bool>,
424}
425
426#[derive(Default)]
427pub struct RuntimeOptions {
428 pub source_map_getter: Option<Rc<dyn SourceMapGetter>>,
430
431 pub get_error_class_fn: Option<GetErrorClassFn>,
434
435 pub module_loader: Option<Rc<dyn ModuleLoader>>,
441
442 pub extension_transpiler: Option<Rc<ExtensionTranspiler>>,
444
445 pub op_metrics_factory_fn: Option<OpMetricsFactoryFn>,
448
449 pub extensions: Vec<Extension>,
457
458 pub startup_snapshot: Option<&'static [u8]>,
463
464 pub skip_op_registration: bool,
466
467 pub create_params: Option<v8::CreateParams>,
469
470 pub v8_platform: Option<v8::SharedRef<v8::Platform>>,
473
474 pub shared_array_buffer_store: Option<SharedArrayBufferStore>,
480
481 pub compiled_wasm_module_store: Option<CompiledWasmModuleStore>,
488
489 pub inspector: bool,
491
492 pub is_main: bool,
495
496 #[cfg(any(test, feature = "unsafe_runtime_options"))]
497 pub unsafe_expose_natives_and_gc: bool,
501
502 pub feature_checker: Option<Arc<FeatureChecker>>,
505
506 pub validate_import_attributes_cb: Option<ValidateImportAttributesCb>,
515
516 pub import_meta_resolve_callback: Option<ImportMetaResolveCallback>,
521
522 pub wait_for_inspector_disconnect_callback:
529 Option<WaitForInspectorDisconnectCallback>,
530
531 pub custom_module_evaluation_cb: Option<CustomModuleEvaluationCb>,
534
535 pub enable_code_cache: bool,
539
540 pub eval_context_code_cache_cbs:
543 Option<(EvalContextGetCodeCacheCb, EvalContextCodeCacheReadyCb)>,
544}
545
546impl RuntimeOptions {
547 #[cfg(any(test, feature = "unsafe_runtime_options"))]
548 fn unsafe_expose_natives_and_gc(&self) -> bool {
549 self.unsafe_expose_natives_and_gc
550 }
551
552 #[cfg(not(any(test, feature = "unsafe_runtime_options")))]
553 fn unsafe_expose_natives_and_gc(&self) -> bool {
554 false
555 }
556}
557
558#[derive(Copy, Clone, Debug)]
559pub struct PollEventLoopOptions {
560 pub wait_for_inspector: bool,
561 pub pump_v8_message_loop: bool,
562}
563
564impl Default for PollEventLoopOptions {
565 fn default() -> Self {
566 Self {
567 wait_for_inspector: false,
568 pump_v8_message_loop: true,
569 }
570 }
571}
572
573#[derive(Default)]
574pub struct CreateRealmOptions {
575 pub module_loader: Option<Rc<dyn ModuleLoader>>,
581}
582
583impl JsRuntime {
584 #[cfg(not(any(test, feature = "unsafe_runtime_options")))]
588 pub fn init_platform(v8_platform: Option<v8::SharedRef<v8::Platform>>) {
589 setup::init_v8(v8_platform, cfg!(test), false);
590 }
591
592 #[cfg(any(test, feature = "unsafe_runtime_options"))]
601 pub fn init_platform(
602 v8_platform: Option<v8::SharedRef<v8::Platform>>,
603 expose_natives: bool,
604 ) {
605 setup::init_v8(v8_platform, cfg!(test), expose_natives);
606 }
607
608 pub fn new(mut options: RuntimeOptions) -> JsRuntime {
610 setup::init_v8(
611 options.v8_platform.take(),
612 cfg!(test),
613 options.unsafe_expose_natives_and_gc(),
614 );
615 match JsRuntime::new_inner(options, false) {
616 Ok(runtime) => runtime,
617 Err(err) => {
618 panic!("Failed to initialize a JsRuntime: {:?}", err);
619 }
620 }
621 }
622
623 pub(crate) fn state_from(isolate: &v8::Isolate) -> Rc<JsRuntimeState> {
624 let state_ptr = isolate.get_data(STATE_DATA_OFFSET);
625 let state_rc =
626 unsafe { Rc::from_raw(state_ptr as *const JsRuntimeState) };
629 let state = state_rc.clone();
630 std::mem::forget(state_rc);
631 state
632 }
633
634 pub fn op_state_from(isolate: &v8::Isolate) -> Rc<RefCell<OpState>> {
636 let state = Self::state_from(isolate);
637 state.op_state.clone()
638 }
639
640 pub(crate) fn has_more_work(scope: &mut v8::HandleScope) -> bool {
641 EventLoopPendingState::new_from_scope(scope).is_pending()
642 }
643
644 fn new_inner(
645 mut options: RuntimeOptions,
646 will_snapshot: bool,
647 ) -> Result<JsRuntime, Error> {
648 let init_mode = InitMode::from_options(&options);
649 let mut extensions = std::mem::take(&mut options.extensions);
650 let mut isolate_allocations = IsolateAllocations::default();
651
652 let mut op_state = OpState::new(options.feature_checker.take());
654 extension_set::setup_op_state(&mut op_state, &mut extensions);
655
656 let mut files_loaded = Vec::with_capacity(128);
658 let mut source_mapper: SourceMapper<Rc<dyn SourceMapGetter>> =
659 SourceMapper::new(options.source_map_getter);
660 let mut sources = extension_set::into_sources(
661 options.extension_transpiler.as_deref(),
662 &extensions,
663 &mut source_mapper,
664 |source| {
665 mark_as_loaded_from_fs_during_snapshot(&mut files_loaded, &source.code)
666 },
667 )?;
668
669 let waker = op_state.waker.clone();
672 let op_state = Rc::new(RefCell::new(op_state));
673 let (eval_context_get_code_cache_cb, eval_context_set_code_cache_cb) =
674 if options.enable_code_cache {
675 options
676 .eval_context_code_cache_cbs
677 .map(|cbs| (Some(cbs.0), Some(cbs.1)))
678 .unwrap_or_default()
679 } else {
680 (None, None)
681 };
682 let state_rc = Rc::new(JsRuntimeState {
683 source_mapper: RefCell::new(source_mapper),
684 shared_array_buffer_store: options.shared_array_buffer_store,
685 compiled_wasm_module_store: options.compiled_wasm_module_store,
686 wait_for_inspector_disconnect_callback: options
687 .wait_for_inspector_disconnect_callback,
688 op_state: op_state.clone(),
689 validate_import_attributes_cb: options.validate_import_attributes_cb,
690 custom_module_evaluation_cb: options.custom_module_evaluation_cb,
691 eval_context_get_code_cache_cb,
692 eval_context_code_cache_ready_cb: eval_context_set_code_cache_cb,
693 waker,
694 inspector: None.into(),
696 has_inspector: false.into(),
697 });
698
699 let op_decls =
702 extension_set::init_ops(crate::ops_builtin::BUILTIN_OPS, &mut extensions);
703
704 let op_driver = Rc::new(OpDriverImpl::default());
705 let op_metrics_factory_fn = options.op_metrics_factory_fn.take();
706 let get_error_class_fn = options.get_error_class_fn.unwrap_or(&|_| "Error");
707
708 let mut op_ctxs = extension_set::create_op_ctxs(
709 op_decls,
710 op_metrics_factory_fn,
711 op_driver.clone(),
712 op_state.clone(),
713 state_rc.clone(),
714 get_error_class_fn,
715 );
716
717 let (
719 global_template_middleware,
720 global_object_middlewares,
721 additional_references,
722 ) = extension_set::get_middlewares_and_external_refs(&mut extensions);
723
724 let extension_count = extensions.len();
726 let op_count = op_ctxs.len();
727 let source_count = sources.len();
728 let addl_refs_count = additional_references.len();
729
730 let (maybe_startup_snapshot, mut sidecar_data) = options
731 .startup_snapshot
732 .take()
733 .map(snapshot::deconstruct)
734 .unzip();
735
736 let ops_in_snapshot = sidecar_data
737 .as_ref()
738 .map(|d| d.snapshot_data.op_count)
739 .unwrap_or_default();
740 let sources_in_snapshot = sidecar_data
741 .as_ref()
742 .map(|d| d.snapshot_data.source_count)
743 .unwrap_or_default();
744
745 let snapshot_sources: Vec<&[u8]> = sidecar_data
746 .as_mut()
747 .map(|s| std::mem::take(&mut s.snapshot_data.external_strings))
748 .unwrap_or_default();
749 (
750 isolate_allocations.externalized_sources,
751 isolate_allocations.original_sources,
752 ) = bindings::externalize_sources(&mut sources, snapshot_sources);
753
754 isolate_allocations.external_refs =
755 Some(Box::new(bindings::create_external_references(
756 &op_ctxs,
757 &additional_references,
758 &isolate_allocations.externalized_sources,
759 ops_in_snapshot,
760 sources_in_snapshot,
761 )));
762
763 let external_refs: &v8::ExternalReferences =
764 isolate_allocations.external_refs.as_ref().unwrap().as_ref();
765 let external_refs_static = unsafe { std::mem::transmute(external_refs) };
767
768 let mut isolate = setup::create_isolate(
769 will_snapshot,
770 options.create_params.take(),
771 maybe_startup_snapshot,
772 external_refs_static,
773 );
774
775 let cpp_heap = setup::init_cppgc(&mut isolate);
776
777 for op_ctx in op_ctxs.iter_mut() {
780 op_ctx.isolate = isolate.as_mut() as *mut Isolate;
781 }
782
783 let isolate_ptr = setup::create_isolate_ptr();
785 isolate = unsafe {
788 isolate_ptr.write(isolate);
789 isolate_ptr.read()
790 };
791 op_state.borrow_mut().put(isolate_ptr);
792
793 let context_state = Rc::new(ContextState::new(
795 op_driver.clone(),
796 isolate_ptr,
797 options.get_error_class_fn.unwrap_or(&|_| "Error"),
798 op_ctxs,
799 ));
800
801 let spawner = context_state
804 .task_spawner_factory
805 .clone()
806 .new_same_thread_spawner();
807 op_state.borrow_mut().put(spawner);
808 let spawner = context_state
809 .task_spawner_factory
810 .clone()
811 .new_cross_thread_spawner();
812 op_state.borrow_mut().put(spawner);
813
814 let mut snapshotted_data = None;
816 let main_context = {
817 let scope = &mut v8::HandleScope::new(&mut isolate);
818
819 let context = create_context(
820 scope,
821 &global_template_middleware,
822 &global_object_middlewares,
823 );
824
825 if let Some(raw_data) = sidecar_data {
827 snapshotted_data = Some(snapshot::load_snapshotted_data_from_snapshot(
828 scope, context, raw_data,
829 ));
830 }
831
832 v8::Global::new(scope, context)
833 };
834
835 let mut context_scope: v8::HandleScope =
836 v8::HandleScope::with_context(&mut isolate, &main_context);
837 let scope = &mut context_scope;
838 let context = v8::Local::new(scope, &main_context);
839
840 if init_mode == InitMode::New {
843 bindings::initialize_deno_core_namespace(scope, context, init_mode);
844 bindings::initialize_primordials_and_infra(scope)?;
845 }
846 if init_mode.needs_ops_bindings() {
849 bindings::initialize_deno_core_ops_bindings(
850 scope,
851 context,
852 &context_state.op_ctxs,
853 );
854 }
855
856 context.set_slot(scope, context_state.clone());
857
858 let inspector = if options.inspector {
859 Some(JsRuntimeInspector::new(scope, context, options.is_main))
860 } else {
861 None
862 };
863
864 let loader = options
868 .module_loader
869 .unwrap_or_else(|| Rc::new(NoopModuleLoader));
870 let import_meta_resolve_cb = options
871 .import_meta_resolve_callback
872 .unwrap_or_else(|| Box::new(default_import_meta_resolve_cb));
873 let exception_state = context_state.exception_state.clone();
874 let module_map = Rc::new(ModuleMap::new(
875 loader,
876 exception_state.clone(),
877 import_meta_resolve_cb,
878 options.enable_code_cache,
879 ));
880
881 if let Some((snapshotted_data, mut data_store)) = snapshotted_data {
882 *exception_state.js_handled_promise_rejection_cb.borrow_mut() =
883 snapshotted_data
884 .js_handled_promise_rejection_cb
885 .map(|cb| data_store.get(scope, cb));
886 module_map.update_with_snapshotted_data(
887 scope,
888 &mut data_store,
889 snapshotted_data.module_map_data,
890 );
891
892 let mut mapper = state_rc.source_mapper.borrow_mut();
893 for (key, map) in snapshotted_data.ext_source_maps {
894 mapper.ext_source_maps.insert(key, map.into());
895 }
896 }
897
898 context.set_slot(scope, module_map.clone());
899
900 let main_realm = {
902 let main_realm =
903 JsRealmInner::new(context_state, main_context, module_map.clone());
904 state_rc.has_inspector.set(inspector.is_some());
906 *state_rc.inspector.borrow_mut() = inspector;
907 main_realm
908 };
909 let main_realm = JsRealm::new(main_realm);
910 scope.set_data(
911 STATE_DATA_OFFSET,
912 Rc::into_raw(state_rc.clone()) as *mut c_void,
913 );
914
915 drop(context_scope);
916
917 let mut js_runtime = JsRuntime {
919 inner: InnerIsolateState {
920 will_snapshot,
921 extension_count,
922 op_count,
923 source_count,
924 addl_refs_count,
925 main_realm: ManuallyDrop::new(main_realm),
926 state: ManuallyDropRc(ManuallyDrop::new(state_rc)),
927 v8_isolate: ManuallyDrop::new(isolate),
928 cpp_heap: ManuallyDrop::new(cpp_heap),
929 },
930 allocations: isolate_allocations,
931 files_loaded_from_fs_during_snapshot: vec![],
932 is_main_runtime: options.is_main,
933 };
934
935 {
938 let realm = JsRealm::clone(&js_runtime.inner.main_realm);
939 let context_global = realm.context();
940 let module_map = realm.0.module_map();
941
942 if init_mode == InitMode::New {
952 js_runtime
953 .execute_virtual_ops_module(context_global, module_map.clone());
954 }
955
956 if init_mode == InitMode::New {
957 js_runtime.execute_builtin_sources(
958 &realm,
959 &module_map,
960 &mut files_loaded,
961 )?;
962 }
963
964 js_runtime.store_js_callbacks(&realm, will_snapshot);
965
966 js_runtime.init_extension_js(&realm, &module_map, sources)?;
967 }
968
969 if will_snapshot {
970 js_runtime.files_loaded_from_fs_during_snapshot = files_loaded;
971 }
972
973 Ok(js_runtime)
975 }
976
977 #[cfg(test)]
978 #[inline]
979 pub(crate) fn module_map(&mut self) -> Rc<ModuleMap> {
980 self.inner.main_realm.0.module_map()
981 }
982
983 #[inline]
984 pub fn main_context(&self) -> v8::Global<v8::Context> {
985 self.inner.main_realm.0.context().clone()
986 }
987
988 #[cfg(test)]
989 pub(crate) fn main_realm(&self) -> JsRealm {
990 JsRealm::clone(&self.inner.main_realm)
991 }
992
993 #[inline]
994 pub fn v8_isolate(&mut self) -> &mut v8::OwnedIsolate {
995 &mut self.inner.v8_isolate
996 }
997
998 #[inline]
999 fn v8_isolate_ptr(&mut self) -> *mut v8::Isolate {
1000 &mut **self.inner.v8_isolate as _
1001 }
1002
1003 #[inline]
1004 pub fn inspector(&mut self) -> Rc<RefCell<JsRuntimeInspector>> {
1005 self.inner.state.inspector()
1006 }
1007
1008 #[inline]
1009 pub fn wait_for_inspector_disconnect(&mut self) {
1010 if let Some(callback) = self
1011 .inner
1012 .state
1013 .wait_for_inspector_disconnect_callback
1014 .as_ref()
1015 {
1016 callback();
1017 }
1018 }
1019
1020 pub fn runtime_activity_stats_factory(&self) -> RuntimeActivityStatsFactory {
1021 RuntimeActivityStatsFactory {
1022 context_state: self.inner.main_realm.0.context_state.clone(),
1023 op_state: self.inner.state.op_state.clone(),
1024 }
1025 }
1026
1027 pub(crate) fn files_loaded_from_fs_during_snapshot(&self) -> &[&'static str] {
1030 &self.files_loaded_from_fs_during_snapshot
1031 }
1032
1033 #[inline]
1034 pub fn handle_scope(&mut self) -> v8::HandleScope {
1035 let isolate = &mut self.inner.v8_isolate;
1036 self.inner.main_realm.handle_scope(isolate)
1037 }
1038
1039 #[inline(always)]
1040 fn with_context_scope<'s, T>(
1042 isolate: *mut v8::Isolate,
1043 context: *mut v8::Context,
1044 f: impl FnOnce(&mut v8::HandleScope<'s>) -> T,
1045 ) -> T {
1046 let mut isolate_scope =
1048 v8::HandleScope::new(unsafe { isolate.as_mut().unwrap_unchecked() });
1049 let context = unsafe { std::mem::transmute(context) };
1052 let mut scope = v8::ContextScope::new(&mut isolate_scope, context);
1053 f(&mut scope)
1054 }
1055
1056 fn execute_virtual_ops_module(
1059 &mut self,
1060 context_global: &v8::Global<v8::Context>,
1061 module_map: Rc<ModuleMap>,
1062 ) {
1063 let scope = &mut self.handle_scope();
1064 let context_local = v8::Local::new(scope, context_global);
1065 let context_state = JsRealm::state_from_scope(scope);
1066 let global = context_local.global(scope);
1067 let synthetic_module_exports = create_exports_for_ops_virtual_module(
1068 &context_state.op_ctxs,
1069 scope,
1070 global,
1071 );
1072 let mod_id = module_map
1073 .new_synthetic_module(
1074 scope,
1075 VIRTUAL_OPS_MODULE_NAME,
1076 crate::ModuleType::JavaScript,
1077 synthetic_module_exports,
1078 )
1079 .unwrap();
1080 module_map.mod_evaluate_sync(scope, mod_id).unwrap();
1081 }
1082
1083 fn execute_builtin_sources(
1090 &mut self,
1091 realm: &JsRealm,
1092 module_map: &Rc<ModuleMap>,
1093 files_loaded: &mut Vec<&'static str>,
1094 ) -> Result<(), Error> {
1095 let scope = &mut realm.handle_scope(self.v8_isolate());
1096
1097 for source_file in &BUILTIN_SOURCES {
1098 let name = source_file.specifier.v8_string(scope);
1099 let source = source_file.source.v8_string(scope);
1100
1101 let origin = bindings::script_origin(scope, name);
1102 let script = v8::Script::compile(scope, source, Some(&origin))
1103 .with_context(|| {
1104 format!("Failed to parse {}", source_file.specifier)
1105 })?;
1106 script.run(scope).with_context(|| {
1107 format!("Failed to execute {}", source_file.specifier)
1108 })?;
1109 }
1110
1111 for file_source in &BUILTIN_ES_MODULES {
1112 mark_as_loaded_from_fs_during_snapshot(files_loaded, &file_source.code);
1113 module_map.lazy_load_es_module_with_code(
1114 scope,
1115 file_source.specifier,
1116 file_source.load()?,
1117 None,
1118 )?;
1119 }
1120
1121 Ok(())
1122 }
1123
1124 async fn init_extension_js_inner(
1126 &mut self,
1127 realm: &JsRealm,
1128 module_map: &Rc<ModuleMap>,
1129 loaded_sources: LoadedSources,
1130 ) -> Result<(), Error> {
1131 for source in loaded_sources.lazy_esm {
1133 module_map.add_lazy_loaded_esm_source(source.specifier, source.code);
1134 }
1135
1136 let loader = module_map.loader.borrow().clone();
1142 let mut modules = Vec::with_capacity(loaded_sources.esm.len());
1143 let mut sources = Vec::with_capacity(loaded_sources.esm.len());
1144 for esm in loaded_sources.esm {
1145 modules.push(ModuleSpecifier::parse(&esm.specifier).unwrap());
1146 sources.push((esm.specifier, esm.code));
1147 }
1148 let ext_loader = Rc::new(ExtModuleLoader::new(sources)?);
1149 *module_map.loader.borrow_mut() = ext_loader.clone();
1150
1151 for module in modules {
1153 realm
1154 .load_side_es_module_from_code(self.v8_isolate(), &module, None)
1155 .await?;
1156 }
1157
1158 for source in loaded_sources.js {
1160 realm.execute_script(self.v8_isolate(), source.specifier, source.code)?;
1161 }
1162
1163 for specifier in loaded_sources.esm_entry_points {
1165 let Some(mod_id) =
1166 module_map.get_id(&specifier, RequestedModuleType::None)
1167 else {
1168 bail!("{} not present in the module map", specifier);
1169 };
1170
1171 let isolate = self.v8_isolate();
1172 let scope = &mut realm.handle_scope(isolate);
1173 module_map.mod_evaluate_sync(scope, mod_id)?;
1174 }
1175
1176 #[cfg(debug_assertions)]
1177 {
1178 let mut scope = realm.handle_scope(self.v8_isolate());
1179 module_map.check_all_modules_evaluated(&mut scope)?;
1180 }
1181
1182 let module_map = realm.0.module_map();
1183 *module_map.loader.borrow_mut() = loader;
1184 Rc::try_unwrap(ext_loader)
1185 .map_err(drop)
1186 .unwrap()
1187 .finalize()?;
1188
1189 Ok(())
1190 }
1191
1192 fn init_extension_js(
1194 &mut self,
1195 realm: &JsRealm,
1196 module_map: &Rc<ModuleMap>,
1197 loaded_sources: LoadedSources,
1198 ) -> Result<(), Error> {
1199 futures::executor::block_on(self.init_extension_js_inner(
1200 realm,
1201 module_map,
1202 loaded_sources,
1203 ))?;
1204
1205 Ok(())
1206 }
1207
1208 pub fn eval<'s, T>(
1209 scope: &mut v8::HandleScope<'s>,
1210 code: &str,
1211 ) -> Option<v8::Local<'s, T>>
1212 where
1213 v8::Local<'s, T>: TryFrom<v8::Local<'s, v8::Value>, Error = v8::DataError>,
1214 {
1215 let scope = &mut v8::EscapableHandleScope::new(scope);
1216 let source = v8::String::new(scope, code).unwrap();
1217 let script = v8::Script::compile(scope, source, None).unwrap();
1218 let v = script.run(scope)?;
1219 scope.escape(v).try_into().ok()
1220 }
1221
1222 fn store_js_callbacks(&mut self, realm: &JsRealm, will_snapshot: bool) {
1225 let (event_loop_tick_cb, build_custom_error_cb, wasm_instantiate_fn) = {
1226 let scope = &mut realm.handle_scope(self.v8_isolate());
1227 let context = realm.context();
1228 let context_local = v8::Local::new(scope, context);
1229 let global = context_local.global(scope);
1230 let deno_obj: v8::Local<v8::Object> =
1233 bindings::get(scope, global, DENO, "Deno");
1234 let core_obj: v8::Local<v8::Object> =
1235 bindings::get(scope, deno_obj, CORE, "Deno.core");
1236
1237 let event_loop_tick_cb: v8::Local<v8::Function> = bindings::get(
1238 scope,
1239 core_obj,
1240 EVENT_LOOP_TICK,
1241 "Deno.core.eventLoopTick",
1242 );
1243 let build_custom_error_cb: v8::Local<v8::Function> = bindings::get(
1244 scope,
1245 core_obj,
1246 BUILD_CUSTOM_ERROR,
1247 "Deno.core.buildCustomError",
1248 );
1249
1250 let mut wasm_instantiate_fn = None;
1251 if !will_snapshot {
1252 let web_assembly_object: v8::Local<v8::Object> =
1253 bindings::get(scope, global, WEBASSEMBLY, "WebAssembly");
1254 wasm_instantiate_fn = Some(bindings::get::<v8::Local<v8::Function>>(
1255 scope,
1256 web_assembly_object,
1257 INSTANTIATE,
1258 "WebAssembly.instantiate",
1259 ));
1260 }
1261
1262 (
1263 v8::Global::new(scope, event_loop_tick_cb),
1264 v8::Global::new(scope, build_custom_error_cb),
1265 wasm_instantiate_fn.map(|f| v8::Global::new(scope, f)),
1266 )
1267 };
1268
1269 let state_rc = realm.0.state();
1271 state_rc
1272 .js_event_loop_tick_cb
1273 .borrow_mut()
1274 .replace(Rc::new(event_loop_tick_cb));
1275 state_rc
1276 .exception_state
1277 .js_build_custom_error_cb
1278 .borrow_mut()
1279 .replace(Rc::new(build_custom_error_cb));
1280 if let Some(wasm_instantiate_fn) = wasm_instantiate_fn {
1281 state_rc
1282 .wasm_instantiate_fn
1283 .borrow_mut()
1284 .replace(Rc::new(wasm_instantiate_fn));
1285 }
1286 }
1287
1288 pub fn op_state(&mut self) -> Rc<RefCell<OpState>> {
1291 self.inner.state.op_state.clone()
1292 }
1293
1294 pub fn op_names(&self) -> Vec<&'static str> {
1296 let state = &self.inner.main_realm.0.context_state;
1297 state.op_ctxs.iter().map(|o| o.decl.name).collect()
1298 }
1299
1300 pub fn execute_script(
1319 &mut self,
1320 name: &'static str,
1321 source_code: impl IntoModuleCodeString,
1322 ) -> Result<v8::Global<v8::Value>, Error> {
1323 let isolate = &mut self.inner.v8_isolate;
1324 self.inner.main_realm.execute_script(
1325 isolate,
1326 FastString::from_static(name),
1327 source_code.into_module_code(),
1328 )
1329 }
1330
1331 pub fn call(
1339 &mut self,
1340 function: &v8::Global<v8::Function>,
1341 ) -> impl Future<Output = Result<v8::Global<v8::Value>, Error>> {
1342 self.call_with_args(function, &[])
1343 }
1344
1345 pub fn scoped_call(
1353 scope: &mut v8::HandleScope,
1354 function: &v8::Global<v8::Function>,
1355 ) -> impl Future<Output = Result<v8::Global<v8::Value>, Error>> {
1356 Self::scoped_call_with_args(scope, function, &[])
1357 }
1358
1359 pub fn call_with_args(
1367 &mut self,
1368 function: &v8::Global<v8::Function>,
1369 args: &[v8::Global<v8::Value>],
1370 ) -> impl Future<Output = Result<v8::Global<v8::Value>, Error>> {
1371 let scope = &mut self.handle_scope();
1372 Self::scoped_call_with_args(scope, function, args)
1373 }
1374
1375 pub fn scoped_call_with_args(
1383 scope: &mut v8::HandleScope,
1384 function: &v8::Global<v8::Function>,
1385 args: &[v8::Global<v8::Value>],
1386 ) -> impl Future<Output = Result<v8::Global<v8::Value>, Error>> {
1387 let scope = &mut v8::TryCatch::new(scope);
1388 let cb = function.open(scope);
1389 let this = v8::undefined(scope).into();
1390 let promise = if args.is_empty() {
1391 cb.call(scope, this, &[])
1392 } else {
1393 let mut local_args: SmallVec<[v8::Local<v8::Value>; 8]> =
1394 SmallVec::with_capacity(args.len());
1395 for v in args {
1396 local_args.push(v8::Local::new(scope, v));
1397 }
1398 cb.call(scope, this, &local_args)
1399 };
1400
1401 if promise.is_none() {
1402 if scope.is_execution_terminating() {
1403 let undefined = v8::undefined(scope).into();
1404 return RcPromiseFuture::new(exception_to_err_result(
1405 scope, undefined, false, true,
1406 ));
1407 }
1408 let exception = scope.exception().unwrap();
1409 return RcPromiseFuture::new(exception_to_err_result(
1410 scope, exception, false, true,
1411 ));
1412 }
1413 let promise = promise.unwrap();
1414 if !promise.is_promise() {
1415 return RcPromiseFuture::new(Ok(v8::Global::new(scope, promise)));
1416 }
1417 let promise = v8::Local::<v8::Promise>::try_from(promise).unwrap();
1418 Self::resolve_promise_inner(scope, promise)
1419 }
1420
1421 #[deprecated = "Use call"]
1425 pub async fn call_and_await(
1426 &mut self,
1427 function: &v8::Global<v8::Function>,
1428 ) -> Result<v8::Global<v8::Value>, Error> {
1429 let call = self.call(function);
1430 self
1431 .with_event_loop_promise(call, PollEventLoopOptions::default())
1432 .await
1433 }
1434
1435 #[deprecated = "Use call_with_args"]
1439 pub async fn call_with_args_and_await(
1440 &mut self,
1441 function: &v8::Global<v8::Function>,
1442 args: &[v8::Global<v8::Value>],
1443 ) -> Result<v8::Global<v8::Value>, Error> {
1444 let call = self.call_with_args(function, args);
1445 self
1446 .with_event_loop_promise(call, PollEventLoopOptions::default())
1447 .await
1448 }
1449
1450 pub fn get_module_namespace(
1455 &mut self,
1456 module_id: ModuleId,
1457 ) -> Result<v8::Global<v8::Object>, Error> {
1458 let isolate = &mut self.inner.v8_isolate;
1459 self
1460 .inner
1461 .main_realm
1462 .get_module_namespace(isolate, module_id)
1463 }
1464
1465 pub fn add_near_heap_limit_callback<C>(&mut self, cb: C)
1471 where
1472 C: FnMut(usize, usize) -> usize + 'static,
1473 {
1474 let boxed_cb = Box::new(RefCell::new(cb));
1475 let data = boxed_cb.as_ptr() as *mut c_void;
1476
1477 let prev = self
1478 .allocations
1479 .near_heap_limit_callback_data
1480 .replace((boxed_cb, near_heap_limit_callback::<C>));
1481 if let Some((_, prev_cb)) = prev {
1482 self
1483 .v8_isolate()
1484 .remove_near_heap_limit_callback(prev_cb, 0);
1485 }
1486
1487 self
1488 .v8_isolate()
1489 .add_near_heap_limit_callback(near_heap_limit_callback::<C>, data);
1490 }
1491
1492 pub fn remove_near_heap_limit_callback(&mut self, heap_limit: usize) {
1493 if let Some((_, cb)) = self.allocations.near_heap_limit_callback_data.take()
1494 {
1495 self
1496 .v8_isolate()
1497 .remove_near_heap_limit_callback(cb, heap_limit);
1498 }
1499 }
1500
1501 fn pump_v8_message_loop(
1502 &mut self,
1503 scope: &mut v8::HandleScope,
1504 ) -> Result<(), Error> {
1505 while v8::Platform::pump_message_loop(
1506 &v8::V8::get_current_platform(),
1507 scope,
1508 false, ) {
1510 }
1512
1513 let tc_scope = &mut v8::TryCatch::new(scope);
1514 tc_scope.perform_microtask_checkpoint();
1515 match tc_scope.exception() {
1516 None => Ok(()),
1517 Some(exception) => {
1518 exception_to_err_result(tc_scope, exception, false, true)
1519 }
1520 }
1521 }
1522
1523 pub fn maybe_init_inspector(&mut self) {
1524 let inspector = &mut self.inner.state.inspector.borrow_mut();
1525 if inspector.is_some() {
1526 return;
1527 }
1528
1529 let context = self.main_context();
1530 let scope = &mut v8::HandleScope::with_context(
1531 self.inner.v8_isolate.as_mut(),
1532 context.clone(),
1533 );
1534 let context = v8::Local::new(scope, context);
1535
1536 self.inner.state.has_inspector.set(true);
1537 **inspector = Some(JsRuntimeInspector::new(
1538 scope,
1539 context,
1540 self.is_main_runtime,
1541 ));
1542 }
1543
1544 pub fn resolve(
1549 &mut self,
1550 promise: v8::Global<v8::Value>,
1551 ) -> impl Future<Output = Result<v8::Global<v8::Value>, Error>> {
1552 let scope = &mut self.handle_scope();
1553 Self::scoped_resolve(scope, promise)
1554 }
1555
1556 pub fn scoped_resolve(
1561 scope: &mut v8::HandleScope,
1562 promise: v8::Global<v8::Value>,
1563 ) -> impl Future<Output = Result<v8::Global<v8::Value>, Error>> {
1564 let promise = v8::Local::new(scope, promise);
1565 if !promise.is_promise() {
1566 return RcPromiseFuture::new(Ok(v8::Global::new(scope, promise)));
1567 }
1568 let promise = v8::Local::<v8::Promise>::try_from(promise).unwrap();
1569 Self::resolve_promise_inner(scope, promise)
1570 }
1571
1572 #[deprecated = "Use resolve"]
1577 pub async fn resolve_value(
1578 &mut self,
1579 global: v8::Global<v8::Value>,
1580 ) -> Result<v8::Global<v8::Value>, Error> {
1581 let resolve = self.resolve(global);
1582 self
1583 .with_event_loop_promise(resolve, PollEventLoopOptions::default())
1584 .await
1585 }
1586
1587 fn resolve_promise_inner<'s>(
1589 scope: &mut v8::HandleScope<'s>,
1590 promise: v8::Local<'s, v8::Promise>,
1591 ) -> RcPromiseFuture {
1592 let future = RcPromiseFuture::default();
1593 let f = future.clone();
1594 watch_promise(scope, promise, move |scope, _rv, res| {
1595 let res = match res {
1596 Ok(l) => Ok(v8::Global::new(scope, l)),
1597 Err(e) => exception_to_err_result(scope, e, true, true),
1598 };
1599 f.0.resolved.set(Some(res));
1600 if let Some(waker) = f.0.waker.take() {
1601 waker.wake();
1602 }
1603 });
1604
1605 future
1606 }
1607
1608 pub async fn run_event_loop(
1616 &mut self,
1617 poll_options: PollEventLoopOptions,
1618 ) -> Result<(), Error> {
1619 poll_fn(|cx| self.poll_event_loop(cx, poll_options)).await
1620 }
1621
1622 pub async fn with_event_loop_promise<'fut, T, E>(
1627 &mut self,
1628 mut fut: impl Future<Output = Result<T, E>> + Unpin + 'fut,
1629 poll_options: PollEventLoopOptions,
1630 ) -> Result<T, AnyError>
1631 where
1632 AnyError: From<E>,
1633 {
1634 poll_fn(|cx| {
1636 if let Poll::Ready(t) = fut.poll_unpin(cx) {
1637 return Poll::Ready(t.map_err(|e| e.into()));
1638 }
1639 if let Poll::Ready(t) = self.poll_event_loop(cx, poll_options) {
1640 t?;
1641 if let Poll::Ready(t) = fut.poll_unpin(cx) {
1642 return Poll::Ready(t.map_err(|e| e.into()));
1643 }
1644 return Poll::Ready(Err(anyhow!("Promise resolution is still pending but the event loop has already resolved.")));
1645 }
1646 Poll::Pending
1647 }).await
1648 }
1649
1650 pub async fn with_event_loop_future<'fut, T, E>(
1657 &mut self,
1658 mut fut: impl Future<Output = Result<T, E>> + Unpin + 'fut,
1659 poll_options: PollEventLoopOptions,
1660 ) -> Result<T, AnyError>
1661 where
1662 AnyError: From<E>,
1663 {
1664 poll_fn(|cx| {
1666 if let Poll::Ready(t) = fut.poll_unpin(cx) {
1667 return Poll::Ready(t.map_err(|e| e.into()));
1668 }
1669 if let Poll::Ready(t) = self.poll_event_loop(cx, poll_options) {
1670 _ = t;
1674 }
1675 Poll::Pending
1676 })
1677 .await
1678 }
1679
1680 pub fn poll_event_loop(
1685 &mut self,
1686 cx: &mut Context,
1687 poll_options: PollEventLoopOptions,
1688 ) -> Poll<Result<(), Error>> {
1689 let isolate = self.v8_isolate_ptr();
1690 Self::with_context_scope(
1691 isolate,
1692 self.inner.main_realm.context_ptr(),
1693 move |scope| self.poll_event_loop_inner(cx, scope, poll_options),
1694 )
1695 }
1696
1697 fn poll_event_loop_inner(
1698 &mut self,
1699 cx: &mut Context,
1700 scope: &mut v8::HandleScope,
1701 poll_options: PollEventLoopOptions,
1702 ) -> Poll<Result<(), Error>> {
1703 let has_inspector = self.inner.state.has_inspector.get();
1704 self.inner.state.waker.register(cx.waker());
1705
1706 if has_inspector {
1707 let _ = self.inspector().borrow().poll_sessions(Some(cx)).unwrap();
1709 }
1710
1711 if poll_options.pump_v8_message_loop {
1712 self.pump_v8_message_loop(scope)?;
1713 }
1714
1715 let realm = &self.inner.main_realm;
1716 let modules = &realm.0.module_map;
1717 let context_state = &realm.0.context_state;
1718 let exception_state = &context_state.exception_state;
1719
1720 modules.poll_progress(cx, scope)?;
1721
1722 let dispatched_ops = Self::do_js_event_loop_tick_realm(
1727 cx,
1728 scope,
1729 context_state,
1730 exception_state,
1731 )?;
1732 exception_state.check_exception_condition(scope)?;
1733
1734 let pending_state =
1736 EventLoopPendingState::new(scope, context_state, modules);
1737
1738 if !pending_state.is_pending() {
1739 if has_inspector {
1740 let inspector = self.inspector();
1741 let has_active_sessions = inspector.borrow().has_active_sessions();
1742 let has_blocking_sessions = inspector.borrow().has_blocking_sessions();
1743
1744 if poll_options.wait_for_inspector && has_active_sessions {
1745 if !has_blocking_sessions {
1749 let context = self.main_context();
1750 inspector.borrow_mut().context_destroyed(scope, context);
1751 self.wait_for_inspector_disconnect();
1752 }
1753
1754 return Poll::Pending;
1755 }
1756 }
1757
1758 return Poll::Ready(Ok(()));
1759 }
1760
1761 #[allow(clippy::suspicious_else_formatting, clippy::if_same_then_else)]
1769 {
1770 if pending_state.has_pending_background_tasks
1771 || pending_state.has_tick_scheduled
1772 || pending_state.has_pending_promise_events
1773 {
1774 self.inner.state.waker.wake();
1775 } else
1776 if (pending_state.has_pending_module_evaluation
1778 || pending_state.has_pending_dyn_module_evaluation)
1779 && dispatched_ops
1780 {
1781 self.inner.state.waker.wake();
1782 }
1783 }
1784
1785 if pending_state.has_pending_module_evaluation {
1786 if pending_state.has_pending_ops
1787 || pending_state.has_pending_dyn_imports
1788 || pending_state.has_pending_dyn_module_evaluation
1789 || pending_state.has_pending_background_tasks
1790 || pending_state.has_tick_scheduled
1791 {
1792 } else {
1794 return Poll::Ready(Err(
1795 find_and_report_stalled_level_await_in_any_realm(scope, &realm.0),
1796 ));
1797 }
1798 }
1799
1800 if pending_state.has_pending_dyn_module_evaluation {
1801 if pending_state.has_pending_ops
1802 || pending_state.has_pending_dyn_imports
1803 || pending_state.has_pending_background_tasks
1804 || pending_state.has_tick_scheduled
1805 {
1806 } else if realm.modules_idle() {
1808 return Poll::Ready(Err(
1809 find_and_report_stalled_level_await_in_any_realm(scope, &realm.0),
1810 ));
1811 } else {
1812 realm.increment_modules_idle();
1816 self.inner.state.waker.wake();
1817 }
1818 }
1819
1820 Poll::Pending
1821 }
1822}
1823
1824fn find_and_report_stalled_level_await_in_any_realm(
1825 scope: &mut v8::HandleScope,
1826 inner_realm: &JsRealmInner,
1827) -> Error {
1828 let module_map = inner_realm.module_map();
1829 let messages = module_map.find_stalled_top_level_await(scope);
1830
1831 if !messages.is_empty() {
1832 let msg = v8::Local::new(scope, &messages[0]);
1837 let js_error = JsError::from_v8_message(scope, msg);
1838 return js_error.into();
1839 }
1840
1841 unreachable!("Expected at least one stalled top-level await");
1842}
1843
1844fn create_context<'a>(
1845 scope: &mut v8::HandleScope<'a, ()>,
1846 global_template_middlewares: &[GlobalTemplateMiddlewareFn],
1847 global_object_middlewares: &[GlobalObjectMiddlewareFn],
1848) -> v8::Local<'a, v8::Context> {
1849 let mut global_object_template = v8::ObjectTemplate::new(scope);
1851 for middleware in global_template_middlewares {
1852 global_object_template = middleware(scope, global_object_template);
1853 }
1854 let context = v8::Context::new_from_template(scope, global_object_template);
1855 let scope = &mut v8::ContextScope::new(scope, context);
1856
1857 let global_wrapper = context.global(scope);
1860 let real_global = global_wrapper
1861 .get_prototype(scope)
1862 .unwrap()
1863 .to_object(scope)
1864 .unwrap();
1865 for middleware in global_object_middlewares {
1866 middleware(scope, real_global);
1867 }
1868 context
1869}
1870
1871impl JsRuntimeForSnapshot {
1872 pub fn new(mut options: RuntimeOptions) -> JsRuntimeForSnapshot {
1873 setup::init_v8(
1874 options.v8_platform.take(),
1875 true,
1876 options.unsafe_expose_natives_and_gc(),
1877 );
1878
1879 let runtime = match JsRuntime::new_inner(options, true) {
1880 Ok(r) => r,
1881 Err(err) => {
1882 panic!("Failed to initialize JsRuntime for snapshotting: {:?}", err);
1883 }
1884 };
1885 JsRuntimeForSnapshot(runtime)
1886 }
1887
1888 pub fn snapshot(mut self) -> Box<[u8]> {
1892 self.inner.prepare_for_cleanup();
1894 let externals_count =
1895 self.0.allocations.external_refs.as_ref().unwrap().len() as _;
1896 let original_sources =
1897 std::mem::take(&mut self.0.allocations.original_sources);
1898 let external_strings = original_sources
1899 .iter()
1900 .map(|s| s.as_str().as_bytes())
1901 .collect();
1902 let realm = JsRealm::clone(&self.inner.main_realm);
1903
1904 {
1906 let mut scope = realm.handle_scope(self.v8_isolate());
1907 let local_context = v8::Local::new(&mut scope, realm.context());
1908 scope.set_default_context(local_context);
1909 }
1910
1911 let source_maps = std::mem::take(
1913 &mut self.inner.state.source_mapper.borrow_mut().ext_source_maps,
1914 );
1915 let mut ext_source_maps = HashMap::with_capacity(source_maps.len());
1916 for (k, v) in &source_maps {
1917 ext_source_maps.insert(k.clone(), v.as_ref());
1918 }
1919
1920 let sidecar_data = {
1926 let mut data_store = SnapshotStoreDataStore::default();
1927 let module_map_data = {
1928 let module_map = realm.0.module_map();
1929 module_map.serialize_for_snapshotting(&mut data_store)
1930 };
1931 let maybe_js_handled_promise_rejection_cb = {
1932 let context_state = &realm.0.context_state;
1933 let exception_state = &context_state.exception_state;
1934 exception_state
1935 .js_handled_promise_rejection_cb
1936 .borrow()
1937 .clone()
1938 }
1939 .map(|cb| data_store.register(cb));
1940
1941 let snapshotted_data = SnapshottedData {
1942 module_map_data,
1943 externals_count,
1944 op_count: self.inner.op_count,
1945 addl_refs_count: self.inner.addl_refs_count,
1946 source_count: self.inner.source_count,
1947 extension_count: self.inner.extension_count,
1948 js_handled_promise_rejection_cb: maybe_js_handled_promise_rejection_cb,
1949 ext_source_maps,
1950 external_strings,
1951 };
1952
1953 let mut scope = realm.handle_scope(self.v8_isolate());
1954 snapshot::store_snapshotted_data_for_snapshot(
1955 &mut scope,
1956 realm.context().clone(),
1957 snapshotted_data,
1958 data_store,
1959 )
1960 };
1961 drop(realm);
1962
1963 let v8_data = self
1964 .0
1965 .inner
1966 .prepare_for_snapshot()
1967 .create_blob(v8::FunctionCodeHandling::Keep)
1968 .unwrap();
1969
1970 snapshot::serialize(v8_data, sidecar_data)
1971 }
1972}
1973
1974#[derive(Clone, Copy, PartialEq, Eq, Debug)]
1975pub(crate) struct EventLoopPendingState {
1976 has_pending_ops: bool,
1977 has_pending_refed_ops: bool,
1978 has_pending_dyn_imports: bool,
1979 has_pending_dyn_module_evaluation: bool,
1980 has_pending_module_evaluation: bool,
1981 has_pending_background_tasks: bool,
1982 has_tick_scheduled: bool,
1983 has_pending_promise_events: bool,
1984}
1985
1986impl EventLoopPendingState {
1987 pub fn new(
1989 scope: &mut v8::HandleScope<()>,
1990 state: &ContextState,
1991 modules: &ModuleMap,
1992 ) -> Self {
1993 let num_unrefed_ops = state.unrefed_ops.borrow().len();
1994 let num_pending_ops = state.pending_ops.len();
1995 let has_pending_tasks = state.task_spawner_factory.has_pending_tasks();
1996 let has_pending_timers = !state.timers.is_empty();
1997 let has_pending_refed_timers = state.timers.has_pending_timers();
1998 let has_pending_dyn_imports = modules.has_pending_dynamic_imports();
1999 let has_pending_dyn_module_evaluation =
2000 modules.has_pending_dyn_module_evaluation();
2001 let has_pending_module_evaluation = modules.has_pending_module_evaluation();
2002 let has_pending_promise_events = !state
2003 .exception_state
2004 .pending_promise_rejections
2005 .borrow()
2006 .is_empty()
2007 || !state
2008 .exception_state
2009 .pending_handled_promise_rejections
2010 .borrow()
2011 .is_empty();
2012 let has_pending_refed_ops = has_pending_tasks
2013 || has_pending_refed_timers
2014 || num_pending_ops > num_unrefed_ops;
2015 EventLoopPendingState {
2016 has_pending_ops: has_pending_refed_ops
2017 || has_pending_timers
2018 || (num_pending_ops > 0),
2019 has_pending_refed_ops,
2020 has_pending_dyn_imports,
2021 has_pending_dyn_module_evaluation,
2022 has_pending_module_evaluation,
2023 has_pending_background_tasks: scope.has_pending_background_tasks(),
2024 has_tick_scheduled: state.has_next_tick_scheduled.get(),
2025 has_pending_promise_events,
2026 }
2027 }
2028
2029 pub fn new_from_scope(scope: &mut v8::HandleScope) -> Self {
2031 let module_map = JsRealm::module_map_from(scope);
2032 let context_state = JsRealm::state_from_scope(scope);
2033 Self::new(scope, &context_state, &module_map)
2034 }
2035
2036 pub fn is_pending(&self) -> bool {
2037 self.has_pending_refed_ops
2038 || self.has_pending_dyn_imports
2039 || self.has_pending_dyn_module_evaluation
2040 || self.has_pending_module_evaluation
2041 || self.has_pending_background_tasks
2042 || self.has_tick_scheduled
2043 || self.has_pending_promise_events
2044 }
2045}
2046
2047extern "C" fn near_heap_limit_callback<F>(
2048 data: *mut c_void,
2049 current_heap_limit: usize,
2050 initial_heap_limit: usize,
2051) -> usize
2052where
2053 F: FnMut(usize, usize) -> usize,
2054{
2055 let callback = unsafe { &mut *(data as *mut F) };
2058 callback(current_heap_limit, initial_heap_limit)
2059}
2060
2061impl JsRuntimeState {
2062 pub(crate) fn inspector(&self) -> Rc<RefCell<JsRuntimeInspector>> {
2063 self.inspector.borrow().as_ref().unwrap().clone()
2064 }
2065
2066 pub fn notify_new_dynamic_import(&self) {
2069 self.waker.wake();
2071 }
2072
2073 pub(crate) fn with_inspector<T>(
2075 &self,
2076 mut f: impl FnMut(&JsRuntimeInspector) -> T,
2077 ) -> Option<T> {
2078 if !self.has_inspector.get() {
2080 return None;
2081 }
2082 self
2083 .inspector
2084 .borrow()
2085 .as_ref()
2086 .map(|inspector| f(&inspector.borrow()))
2087 }
2088}
2089
2090impl JsRuntime {
2092 #[cfg(test)]
2093 pub(crate) fn instantiate_module(
2094 &mut self,
2095 id: ModuleId,
2096 ) -> Result<(), v8::Global<v8::Value>> {
2097 let isolate = &mut self.inner.v8_isolate;
2098 let realm = JsRealm::clone(&self.inner.main_realm);
2099 let scope = &mut realm.handle_scope(isolate);
2100 realm.instantiate_module(scope, id)
2101 }
2102
2103 pub fn mod_evaluate(
2121 &mut self,
2122 id: ModuleId,
2123 ) -> impl Future<Output = Result<(), Error>> {
2124 let isolate = &mut self.inner.v8_isolate;
2125 let realm = &self.inner.main_realm;
2126 let scope = &mut realm.handle_scope(isolate);
2127 self.inner.main_realm.0.module_map.mod_evaluate(scope, id)
2128 }
2129
2130 pub async fn load_main_es_module_from_code(
2143 &mut self,
2144 specifier: &ModuleSpecifier,
2145 code: impl IntoModuleCodeString,
2146 ) -> Result<ModuleId, Error> {
2147 let isolate = &mut self.inner.v8_isolate;
2148 self
2149 .inner
2150 .main_realm
2151 .load_main_es_module_from_code(
2152 isolate,
2153 specifier,
2154 Some(code.into_module_code()),
2155 )
2156 .await
2157 }
2158
2159 pub async fn load_main_es_module(
2168 &mut self,
2169 specifier: &ModuleSpecifier,
2170 ) -> Result<ModuleId, Error> {
2171 let isolate = &mut self.inner.v8_isolate;
2172 self
2173 .inner
2174 .main_realm
2175 .load_main_es_module_from_code(isolate, specifier, None)
2176 .await
2177 }
2178
2179 pub async fn load_side_es_module_from_code(
2193 &mut self,
2194 specifier: &ModuleSpecifier,
2195 code: impl IntoModuleCodeString,
2196 ) -> Result<ModuleId, Error> {
2197 let isolate = &mut self.inner.v8_isolate;
2198 self
2199 .inner
2200 .main_realm
2201 .load_side_es_module_from_code(
2202 isolate,
2203 specifier,
2204 Some(code.into_module_code()),
2205 )
2206 .await
2207 }
2208
2209 pub async fn load_side_es_module(
2218 &mut self,
2219 specifier: &ModuleSpecifier,
2220 ) -> Result<ModuleId, Error> {
2221 let isolate = &mut self.inner.v8_isolate;
2222 self
2223 .inner
2224 .main_realm
2225 .load_side_es_module_from_code(isolate, specifier, None)
2226 .await
2227 }
2228
2229 pub fn lazy_load_es_module_with_code(
2237 &mut self,
2238 specifier: impl IntoModuleName,
2239 code: impl IntoModuleCodeString,
2240 ) -> Result<v8::Global<v8::Value>, Error> {
2241 let isolate = &mut self.inner.v8_isolate;
2242 self.inner.main_realm.lazy_load_es_module_with_code(
2243 isolate,
2244 specifier.into_module_name(),
2245 code.into_module_code(),
2246 )
2247 }
2248
2249 fn do_js_event_loop_tick_realm(
2250 cx: &mut Context,
2251 scope: &mut v8::HandleScope,
2252 context_state: &ContextState,
2253 exception_state: &ExceptionState,
2254 ) -> Result<bool, Error> {
2255 let mut dispatched_ops = false;
2256
2257 let tasks = context_state.task_spawner_factory.poll_inner(cx);
2260 if let Poll::Ready(tasks) = tasks {
2261 dispatched_ops = true;
2263 for task in tasks {
2264 task(scope);
2265 }
2266 }
2267
2268 const MAX_VEC_SIZE_FOR_OPS: usize = 1024;
2272
2273 let mut args: SmallVec<[v8::Local<v8::Value>; 32]> =
2280 SmallVec::with_capacity(32);
2281
2282 loop {
2283 if args.len() >= MAX_VEC_SIZE_FOR_OPS {
2284 cx.waker().wake_by_ref();
2286 break;
2287 }
2288
2289 let Poll::Ready((promise_id, op_id, res)) =
2290 context_state.pending_ops.poll_ready(cx)
2291 else {
2292 break;
2293 };
2294
2295 let res = res.unwrap(scope, context_state.get_error_class_fn);
2296
2297 {
2298 let op_ctx = &context_state.op_ctxs[op_id as usize];
2299 if op_ctx.metrics_enabled() {
2300 if res.is_ok() {
2301 dispatch_metrics_async(op_ctx, OpMetricsEvent::CompletedAsync);
2302 } else {
2303 dispatch_metrics_async(op_ctx, OpMetricsEvent::ErrorAsync);
2304 }
2305 }
2306 }
2307
2308 context_state.unrefed_ops.borrow_mut().remove(&promise_id);
2309 context_state
2310 .activity_traces
2311 .complete(RuntimeActivityType::AsyncOp, promise_id as _);
2312 dispatched_ops |= true;
2313 args.push(v8::Integer::new(scope, promise_id).into());
2314 args.push(res.unwrap_or_else(std::convert::identity));
2315 }
2316
2317 let undefined: v8::Local<v8::Value> = v8::undefined(scope).into();
2318 let has_tick_scheduled = context_state.has_next_tick_scheduled.get();
2319 dispatched_ops |= has_tick_scheduled;
2320
2321 while let Some((promise, result)) = exception_state
2322 .pending_handled_promise_rejections
2323 .borrow_mut()
2324 .pop_front()
2325 {
2326 if let Some(handler) = exception_state
2327 .js_handled_promise_rejection_cb
2328 .borrow()
2329 .as_ref()
2330 {
2331 let function = handler.open(scope);
2332
2333 let args = [
2334 v8::Local::new(scope, promise).into(),
2335 v8::Local::new(scope, result),
2336 ];
2337 function.call(scope, undefined, &args);
2338 }
2339 }
2340
2341 let rejections = if !exception_state
2342 .pending_promise_rejections
2343 .borrow_mut()
2344 .is_empty()
2345 {
2346 let mut pending_rejections =
2348 exception_state.pending_promise_rejections.borrow_mut();
2349 let mut rejections = VecDeque::default();
2350 std::mem::swap(&mut *pending_rejections, &mut rejections);
2351 drop(pending_rejections);
2352
2353 let arr = v8::Array::new(scope, (rejections.len() * 2) as i32);
2354 let mut index = 0;
2355 for rejection in rejections.into_iter() {
2356 let value = v8::Local::new(scope, rejection.0);
2357 arr.set_index(scope, index, value.into());
2358 index += 1;
2359 let value = v8::Local::new(scope, rejection.1);
2360 arr.set_index(scope, index, value);
2361 index += 1;
2362 }
2363 arr.into()
2364 } else {
2365 undefined
2366 };
2367
2368 args.push(rejections);
2369
2370 let timers =
2373 if let Poll::Ready(timers) = context_state.timers.poll_timers(cx) {
2374 let traces_enabled = context_state.activity_traces.is_enabled();
2375 let arr = v8::Array::new(scope, (timers.len() * 3) as _);
2376 #[allow(clippy::needless_range_loop)]
2377 for i in 0..timers.len() {
2378 if traces_enabled {
2379 context_state
2381 .activity_traces
2382 .complete(RuntimeActivityType::Timer, timers[i].0 as _);
2383 }
2384 let value = v8::Integer::new(scope, timers[i].1 .1 as _);
2386 arr.set_index(scope, (i * 3) as _, value.into());
2387 let value = v8::Number::new(scope, timers[i].0 as _);
2388 arr.set_index(scope, (i * 3 + 1) as _, value.into());
2389 let value = v8::Local::new(scope, timers[i].1 .0.clone());
2390 arr.set_index(scope, (i * 3 + 2) as _, value.into());
2391 }
2392 arr.into()
2393 } else {
2394 undefined
2395 };
2396 args.push(timers);
2397
2398 let has_tick_scheduled = v8::Boolean::new(scope, has_tick_scheduled);
2399 args.push(has_tick_scheduled.into());
2400
2401 let tc_scope = &mut v8::TryCatch::new(scope);
2402 let js_event_loop_tick_cb = context_state.js_event_loop_tick_cb.borrow();
2403 let js_event_loop_tick_cb =
2404 js_event_loop_tick_cb.as_ref().unwrap().open(tc_scope);
2405
2406 js_event_loop_tick_cb.call(tc_scope, undefined, args.as_slice());
2407
2408 if let Some(exception) = tc_scope.exception() {
2409 return exception_to_err_result(tc_scope, exception, false, true);
2410 }
2411
2412 if tc_scope.has_terminated() || tc_scope.is_execution_terminating() {
2413 return Ok(false);
2414 }
2415
2416 Ok(dispatched_ops)
2417 }
2418}
2419
2420fn mark_as_loaded_from_fs_during_snapshot(
2421 files_loaded: &mut Vec<&'static str>,
2422 source: &ExtensionFileSourceCode,
2423) {
2424 #[allow(deprecated)]
2425 if let ExtensionFileSourceCode::LoadedFromFsDuringSnapshot(path) = source {
2426 files_loaded.push(path);
2427 }
2428}