rw_deno_core/runtime/
jsruntime.rs

1// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
2use 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/// Objects that need to live as long as the isolate
98#[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
107/// ManuallyDrop<Rc<...>> is clone, but it returns a ManuallyDrop<Rc<...>> which is a massive
108/// memory-leak footgun.
109pub(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
131/// This struct contains the [`JsRuntimeState`] and [`v8::OwnedIsolate`] that are required
132/// to do an orderly shutdown of V8. We keep these in a separate struct to allow us to control
133/// the destruction more closely, as snapshots require the isolate to be destroyed by the
134/// snapshot process, not the destructor.
135///
136/// The way rusty_v8 works w/snapshots is that the [`v8::OwnedIsolate`] gets consumed by a
137/// [`v8::snapshot::SnapshotCreator`] that is stored in its annex. It's a bit awkward, because this
138/// means we cannot let it drop (because we don't have it after a snapshot). On top of that, we have
139/// to consume it in the snapshot creator because otherwise it panics.
140///
141/// This inner struct allows us to let the outer JsRuntime drop normally without a Drop impl, while we
142/// control dropping more closely here using ManuallyDrop.
143pub(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  /// Clean out the opstate and take the inspector to prevent the inspector from getting destroyed
157  /// after we've torn down the contexts. If the inspector is not correctly torn down, random crashes
158  /// happen in tests (and possibly for users using the inspector).
159  pub fn prepare_for_cleanup(&mut self) {
160    // Explicitly shut down the op driver here, just in case there are other references to it
161    // that prevent it from dropping after we invalidate the state.
162    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    // SAFETY: We are sure that it's a valid pointer for whole lifetime of
179    // the runtime.
180    _ = 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    // SAFETY: We're copying out of self and then immediately forgetting self
192    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    // SAFETY: We gotta drop these
209    unsafe {
210      ManuallyDrop::drop(&mut self.state.0);
211      if self.will_snapshot {
212        // Create the snapshot and just drop it.
213        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  /// We have no snapshot -- this is a pristine context.
225  New,
226  /// We are using a snapshot, thus certain initialization steps are skipped.
227  FromSnapshot {
228    // Can we skip the work of op registration?
229    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
301/// These files are executed just after a new context is created. They provided
302/// the necessary infrastructure to bind ops.
303pub(crate) static CONTEXT_SETUP_SOURCES: [InternalSourceFile; 2] = [
304  internal_source_file!("00_primordials.js"),
305  internal_source_file!("00_infra.js"),
306];
307
308/// These files are executed when we start setting up extensions. They rely
309/// on ops being already fully set up.
310pub(crate) static BUILTIN_SOURCES: [InternalSourceFile; 2] = [
311  internal_source_file!("01_core.js"),
312  internal_source_file!("02_error.js"),
313];
314
315/// Executed after `BUILTIN_SOURCES` are executed. Provides a thin ES module
316/// that exports `core`, `internals` and `primordials` objects.
317pub(crate) static BUILTIN_ES_MODULES: [ExtensionFileSource; 1] =
318  include_js_files!(core "mod.js",);
319
320/// We have `ext:core/ops` and `ext:core/mod.js` that are always provided.
321#[cfg(test)]
322pub(crate) const NO_OF_BUILTIN_MODULES: usize = 2;
323
324/// A single execution context of JavaScript. Corresponds roughly to the "Web
325/// Worker" concept in the DOM.
326///
327/// The JsRuntime future completes when there is an error or when all
328/// pending ops have completed.
329///
330/// Use [`JsRuntimeForSnapshot`] to be able to create a snapshot.
331///
332/// Note: since V8 11.6, all runtimes must have a common parent thread that
333/// initalized the V8 platform. This can be done by calling
334/// [`JsRuntime::init_platform`] explicitly, or it will be done automatically on
335/// the calling thread when the first runtime is created.
336pub struct JsRuntime {
337  pub(crate) inner: InnerIsolateState,
338  pub(crate) allocations: IsolateAllocations,
339  // Contains paths of source files that were executed in
340  // [`JsRuntime::init_extension_js`]. This field is populated only if a
341  // snapshot is being created.
342  files_loaded_from_fs_during_snapshot: Vec<&'static str>,
343  // Marks if this is considered the top-level runtime. Used only by inspector.
344  is_main_runtime: bool,
345}
346
347/// The runtime type used for snapshot creation.
348pub 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
406/// Internal state for JsRuntime which is stored in one of v8::Isolate's
407/// embedder slots.
408pub 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  /// Accessed through [`JsRuntimeState::with_inspector`].
422  inspector: RefCell<Option<Rc<RefCell<JsRuntimeInspector>>>>,
423  has_inspector: Cell<bool>,
424}
425
426#[derive(Default)]
427pub struct RuntimeOptions {
428  /// Source map reference for errors.
429  pub source_map_getter: Option<Rc<dyn SourceMapGetter>>,
430
431  /// Allows to map error type to a string "class" used to represent
432  /// error in JavaScript.
433  pub get_error_class_fn: Option<GetErrorClassFn>,
434
435  /// Implementation of `ModuleLoader` which will be
436  /// called when V8 requests to load ES modules in the main realm.
437  ///
438  /// If not provided runtime will error if code being
439  /// executed tries to load modules.
440  pub module_loader: Option<Rc<dyn ModuleLoader>>,
441
442  /// If specified, transpiles extensions before loading.
443  pub extension_transpiler: Option<Rc<ExtensionTranspiler>>,
444
445  /// Provide a function that may optionally provide a metrics collector
446  /// for a given op.
447  pub op_metrics_factory_fn: Option<OpMetricsFactoryFn>,
448
449  /// JsRuntime extensions, not to be confused with ES modules.
450  /// Only ops registered by extensions will be initialized. If you need
451  /// to execute JS code from extensions, pass source files in `js` or `esm`
452  /// option on `ExtensionBuilder`.
453  ///
454  /// If you are creating a runtime from a snapshot take care not to include
455  /// JavaScript sources in the extensions.
456  pub extensions: Vec<Extension>,
457
458  /// V8 snapshot that should be loaded on startup.
459  ///
460  /// For testing, use `runtime.snapshot()` and then [`Box::leak`] to acquire
461  // a static slice.
462  pub startup_snapshot: Option<&'static [u8]>,
463
464  /// Should op registration be skipped?
465  pub skip_op_registration: bool,
466
467  /// Isolate creation parameters.
468  pub create_params: Option<v8::CreateParams>,
469
470  /// V8 platform instance to use. Used when Deno initializes V8
471  /// (which it only does once), otherwise it's silently dropped.
472  pub v8_platform: Option<v8::SharedRef<v8::Platform>>,
473
474  /// The store to use for transferring SharedArrayBuffers between isolates.
475  /// If multiple isolates should have the possibility of sharing
476  /// SharedArrayBuffers, they should use the same [SharedArrayBufferStore]. If
477  /// no [SharedArrayBufferStore] is specified, SharedArrayBuffer can not be
478  /// serialized.
479  pub shared_array_buffer_store: Option<SharedArrayBufferStore>,
480
481  /// The store to use for transferring `WebAssembly.Module` objects between
482  /// isolates.
483  /// If multiple isolates should have the possibility of sharing
484  /// `WebAssembly.Module` objects, they should use the same
485  /// [CompiledWasmModuleStore]. If no [CompiledWasmModuleStore] is specified,
486  /// `WebAssembly.Module` objects cannot be serialized.
487  pub compiled_wasm_module_store: Option<CompiledWasmModuleStore>,
488
489  /// Start inspector instance to allow debuggers to connect.
490  pub inspector: bool,
491
492  /// Describe if this is the main runtime instance, used by debuggers in some
493  /// situation - like disconnecting when program finishes running.
494  pub is_main: bool,
495
496  #[cfg(any(test, feature = "unsafe_runtime_options"))]
497  /// Should this isolate expose the v8 natives (eg: %OptimizeFunctionOnNextCall) and
498  /// GC control functions (`gc()`)? WARNING: This should not be used for production code as
499  /// this may expose the runtime to security vulnerabilities.
500  pub unsafe_expose_natives_and_gc: bool,
501
502  /// An optional instance of `FeatureChecker`. If one is not provided, the
503  /// default instance will be created that has no features enabled.
504  pub feature_checker: Option<Arc<FeatureChecker>>,
505
506  /// A callback that can be used to validate import attributes received at
507  /// the import site. If no callback is provided, all attributes are allowed.
508  ///
509  /// Embedders might use this callback to eg. validate value of "type"
510  /// attribute, not allowing other types than "JSON".
511  ///
512  /// To signal validation failure, users should throw an V8 exception inside
513  /// the callback.
514  pub validate_import_attributes_cb: Option<ValidateImportAttributesCb>,
515
516  /// A callback that can be used to customize behavior of
517  /// `import.meta.resolve()` API. If no callback is provided, a default one
518  /// is used. The default callback returns value of
519  /// `RuntimeOptions::module_loader::resolve()` call.
520  pub import_meta_resolve_callback: Option<ImportMetaResolveCallback>,
521
522  /// A callback that is called when the event loop has no more work to do,
523  /// but there are active, non-blocking inspector session (eg. Chrome
524  /// DevTools inspector is connected). The embedder can use this callback
525  /// to eg. print a message notifying user about program finished running.
526  /// This callback can be called multiple times, eg. after the program finishes
527  /// more work can be scheduled from the DevTools.
528  pub wait_for_inspector_disconnect_callback:
529    Option<WaitForInspectorDisconnectCallback>,
530
531  /// A callback that allows to evaluate a custom type of a module - eg.
532  /// embedders might implement loading WASM or test modules.
533  pub custom_module_evaluation_cb: Option<CustomModuleEvaluationCb>,
534
535  // Controls whether V8 code cache is enabled. Code cache can be applied
536  // to ES modules (loaded through `ModuleLoader`) and to scripts evaluated
537  // through `Deno.core.evalContext`.
538  pub enable_code_cache: bool,
539
540  /// Callbacks to retrieve and store code cache for scripts evaluated
541  /// through evalContext.
542  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  /// Implementation of `ModuleLoader` which will be
576  /// called when V8 requests to load ES modules in the realm.
577  ///
578  /// If not provided, there will be an error if code being
579  /// executed tries to load modules from the realm.
580  pub module_loader: Option<Rc<dyn ModuleLoader>>,
581}
582
583impl JsRuntime {
584  /// Explicitly initalizes the V8 platform using the passed platform. This
585  /// should only be called once per process. Further calls will be silently
586  /// ignored.
587  #[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  /// Explicitly initalizes the V8 platform using the passed platform. This
593  /// should only be called once per process. Further calls will be silently
594  /// ignored.
595  ///
596  /// The `expose_natives` flag is used to expose the v8 natives
597  /// (eg: %OptimizeFunctionOnNextCall) and GC control functions (`gc()`).
598  /// WARNING: This should not be used for production code as
599  /// this may expose the runtime to security vulnerabilities.
600  #[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  /// Only constructor, configuration is done through `options`.
609  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      // SAFETY: We are sure that it's a valid pointer for whole lifetime of
627      // the runtime.
628      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  /// Returns the `OpState` associated with the passed `Isolate`.
635  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    // First let's create an `OpState` and contribute to it from extensions...
653    let mut op_state = OpState::new(options.feature_checker.take());
654    extension_set::setup_op_state(&mut op_state, &mut extensions);
655
656    // Load the sources and source maps
657    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    // ...now let's set up ` JsRuntimeState`, we'll need to set some fields
670    // later, after `JsRuntime` is all set up...
671    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      // Some fields are initialized later after isolate is created
695      inspector: None.into(),
696      has_inspector: false.into(),
697    });
698
699    // ...now we're moving on to ops; set them up, create `OpCtx` for each op
700    // and get ready to actually create V8 isolate...
701    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    // ...ops are now almost fully set up; let's create a V8 isolate...
718    let (
719      global_template_middleware,
720      global_object_middlewares,
721      additional_references,
722    ) = extension_set::get_middlewares_and_external_refs(&mut extensions);
723
724    // Capture the extension, op and source counts
725    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    // SAFETY: We attach external_refs to IsolateAllocations which will live as long as the isolate
766    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    // ...isolate is fully set up, we can forward its pointer to the ops to finish
778    // their' setup...
779    for op_ctx in op_ctxs.iter_mut() {
780      op_ctx.isolate = isolate.as_mut() as *mut Isolate;
781    }
782
783    // TODO(Bartlomieju): this can be simplified
784    let isolate_ptr = setup::create_isolate_ptr();
785    // SAFETY: this is first use of `isolate_ptr` so we are sure we're
786    // not overwriting an existing pointer.
787    isolate = unsafe {
788      isolate_ptr.write(isolate);
789      isolate_ptr.read()
790    };
791    op_state.borrow_mut().put(isolate_ptr);
792
793    // ...once ops and isolate are set up, we can create a `ContextState`...
794    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    // TODO(bartlomieju): factor out
802    // Add the task spawners to the OpState
803    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    // ...and with `ContextState` available we can set up V8 context...
815    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      // Get module map data from the snapshot
826      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    // ...followed by creation of `Deno.core` namespace, as well as internal
841    // infrastructure to provide JavaScript bindings for ops...
842    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 we're creating a new runtime or there are new ops to register
847    // set up JavaScript bindings for them.
848    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    // ...now that JavaScript bindings to ops are available we can deserialize
865    // modules stored in the snapshot (because they depend on the ops and external
866    // references must match properly) and recreate a module map...
867    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    // ...we are ready to create a "realm" for the context...
901    let main_realm = {
902      let main_realm =
903        JsRealmInner::new(context_state, main_context, module_map.clone());
904      // TODO(bartlomieju): why is this done in here? Maybe we can hoist it out?
905      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    // ...which allows us to create the `JsRuntime` instance...
918    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    // ...we're almost done with the setup, all that's left is to execute
936    // internal JS and then execute code provided by extensions...
937    {
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      // TODO(bartlomieju): this is somewhat duplicated in `bindings::initialize_context`,
943      // but for migration period we need to have ops available in both `Deno.core.ops`
944      // as well as have them available in "virtual ops module"
945      // if !matches!(
946      //   self.init_mode,
947      //   InitMode::FromSnapshot {
948      //     skip_op_registration: true
949      //   }
950      // ) {
951      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    // ...and we've made it; `JsRuntime` is ready to execute user code.
974    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  // TODO(bartlomieju): remove, instead `JsRuntimeForSnapshot::new` should return
1028  // a struct that contains this data.
1029  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  /// Create a scope on the stack with the given context
1041  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    // SAFETY: We know this isolate is valid and non-null at this time
1047    let mut isolate_scope =
1048      v8::HandleScope::new(unsafe { isolate.as_mut().unwrap_unchecked() });
1049    // SAFETY: We know the context is valid and non-null at this time, and that a Local and pointer share the
1050    // same representation
1051    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  /// Create a synthetic module - `ext:core/ops` - that exports all ops registered
1057  /// with the runtime.
1058  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  /// Executes built-in scripts and ES modules - this code is required for
1084  /// ops system to work properly, as well as providing necessary bindings
1085  /// on the `Deno.core` namespace.
1086  ///
1087  /// This is not done in [`bindings::initialize_primordials_and_infra`] because
1088  /// some of this code already relies on certain ops being available.
1089  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  /// Initializes JS of provided Extensions in the given realm.
1125  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    // First, add all the lazy ESM
1132    for source in loaded_sources.lazy_esm {
1133      module_map.add_lazy_loaded_esm_source(source.specifier, source.code);
1134    }
1135
1136    // Temporarily override the loader of the `ModuleMap` so we can load
1137    // extension code.
1138
1139    // TODO(bartlomieju): maybe this should be a method on the `ModuleMap`,
1140    // instead of explicitly changing the `.loader` field?
1141    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    // Next, load the extension modules as side modules (but do not execute them)
1152    for module in modules {
1153      realm
1154        .load_side_es_module_from_code(self.v8_isolate(), &module, None)
1155        .await?;
1156    }
1157
1158    // Execute extension scripts
1159    for source in loaded_sources.js {
1160      realm.execute_script(self.v8_isolate(), source.specifier, source.code)?;
1161    }
1162
1163    // ...then execute all entry points
1164    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  /// Initializes JS of provided Extensions in the given realm.
1193  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  /// Grab and store JavaScript bindings to callbacks necessary for the
1223  /// JsRuntime to operate properly.
1224  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      // TODO(bartlomieju): these probably could be captured from main realm so we don't have to
1231      // look up them again?
1232      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    // Put global handles in the realm's ContextState
1270    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  /// Returns the runtime's op state, which can be used to maintain ops
1289  /// and access resources between op calls.
1290  pub fn op_state(&mut self) -> Rc<RefCell<OpState>> {
1291    self.inner.state.op_state.clone()
1292  }
1293
1294  /// Returns the runtime's op names, ordered by OpId.
1295  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  /// Executes traditional, non-ECMAScript-module JavaScript code, This code executes in
1301  /// the global scope by default, and it is possible to maintain local JS state and invoke
1302  /// this method multiple times.
1303  ///
1304  /// `name` can be a filepath or any other string, but it is required to be 7-bit ASCII, eg.
1305  ///
1306  ///   - "/some/file/path.js"
1307  ///   - "<anon>"
1308  ///   - "[native code]"
1309  ///
1310  /// The same `name` value can be used for multiple executions.
1311  ///
1312  /// The source may be any type that implements the internal [`IntoModuleCodeString`] trait, but
1313  /// it is highly recommended that embedders use the [`ascii_str!`] to generate the fastest version
1314  /// of strings for v8 to handle. If the strings are not static, you may also pass a [`String`]
1315  /// generated by the [`format!`] macro.
1316  ///
1317  /// `Error` can usually be downcast to `JsError`.
1318  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  /// Call a function and return a future resolving with the return value of the
1332  /// function. If the function returns a promise, the future will resolve only once the
1333  /// event loop resolves the underlying promise. If the future rejects, the future will
1334  /// resolve with the underlying error.
1335  ///
1336  /// The event loop must be polled seperately for this future to resolve. If the event loop
1337  /// is not polled, the future will never make progress.
1338  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  /// Call a function and returns a future resolving with the return value of the
1346  /// function. If the function returns a promise, the future will resolve only once the
1347  /// event loop resolves the underlying promise. If the future rejects, the future will
1348  /// resolve with the underlying error.
1349  ///
1350  /// The event loop must be polled seperately for this future to resolve. If the event loop
1351  /// is not polled, the future will never make progress.
1352  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  /// Call a function and returns a future resolving with the return value of the
1360  /// function. If the function returns a promise, the future will resolve only once the
1361  /// event loop resolves the underlying promise. If the future rejects, the future will
1362  /// resolve with the underlying error.
1363  ///
1364  /// The event loop must be polled seperately for this future to resolve. If the event loop
1365  /// is not polled, the future will never make progress.
1366  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  /// Call a function and returns a future resolving with the return value of the
1376  /// function. If the function returns a promise, the future will resolve only once the
1377  /// event loop resolves the underlying promise. If the future rejects, the future will
1378  /// resolve with the underlying error.
1379  ///
1380  /// The event loop must be polled seperately for this future to resolve. If the event loop
1381  /// is not polled, the future will never make progress.
1382  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  /// Call a function. If it returns a promise, run the event loop until that
1422  /// promise is settled. If the promise rejects or there is an uncaught error
1423  /// in the event loop, return `Err(error)`. Or return `Ok(<await returned>)`.
1424  #[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  /// Call a function with args. If it returns a promise, run the event loop until that
1436  /// promise is settled. If the promise rejects or there is an uncaught error
1437  /// in the event loop, return `Err(error)`. Or return `Ok(<await returned>)`.
1438  #[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  /// Returns the namespace object of a module.
1451  ///
1452  /// This is only available after module evaluation has completed.
1453  /// This function panics if module has not been instantiated.
1454  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  /// Registers a callback on the isolate when the memory limits are approached.
1466  /// Use this to prevent V8 from crashing the process when reaching the limit.
1467  ///
1468  /// Calls the closure with the current heap limit and the initial heap limit.
1469  /// The return value of the closure is set as the new limit.
1470  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, // don't block if there are no tasks
1509    ) {
1510      // do nothing
1511    }
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  /// Waits for the given value to resolve while polling the event loop.
1545  ///
1546  /// This future resolves when either the value is resolved or the event loop runs to
1547  /// completion.
1548  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  /// Waits for the given value to resolve while polling the event loop.
1557  ///
1558  /// This future resolves when either the value is resolved or the event loop runs to
1559  /// completion.
1560  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  /// Waits for the given value to resolve while polling the event loop.
1573  ///
1574  /// This future resolves when either the value is resolved or the event loop runs to
1575  /// completion.
1576  #[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  /// Given a promise, returns a future that resolves when it does.
1588  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  /// Runs event loop to completion
1609  ///
1610  /// This future resolves when:
1611  ///  - there are no more pending dynamic imports
1612  ///  - there are no more pending ops
1613  ///  - there are no more active inspector sessions (only if
1614  ///     `PollEventLoopOptions.wait_for_inspector` is set to true)
1615  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  /// A utility function that run provided future concurrently with the event loop.
1623  ///
1624  /// If the event loop resolves while polling the future, it return an error with the text
1625  /// `Promise resolution is still pending but the event loop has already resolved.`
1626  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    // Manually implement tokio::select
1635    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  /// A utility function that run provided future concurrently with the event loop.
1651  ///
1652  /// If the event loop resolves while polling the future, it will continue to be polled,
1653  /// regardless of whether it returned an error or success.
1654  ///
1655  /// Useful for interacting with local inspector session.
1656  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    // Manually implement tokio::select
1665    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        // TODO(mmastrac): We need to ignore this error for things like the repl to behave as
1671        // they did before, but this is definitely not correct. It's just something we're
1672        // relying on. :(
1673        _ = t;
1674      }
1675      Poll::Pending
1676    })
1677    .await
1678  }
1679
1680  /// Runs a single tick of event loop
1681  ///
1682  /// If `PollEventLoopOptions.wait_for_inspector` is set to true, the event
1683  /// loop will return `Poll::Pending` if there are active inspector sessions.
1684  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      // We poll the inspector first.
1708      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    // Resolve async ops, run all next tick callbacks and macrotasks callbacks
1723    // and only then check for any promise exceptions (`unhandledrejection`
1724    // handlers are run in macrotasks callbacks so we need to let them run
1725    // first).
1726    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    // Get the pending state from the main realm, or all realms
1735    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 there are no blocking sessions (eg. REPL) we can now notify
1746          // debugger that the program has finished running and we're ready
1747          // to exit the process once debugger disconnects.
1748          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    // Check if more async ops have been dispatched
1762    // during this turn of event loop.
1763    // If there are any pending background tasks, we also wake the runtime to
1764    // make sure we don't miss them.
1765    // TODO(andreubotella) The event loop will spin as long as there are pending
1766    // background tasks. We should look into having V8 notify us when a
1767    // background task is done.
1768    #[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 ops were dispatched we may have progress on pending modules that we should re-check
1777      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        // pass, will be polled again
1793      } 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        // pass, will be polled again
1807      } 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        // Delay the above error by one spin of the event loop. A dynamic import
1813        // evaluation may complete during this, in which case the counter will
1814        // reset.
1815        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    // We are gonna print only a single message to provide a nice formatting
1833    // with source line of offending promise shown. Once user fixed it, then
1834    // they will get another error message for the next promise (but this
1835    // situation is gonna be very rare, if ever happening).
1836    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  // Set up the global object template and create context from it.
1850  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  // Get the global wrapper object from the context, get the real inner
1858  // global object from it, and and configure it using the middlewares.
1859  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  /// Takes a snapshot and consumes the runtime.
1889  ///
1890  /// `Error` can usually be downcast to `JsError`.
1891  pub fn snapshot(mut self) -> Box<[u8]> {
1892    // Ensure there are no live inspectors to prevent crashes.
1893    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    // Set the context to be snapshot's default context
1905    {
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    // Borrow the source maps during the snapshot to avoid copies
1912    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    // Serialize the module map and store its data in the snapshot.
1921    // TODO(mmastrac): This should deconstruct the realm into sidecar data rather than
1922    // extracting it from the realm and then tearing the realm down. IE, this should
1923    // probably be a method on `JsRealm` with a self-consuming parameter signature:
1924    // `fn into_sidecar_data(self) -> ...`.
1925    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  /// Collect event loop state from all the sub-states.
1988  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  /// Collect event loop state from all the states stored in the scope.
2030  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  // SAFETY: The data is a pointer to the Rust callback function. It is stored
2056  // in `JsRuntime::allocations` and thus is guaranteed to outlive the isolate.
2057  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  /// Called by `bindings::host_import_module_dynamically_callback`
2067  /// after initiating new dynamic import load.
2068  pub fn notify_new_dynamic_import(&self) {
2069    // Notify event loop to poll again soon.
2070    self.waker.wake();
2071  }
2072
2073  /// Performs an action with the inspector, if we have one
2074  pub(crate) fn with_inspector<T>(
2075    &self,
2076    mut f: impl FnMut(&JsRuntimeInspector) -> T,
2077  ) -> Option<T> {
2078    // Fast path
2079    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
2090// Related to module loading
2091impl 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  /// Evaluates an already instantiated ES module.
2104  ///
2105  /// Returns a future that resolves when module promise resolves.
2106  /// Implementors must manually call [`JsRuntime::run_event_loop`] to drive
2107  /// module evaluation future.
2108  ///
2109  /// Modules with top-level await are treated like promises, so a `throw` in the top-level
2110  /// block of a module is treated as an unhandled rejection. These rejections are provided to
2111  /// the unhandled promise rejection handler, which has the opportunity to pass them off to
2112  /// error-handling code. If those rejections are not handled (indicated by a `false` return
2113  /// from that unhandled promise rejection handler), then the runtime will terminate.
2114  ///
2115  /// The future provided by `mod_evaluate` will only return errors in the case where
2116  /// the runtime is shutdown and no longer available to provide unhandled rejection
2117  /// information.
2118  ///
2119  /// This function panics if module has not been instantiated.
2120  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  /// Asynchronously load specified module and all of its dependencies.
2131  ///
2132  /// The module will be marked as "main", and because of that
2133  /// "import.meta.main" will return true when checked inside that module.
2134  ///
2135  /// The source may be any type that implements the internal [`IntoModuleCodeString`] trait, but
2136  /// it is highly recommended that embedders use the [`ascii_str!`] to generate the fastest version
2137  /// of strings for v8 to handle. If the strings are not static, you may also pass a [`String`]
2138  /// generated by the [`format!`] macro.
2139  ///
2140  /// User must call [`JsRuntime::mod_evaluate`] with returned `ModuleId`
2141  /// manually after load is finished.
2142  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  /// Asynchronously load specified module and all of its dependencies, retrieving
2160  /// the module from the supplied [`ModuleLoader`].
2161  ///
2162  /// The module will be marked as "main", and because of that
2163  /// "import.meta.main" will return true when checked inside that module.
2164  ///
2165  /// User must call [`JsRuntime::mod_evaluate`] with returned `ModuleId`
2166  /// manually after load is finished.
2167  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  /// Asynchronously load specified ES module and all of its dependencies from the
2180  /// provided source.
2181  ///
2182  /// This method is meant to be used when loading some utility code that
2183  /// might be later imported by the main module (ie. an entry point module).
2184  ///
2185  /// The source may be any type that implements the internal [`IntoModuleCodeString`] trait, but
2186  /// it is highly recommended that embedders use the [`ascii_str!`] to generate the fastest version
2187  /// of strings for v8 to handle. If the strings are not static, you may also pass a [`String`]
2188  /// generated by the [`format!`] macro.
2189  ///
2190  /// User must call [`JsRuntime::mod_evaluate`] with returned `ModuleId`
2191  /// manually after load is finished.
2192  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  /// Asynchronously load specified ES module and all of its dependencies, retrieving
2210  /// the module from the supplied [`ModuleLoader`].
2211  ///
2212  /// This method is meant to be used when loading some utility code that
2213  /// might be later imported by the main module (ie. an entry point module).
2214  ///
2215  /// User must call [`JsRuntime::mod_evaluate`] with returned `ModuleId`
2216  /// manually after load is finished.
2217  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  /// Load and evaluate an ES module provided the specifier and source code.
2230  ///
2231  /// The module should not have Top-Level Await (that is, it should be
2232  /// possible to evaluate it synchronously).
2233  ///
2234  /// It is caller's responsibility to ensure that not duplicate specifiers are
2235  /// passed to this method.
2236  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    // Poll any pending task spawner tasks. Note that we need to poll separately because otherwise
2258    // Rust will extend the lifetime of the borrow longer than we expect.
2259    let tasks = context_state.task_spawner_factory.poll_inner(cx);
2260    if let Poll::Ready(tasks) = tasks {
2261      // TODO(mmastrac): we are using this flag
2262      dispatched_ops = true;
2263      for task in tasks {
2264        task(scope);
2265      }
2266    }
2267
2268    // We return async responses to JS in bounded batches. Note that because
2269    // we're passing these to JS as arguments, it is possible to overflow the
2270    // JS stack by just passing too many.
2271    const MAX_VEC_SIZE_FOR_OPS: usize = 1024;
2272
2273    // each batch is a flat vector of tuples:
2274    // `[promise_id1, op_result1, promise_id2, op_result2, ...]`
2275    // promise_id is a simple integer, op_result is an ops::OpResult
2276    // which contains a value OR an error, encoded as a tuple.
2277    // This batch is received in JS via the special `arguments` variable
2278    // and then each tuple is used to resolve or reject promises
2279    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        // We have too many, bail for now but re-wake the waker
2285        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      // Avoid holding the pending rejection lock longer than necessary
2347      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    // TODO(mmastrac): timer dispatch should be done via direct function call, but we will have to start
2371    // storing the exception-reporting callback.
2372    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            // Timer and interval traces both use RuntimeActivityType::Timer
2380            context_state
2381              .activity_traces
2382              .complete(RuntimeActivityType::Timer, timers[i].0 as _);
2383          }
2384          // depth, id, function
2385          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}