deno_core/runtime/
jsruntime.rs

1// Copyright 2018-2025 the Deno authors. MIT license.
2
3use super::SnapshotStoreDataStore;
4use super::SnapshottedData;
5use super::bindings;
6use super::bindings::create_exports_for_ops_virtual_module;
7use super::bindings::watch_promise;
8use super::exception_state::ExceptionState;
9use super::jsrealm::JsRealmInner;
10use super::op_driver::OpDriver;
11use super::setup;
12use super::snapshot;
13use super::stats::RuntimeActivityStatsFactory;
14use super::v8_static_strings::*;
15use crate::Extension;
16use crate::ExtensionArguments;
17use crate::ExtensionFileSource;
18use crate::ExtensionFileSourceCode;
19use crate::FastStaticString;
20use crate::FastString;
21use crate::ModuleCodeString;
22use crate::NoopModuleLoader;
23use crate::OpMetadata;
24use crate::OpMetricsEvent;
25use crate::OpStackTraceCallback;
26use crate::OpState;
27use crate::ascii_str;
28use crate::ascii_str_include;
29use crate::cppgc::FunctionTemplateData;
30use crate::error::CoreError;
31use crate::error::CoreErrorKind;
32use crate::error::CoreModuleExecuteError;
33use crate::error::CoreModuleParseError;
34use crate::error::ExtensionLazyInitCountMismatchError;
35use crate::error::ExtensionLazyInitOrderMismatchError;
36use crate::error::JsError;
37use crate::error::exception_to_err_result;
38use crate::extension_set;
39use crate::extension_set::LoadedSources;
40use crate::extensions::GlobalObjectMiddlewareFn;
41use crate::extensions::GlobalTemplateMiddlewareFn;
42use crate::inspector::JsRuntimeInspector;
43use crate::module_specifier::ModuleSpecifier;
44use crate::modules::CustomModuleEvaluationCb;
45use crate::modules::EvalContextCodeCacheReadyCb;
46use crate::modules::EvalContextGetCodeCacheCb;
47use crate::modules::ExtCodeCache;
48use crate::modules::ExtModuleLoader;
49use crate::modules::IntoModuleCodeString;
50use crate::modules::IntoModuleName;
51use crate::modules::ModuleId;
52use crate::modules::ModuleLoader;
53use crate::modules::ModuleMap;
54use crate::modules::ModuleName;
55use crate::modules::RequestedModuleType;
56use crate::modules::ValidateImportAttributesCb;
57use crate::modules::script_origin;
58use crate::ops_metrics::OpMetricsFactoryFn;
59use crate::ops_metrics::dispatch_metrics_async;
60use crate::runtime::ContextState;
61use crate::runtime::JsRealm;
62use crate::runtime::OpDriverImpl;
63use crate::runtime::jsrealm;
64use crate::source_map::SourceMapData;
65use crate::source_map::SourceMapper;
66use crate::stats::RuntimeActivityType;
67use deno_error::JsErrorBox;
68use futures::FutureExt;
69use futures::task::AtomicWaker;
70use smallvec::SmallVec;
71use std::any::Any;
72use std::future::Future;
73use std::future::poll_fn;
74use v8::MessageErrorLevel;
75
76use std::cell::Cell;
77use std::cell::RefCell;
78use std::collections::HashMap;
79use std::collections::VecDeque;
80use std::ffi::c_void;
81use std::mem::ManuallyDrop;
82use std::ops::Deref;
83use std::ops::DerefMut;
84use std::pin::Pin;
85use std::rc::Rc;
86use std::sync::Arc;
87use std::sync::Mutex;
88use std::task::Context;
89use std::task::Poll;
90use std::task::Waker;
91
92pub type WaitForInspectorDisconnectCallback = Box<dyn Fn()>;
93const STATE_DATA_OFFSET: u32 = 0;
94
95pub type ExtensionTranspiler =
96  dyn Fn(
97    ModuleName,
98    ModuleCodeString,
99  ) -> Result<(ModuleCodeString, Option<SourceMapData>), JsErrorBox>;
100
101/// Objects that need to live as long as the isolate
102#[derive(Default)]
103pub(crate) struct IsolateAllocations {
104  pub(crate) externalized_sources: Box<[v8::OneByteConst]>,
105  pub(crate) original_sources: Box<[FastString]>,
106  pub(crate) near_heap_limit_callback_data:
107    Option<(Box<RefCell<dyn Any>>, v8::NearHeapLimitCallback)>,
108}
109
110/// ManuallyDrop<Rc<...>> is clone, but it returns a ManuallyDrop<Rc<...>> which is a massive
111/// memory-leak footgun.
112pub(crate) struct ManuallyDropRc<T>(ManuallyDrop<Rc<T>>);
113
114impl<T> ManuallyDropRc<T> {
115  #[allow(unused)]
116  pub fn clone(&self) -> Rc<T> {
117    self.0.deref().clone()
118  }
119}
120
121impl<T> Deref for ManuallyDropRc<T> {
122  type Target = Rc<T>;
123  fn deref(&self) -> &Self::Target {
124    self.0.deref()
125  }
126}
127
128impl<T> DerefMut for ManuallyDropRc<T> {
129  fn deref_mut(&mut self) -> &mut Self::Target {
130    self.0.deref_mut()
131  }
132}
133
134/// This struct contains the [`JsRuntimeState`] and [`v8::OwnedIsolate`] that are required
135/// to do an orderly shutdown of V8. We keep these in a separate struct to allow us to control
136/// the destruction more closely, as snapshots require the isolate to be destroyed by the
137/// snapshot process, not the destructor.
138///
139/// The way rusty_v8 works w/snapshots is that the [`v8::OwnedIsolate`] gets consumed by a
140/// [`v8::snapshot::SnapshotCreator`] that is stored in its annex. It's a bit awkward, because this
141/// means we cannot let it drop (because we don't have it after a snapshot). On top of that, we have
142/// to consume it in the snapshot creator because otherwise it panics.
143///
144/// This inner struct allows us to let the outer JsRuntime drop normally without a Drop impl, while we
145/// control dropping more closely here using ManuallyDrop.
146pub(crate) struct InnerIsolateState {
147  will_snapshot: bool,
148  extensions: Vec<&'static str>,
149  op_count: usize,
150  source_count: usize,
151  addl_refs_count: usize,
152  main_realm: ManuallyDrop<JsRealm>,
153  pub(crate) state: ManuallyDropRc<JsRuntimeState>,
154  v8_isolate: ManuallyDrop<v8::OwnedIsolate>,
155}
156
157impl InnerIsolateState {
158  /// Clean out the opstate and take the inspector to prevent the inspector from getting destroyed
159  /// after we've torn down the contexts. If the inspector is not correctly torn down, random crashes
160  /// happen in tests (and possibly for users using the inspector).
161  pub fn prepare_for_cleanup(&mut self) {
162    // Explicitly shut down the op driver here, just in case there are other references to it
163    // that prevent it from dropping after we invalidate the state.
164    self.main_realm.0.context_state.pending_ops.shutdown();
165    let inspector = self.state.inspector.take();
166    self.state.op_state.borrow_mut().clear();
167    if let Some(inspector) = inspector {
168      assert_eq!(
169        Rc::strong_count(&inspector),
170        1,
171        "The inspector must be dropped before the runtime"
172      );
173    }
174  }
175
176  pub fn cleanup(&mut self) {
177    self.prepare_for_cleanup();
178
179    let state_ptr = self.v8_isolate.get_data(STATE_DATA_OFFSET);
180    // SAFETY: We are sure that it's a valid pointer for whole lifetime of
181    // the runtime.
182    _ = unsafe { Rc::from_raw(state_ptr as *const JsRuntimeState) };
183
184    unsafe {
185      ManuallyDrop::take(&mut self.main_realm).0.destroy();
186    }
187
188    debug_assert_eq!(Rc::strong_count(&self.state), 1);
189  }
190
191  pub fn prepare_for_snapshot(mut self) -> v8::OwnedIsolate {
192    self.cleanup();
193
194    // SAFETY: We're copying out of self and then immediately forgetting self
195    unsafe {
196      ManuallyDrop::drop(&mut self.state.0);
197
198      let isolate = ManuallyDrop::take(&mut self.v8_isolate);
199
200      std::mem::forget(self);
201
202      isolate
203    }
204  }
205}
206
207impl Drop for InnerIsolateState {
208  fn drop(&mut self) {
209    self.cleanup();
210    // SAFETY: We gotta drop these
211    unsafe {
212      ManuallyDrop::drop(&mut self.state.0);
213
214      if self.will_snapshot {
215        // Create the snapshot and just drop it.
216        #[allow(clippy::print_stderr)]
217        {
218          eprintln!("WARNING: v8::OwnedIsolate for snapshot was leaked");
219        }
220      } else {
221        ManuallyDrop::drop(&mut self.v8_isolate);
222      }
223    }
224  }
225}
226
227#[derive(Copy, Clone, Debug, Eq, PartialEq)]
228pub(crate) enum InitMode {
229  /// We have no snapshot -- this is a pristine context.
230  New,
231  /// We are using a snapshot, thus certain initialization steps are skipped.
232  FromSnapshot {
233    // Can we skip the work of op registration?
234    skip_op_registration: bool,
235  },
236}
237
238impl InitMode {
239  fn from_options(options: &RuntimeOptions) -> Self {
240    match options.startup_snapshot {
241      None => Self::New,
242      Some(_) => Self::FromSnapshot {
243        skip_op_registration: options.skip_op_registration,
244      },
245    }
246  }
247
248  #[inline]
249  pub fn needs_ops_bindings(&self) -> bool {
250    !matches!(
251      self,
252      InitMode::FromSnapshot {
253        skip_op_registration: true
254      }
255    )
256  }
257}
258
259#[derive(Default)]
260struct PromiseFuture {
261  resolved: Cell<Option<Result<v8::Global<v8::Value>, Box<JsError>>>>,
262  waker: Cell<Option<Waker>>,
263}
264
265#[derive(Clone, Default)]
266struct RcPromiseFuture(Rc<PromiseFuture>);
267
268impl RcPromiseFuture {
269  pub fn new(res: Result<v8::Global<v8::Value>, Box<JsError>>) -> Self {
270    Self(Rc::new(PromiseFuture {
271      resolved: Some(res).into(),
272      ..Default::default()
273    }))
274  }
275}
276
277impl Future for RcPromiseFuture {
278  type Output = Result<v8::Global<v8::Value>, Box<JsError>>;
279
280  fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
281    let this = self.get_mut();
282    match this.0.resolved.take() {
283      Some(resolved) => Poll::Ready(resolved),
284      _ => {
285        this.0.waker.set(Some(cx.waker().clone()));
286        Poll::Pending
287      }
288    }
289  }
290}
291
292static VIRTUAL_OPS_MODULE_NAME: FastStaticString = ascii_str!("ext:core/ops");
293
294pub(crate) struct InternalSourceFile {
295  pub specifier: FastStaticString,
296  pub source: FastStaticString,
297}
298
299macro_rules! internal_source_file {
300  ($str_:literal) => {{
301    InternalSourceFile {
302      specifier: ascii_str!(concat!("ext:core/", $str_)),
303      source: ascii_str_include!(concat!("../", $str_)),
304    }
305  }};
306}
307
308/// These files are executed just after a new context is created. They provided
309/// the necessary infrastructure to bind ops.
310pub(crate) static CONTEXT_SETUP_SOURCES: [InternalSourceFile; 2] = [
311  internal_source_file!("00_primordials.js"),
312  internal_source_file!("00_infra.js"),
313];
314
315/// These files are executed when we start setting up extensions. They rely
316/// on ops being already fully set up.
317pub(crate) static BUILTIN_SOURCES: [InternalSourceFile; 1] =
318  [internal_source_file!("01_core.js")];
319
320/// Executed after `BUILTIN_SOURCES` are executed. Provides a thin ES module
321/// that exports `core`, `internals` and `primordials` objects.
322pub(crate) static BUILTIN_ES_MODULES: [ExtensionFileSource; 1] =
323  [ExtensionFileSource::new(
324    "ext:core/mod.js",
325    ascii_str_include!("../mod.js"),
326  )];
327
328/// We have `ext:core/ops` and `ext:core/mod.js` that are always provided.
329#[cfg(test)]
330pub(crate) const NO_OF_BUILTIN_MODULES: usize = 2;
331
332/// A single execution context of JavaScript. Corresponds roughly to the "Web
333/// Worker" concept in the DOM.
334///
335/// The JsRuntime future completes when there is an error or when all
336/// pending ops have completed.
337///
338/// Use [`JsRuntimeForSnapshot`] to be able to create a snapshot.
339///
340/// Note: since V8 11.6, all runtimes must have a common parent thread that
341/// initalized the V8 platform. This can be done by calling
342/// [`JsRuntime::init_platform`] explicitly, or it will be done automatically on
343/// the calling thread when the first runtime is created.
344pub struct JsRuntime {
345  pub(crate) inner: InnerIsolateState,
346  pub(crate) allocations: IsolateAllocations,
347  // Contains paths of source files that were executed in
348  // [`JsRuntime::init_extension_js`]. This field is populated only if a
349  // snapshot is being created.
350  files_loaded_from_fs_during_snapshot: Vec<&'static str>,
351  // Marks if this is considered the top-level runtime. Used only by inspector.
352  is_main_runtime: bool,
353}
354
355/// The runtime type used for snapshot creation.
356pub struct JsRuntimeForSnapshot(JsRuntime);
357
358impl Deref for JsRuntimeForSnapshot {
359  type Target = JsRuntime;
360
361  fn deref(&self) -> &Self::Target {
362    &self.0
363  }
364}
365
366impl DerefMut for JsRuntimeForSnapshot {
367  fn deref_mut(&mut self) -> &mut Self::Target {
368    &mut self.0
369  }
370}
371
372pub struct CrossIsolateStore<T>(Arc<Mutex<CrossIsolateStoreInner<T>>>);
373
374struct CrossIsolateStoreInner<T> {
375  map: HashMap<u32, T>,
376  last_id: u32,
377}
378
379impl<T> CrossIsolateStore<T> {
380  pub(crate) fn insert(&self, value: T) -> u32 {
381    let mut store = self.0.lock().unwrap();
382    let last_id = store.last_id;
383    store.map.insert(last_id, value);
384    store.last_id += 1;
385    last_id
386  }
387
388  pub(crate) fn take(&self, id: u32) -> Option<T> {
389    let mut store = self.0.lock().unwrap();
390    store.map.remove(&id)
391  }
392}
393
394impl<T> Default for CrossIsolateStore<T> {
395  fn default() -> Self {
396    CrossIsolateStore(Arc::new(Mutex::new(CrossIsolateStoreInner {
397      map: Default::default(),
398      last_id: 0,
399    })))
400  }
401}
402
403impl<T> Clone for CrossIsolateStore<T> {
404  fn clone(&self) -> Self {
405    Self(self.0.clone())
406  }
407}
408
409pub type SharedArrayBufferStore =
410  CrossIsolateStore<v8::SharedRef<v8::BackingStore>>;
411
412pub type CompiledWasmModuleStore = CrossIsolateStore<v8::CompiledWasmModule>;
413
414/// Internal state for JsRuntime which is stored in one of v8::Isolate's
415/// embedder slots.
416pub struct JsRuntimeState {
417  pub(crate) source_mapper: Rc<RefCell<SourceMapper>>,
418  pub(crate) op_state: Rc<RefCell<OpState>>,
419  pub(crate) shared_array_buffer_store: Option<SharedArrayBufferStore>,
420  pub(crate) compiled_wasm_module_store: Option<CompiledWasmModuleStore>,
421  wait_for_inspector_disconnect_callback:
422    Option<WaitForInspectorDisconnectCallback>,
423  pub(crate) validate_import_attributes_cb: Option<ValidateImportAttributesCb>,
424  pub(crate) custom_module_evaluation_cb: Option<CustomModuleEvaluationCb>,
425  pub(crate) eval_context_get_code_cache_cb:
426    RefCell<Option<EvalContextGetCodeCacheCb>>,
427  pub(crate) eval_context_code_cache_ready_cb:
428    RefCell<Option<EvalContextCodeCacheReadyCb>>,
429  pub(crate) cppgc_template: RefCell<Option<v8::Global<v8::FunctionTemplate>>>,
430  pub(crate) function_templates: Rc<RefCell<FunctionTemplateData>>,
431  pub(crate) callsite_prototype: RefCell<Option<v8::Global<v8::Object>>>,
432  waker: Arc<AtomicWaker>,
433  /// Accessed through [`JsRuntimeState::with_inspector`].
434  inspector: RefCell<Option<Rc<JsRuntimeInspector>>>,
435  has_inspector: Cell<bool>,
436  import_assertions_support: ImportAssertionsSupport,
437  lazy_extensions: Vec<&'static str>,
438}
439
440#[derive(Default)]
441pub struct RuntimeOptions {
442  /// Implementation of `ModuleLoader` which will be
443  /// called when V8 requests to load ES modules in the main realm.
444  ///
445  /// If not provided runtime will error if code being
446  /// executed tries to load modules.
447  pub module_loader: Option<Rc<dyn ModuleLoader>>,
448
449  /// If specified, enables V8 code cache for extension code.
450  pub extension_code_cache: Option<Rc<dyn ExtCodeCache>>,
451
452  /// If specified, transpiles extensions before loading.
453  pub extension_transpiler: Option<Rc<ExtensionTranspiler>>,
454
455  /// Provide a function that may optionally provide a metrics collector
456  /// for a given op.
457  pub op_metrics_factory_fn: Option<OpMetricsFactoryFn>,
458
459  /// JsRuntime extensions, not to be confused with ES modules.
460  /// Only ops registered by extensions will be initialized. If you need
461  /// to execute JS code from extensions, pass source files in `js` or `esm`
462  /// option on `ExtensionBuilder`.
463  ///
464  /// If you are creating a runtime from a snapshot take care not to include
465  /// JavaScript sources in the extensions.
466  pub extensions: Vec<Extension>,
467
468  /// V8 snapshot that should be loaded on startup.
469  ///
470  /// For testing, use `runtime.snapshot()` and then [`Box::leak`] to acquire
471  // a static slice.
472  pub startup_snapshot: Option<&'static [u8]>,
473
474  /// Should op registration be skipped?
475  pub skip_op_registration: bool,
476
477  /// Isolate creation parameters.
478  pub create_params: Option<v8::CreateParams>,
479
480  /// V8 platform instance to use. Used when Deno initializes V8
481  /// (which it only does once), otherwise it's silently dropped.
482  pub v8_platform: Option<v8::SharedRef<v8::Platform>>,
483
484  /// The store to use for transferring SharedArrayBuffers between isolates.
485  /// If multiple isolates should have the possibility of sharing
486  /// SharedArrayBuffers, they should use the same [SharedArrayBufferStore]. If
487  /// no [SharedArrayBufferStore] is specified, SharedArrayBuffer can not be
488  /// serialized.
489  pub shared_array_buffer_store: Option<SharedArrayBufferStore>,
490
491  /// The store to use for transferring `WebAssembly.Module` objects between
492  /// isolates.
493  /// If multiple isolates should have the possibility of sharing
494  /// `WebAssembly.Module` objects, they should use the same
495  /// [CompiledWasmModuleStore]. If no [CompiledWasmModuleStore] is specified,
496  /// `WebAssembly.Module` objects cannot be serialized.
497  pub compiled_wasm_module_store: Option<CompiledWasmModuleStore>,
498
499  /// Start inspector instance to allow debuggers to connect.
500  pub inspector: bool,
501
502  /// Describe if this is the main runtime instance, used by debuggers in some
503  /// situation - like disconnecting when program finishes running.
504  pub is_main: bool,
505
506  #[cfg(any(test, feature = "unsafe_runtime_options"))]
507  /// Should this isolate expose the v8 natives (eg: %OptimizeFunctionOnNextCall) and
508  /// GC control functions (`gc()`)? WARNING: This should not be used for production code as
509  /// this may expose the runtime to security vulnerabilities.
510  pub unsafe_expose_natives_and_gc: bool,
511
512  /// A callback that can be used to validate import attributes received at
513  /// the import site. If no callback is provided, all attributes are allowed.
514  ///
515  /// Embedders might use this callback to eg. validate value of "type"
516  /// attribute, not allowing other types than "JSON".
517  ///
518  /// To signal validation failure, users should throw an V8 exception inside
519  /// the callback.
520  pub validate_import_attributes_cb: Option<ValidateImportAttributesCb>,
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  /// Callbacks to retrieve and store code cache for scripts evaluated
536  /// through evalContext.
537  pub eval_context_code_cache_cbs:
538    Option<(EvalContextGetCodeCacheCb, EvalContextCodeCacheReadyCb)>,
539
540  pub import_assertions_support: ImportAssertionsSupport,
541
542  /// A callback to specify how stack traces should be used when an op is
543  /// annotated with `stack_trace` attribute. Use wisely, as it's very expensive
544  /// to collect stack traces on each op invocation.
545  pub maybe_op_stack_trace_callback: Option<OpStackTraceCallback>,
546}
547
548pub struct ImportAssertionsSupportCustomCallbackArgs {
549  pub maybe_specifier: Option<String>,
550  pub maybe_line_number: Option<usize>,
551  pub column_number: usize,
552  pub maybe_source_line: Option<String>,
553}
554
555#[derive(Default)]
556pub enum ImportAssertionsSupport {
557  /// `assert` keyword is no longer supported and causes a SyntaxError.
558  /// This is the default setting, because V8 unshipped import assertions
559  /// support in v12.6. To enable import assertion one must pass
560  /// `--harmony-import-assertions` V8 flag when initializing the runtime.
561  #[default]
562  Error,
563
564  /// `assert` keyword is supported.
565  Yes,
566
567  /// `assert` keyword is supported, but prints a warning message on occurence.
568  Warning,
569
570  /// `assert` keyword is supported, provided callback is called on each occurence.
571  /// Callback receives optional specifier, optional line number, column number and optional
572  /// source line.
573  CustomCallback(Box<dyn Fn(ImportAssertionsSupportCustomCallbackArgs)>),
574}
575
576impl ImportAssertionsSupport {
577  fn is_enabled(&self) -> bool {
578    matches!(self, Self::Yes | Self::Warning | Self::CustomCallback(_))
579  }
580
581  fn has_warning(&self) -> bool {
582    matches!(self, Self::Warning | Self::CustomCallback(_))
583  }
584}
585
586/// Currently only handles warnings for "import assertions" deprecation
587extern "C" fn isolate_message_listener(
588  message: v8::Local<v8::Message>,
589  _exception: v8::Local<v8::Value>,
590) {
591  v8::callback_scope!(unsafe scope, message);
592  v8::scope!(let scope, scope);
593
594  let message_v8_str = message.get(scope);
595  let message_str = message_v8_str.to_rust_string_lossy(scope);
596
597  if !message_str.starts_with("'assert' is deprecated") {
598    return;
599  }
600
601  let maybe_script_resource_name = message
602    .get_script_resource_name(scope)
603    .map(|s| s.to_rust_string_lossy(scope));
604  let maybe_source_line = message
605    .get_source_line(scope)
606    .map(|s| s.to_rust_string_lossy(scope));
607  let maybe_line_number = message.get_line_number(scope);
608  let start_column = message.get_start_column();
609
610  let js_runtime_state = JsRuntime::state_from(scope);
611  if let Some(specifier) = maybe_script_resource_name.as_ref() {
612    let module_map = JsRealm::module_map_from(scope);
613    module_map
614      .loader
615      .borrow()
616      .purge_and_prevent_code_cache(specifier);
617  }
618
619  match &js_runtime_state.import_assertions_support {
620    ImportAssertionsSupport::Warning => {
621      let mut msg = "⚠️  Import assertions are deprecated. Use `with` keyword, instead of 'assert' keyword.".to_string();
622      if let Some(specifier) = maybe_script_resource_name {
623        if let Some(source_line) = maybe_source_line {
624          msg.push('\n');
625          msg.push_str(&source_line);
626          msg.push('\n');
627          msg.push_str(&format!("{:0width$}^", " ", width = start_column));
628        }
629        msg.push_str(&format!(
630          "  at {}:{}:{}",
631          specifier,
632          maybe_line_number.unwrap(),
633          start_column
634        ));
635        #[allow(clippy::print_stderr)]
636        {
637          eprintln!("{}", msg);
638        }
639      }
640    }
641    ImportAssertionsSupport::CustomCallback(cb) => {
642      cb(ImportAssertionsSupportCustomCallbackArgs {
643        maybe_specifier: maybe_script_resource_name,
644        maybe_line_number,
645        column_number: start_column,
646        maybe_source_line,
647      });
648    }
649    _ => unreachable!(),
650  }
651}
652
653impl RuntimeOptions {
654  #[cfg(any(test, feature = "unsafe_runtime_options"))]
655  fn unsafe_expose_natives_and_gc(&self) -> bool {
656    self.unsafe_expose_natives_and_gc
657  }
658
659  #[cfg(not(any(test, feature = "unsafe_runtime_options")))]
660  fn unsafe_expose_natives_and_gc(&self) -> bool {
661    false
662  }
663}
664
665#[derive(Copy, Clone, Debug)]
666pub struct PollEventLoopOptions {
667  pub wait_for_inspector: bool,
668  pub pump_v8_message_loop: bool,
669}
670
671impl Default for PollEventLoopOptions {
672  fn default() -> Self {
673    Self {
674      wait_for_inspector: false,
675      pump_v8_message_loop: true,
676    }
677  }
678}
679
680#[derive(Default)]
681pub struct CreateRealmOptions {
682  /// Implementation of `ModuleLoader` which will be
683  /// called when V8 requests to load ES modules in the realm.
684  ///
685  /// If not provided, there will be an error if code being
686  /// executed tries to load modules from the realm.
687  pub module_loader: Option<Rc<dyn ModuleLoader>>,
688}
689
690#[macro_export]
691macro_rules! scope {
692  ($scope: ident, $self: expr) => {
693    let context = $self.main_context();
694    let isolate = &mut *$self.v8_isolate();
695    $crate::v8::scope!($scope, isolate);
696    let context = $crate::v8::Local::new($scope, context);
697    let $scope = &mut $crate::v8::ContextScope::new($scope, context);
698  };
699}
700
701impl JsRuntime {
702  /// Explicitly initalizes the V8 platform using the passed platform. This
703  /// should only be called once per process. Further calls will be silently
704  /// ignored.
705  #[cfg(not(any(test, feature = "unsafe_runtime_options")))]
706  pub fn init_platform(
707    v8_platform: Option<v8::SharedRef<v8::Platform>>,
708    import_assertions_enabled: bool,
709  ) {
710    setup::init_v8(v8_platform, cfg!(test), false, import_assertions_enabled);
711  }
712
713  /// Explicitly initalizes the V8 platform using the passed platform. This
714  /// should only be called once per process. Further calls will be silently
715  /// ignored.
716  ///
717  /// The `expose_natives` flag is used to expose the v8 natives
718  /// (eg: %OptimizeFunctionOnNextCall) and GC control functions (`gc()`).
719  /// WARNING: This should not be used for production code as
720  /// this may expose the runtime to security vulnerabilities.
721  #[cfg(any(test, feature = "unsafe_runtime_options"))]
722  pub fn init_platform(
723    v8_platform: Option<v8::SharedRef<v8::Platform>>,
724    expose_natives: bool,
725    import_assertions_enabled: bool,
726  ) {
727    setup::init_v8(
728      v8_platform,
729      cfg!(test),
730      expose_natives,
731      import_assertions_enabled,
732    );
733  }
734
735  /// Only constructor, configuration is done through `options`.
736  /// Panics if the runtime cannot be initialized.
737  pub fn new(options: RuntimeOptions) -> JsRuntime {
738    match Self::try_new(options) {
739      Ok(runtime) => runtime,
740      Err(err) => {
741        panic!(
742          "Failed to initialize a JsRuntime: {}",
743          err.print_with_cause()
744        );
745      }
746    }
747  }
748
749  /// Only constructor, configuration is done through `options`.
750  /// Returns an error if the runtime cannot be initialized.
751  pub fn try_new(mut options: RuntimeOptions) -> Result<JsRuntime, CoreError> {
752    setup::init_v8(
753      options.v8_platform.take(),
754      cfg!(test),
755      options.unsafe_expose_natives_and_gc(),
756      options.import_assertions_support.is_enabled(),
757    );
758    JsRuntime::new_inner(options, false)
759  }
760
761  pub(crate) fn state_from(isolate: &v8::Isolate) -> Rc<JsRuntimeState> {
762    let state_ptr = isolate.get_data(STATE_DATA_OFFSET);
763    let state_rc =
764      // SAFETY: We are sure that it's a valid pointer for whole lifetime of
765      // the runtime.
766      unsafe { Rc::from_raw(state_ptr as *const JsRuntimeState) };
767    let state = state_rc.clone();
768    std::mem::forget(state_rc);
769    state
770  }
771
772  /// Returns the `OpState` associated with the passed `Isolate`.
773  pub fn op_state_from(isolate: &v8::Isolate) -> Rc<RefCell<OpState>> {
774    let state = Self::state_from(isolate);
775    state.op_state.clone()
776  }
777
778  pub(crate) fn has_more_work(scope: &mut v8::PinScope) -> bool {
779    EventLoopPendingState::new_from_scope(scope).is_pending()
780  }
781
782  /// Returns the `OpMetadata` associated with the op `name`.
783  /// Note this is linear with respect to the number of ops registered.
784  pub fn op_metadata(&self, name: &str) -> Option<OpMetadata> {
785    let state = &self.inner.main_realm.0.context_state;
786    state.op_ctxs.iter().find_map(|ctx| {
787      if ctx.decl.name == name {
788        Some(ctx.decl.metadata)
789      } else {
790        None
791      }
792    })
793  }
794
795  fn new_inner(
796    mut options: RuntimeOptions,
797    will_snapshot: bool,
798  ) -> Result<JsRuntime, CoreError> {
799    let init_mode = InitMode::from_options(&options);
800    let mut extensions = std::mem::take(&mut options.extensions);
801    let mut isolate_allocations = IsolateAllocations::default();
802
803    let enable_stack_trace_in_ops =
804      options.maybe_op_stack_trace_callback.is_some();
805
806    // First let's create an `OpState` and contribute to it from extensions...
807    let mut op_state = OpState::new(options.maybe_op_stack_trace_callback);
808    let unrefed_ops = op_state.unrefed_ops.clone();
809
810    let lazy_extensions =
811      extension_set::setup_op_state(&mut op_state, &mut extensions);
812
813    // Load the sources and source maps
814    let mut files_loaded = Vec::with_capacity(128);
815    let loader = options
816      .module_loader
817      .unwrap_or_else(|| Rc::new(NoopModuleLoader));
818
819    let mut source_mapper = SourceMapper::new(loader.clone());
820
821    let (maybe_startup_snapshot, mut sidecar_data) = options
822      .startup_snapshot
823      .take()
824      .map(snapshot::deconstruct)
825      .unzip();
826
827    let mut sources = extension_set::into_sources_and_source_maps(
828      options.extension_transpiler.as_deref(),
829      &extensions,
830      sidecar_data.as_ref().map(|s| &*s.snapshot_data.extensions),
831      |source| {
832        mark_as_loaded_from_fs_during_snapshot(&mut files_loaded, &source.code)
833      },
834    )?;
835
836    for loaded_source in sources
837      .js
838      .iter()
839      .chain(sources.esm.iter())
840      .chain(sources.lazy_esm.iter())
841      .filter(|s| s.maybe_source_map.is_some())
842    {
843      source_mapper.add_ext_source_map(
844        loaded_source.specifier.try_clone().unwrap(),
845        loaded_source.maybe_source_map.clone().unwrap(),
846      );
847    }
848
849    // ...now let's set up ` JsRuntimeState`, we'll need to set some fields
850    // later, after `JsRuntime` is all set up...
851    let waker = op_state.waker.clone();
852    let op_state = Rc::new(RefCell::new(op_state));
853    let (eval_context_get_code_cache_cb, eval_context_set_code_cache_cb) =
854      options
855        .eval_context_code_cache_cbs
856        .map(|cbs| (Some(cbs.0), Some(cbs.1)))
857        .unwrap_or_default();
858    let state_rc = Rc::new(JsRuntimeState {
859      source_mapper: Rc::new(RefCell::new(source_mapper)),
860      shared_array_buffer_store: options.shared_array_buffer_store,
861      compiled_wasm_module_store: options.compiled_wasm_module_store,
862      wait_for_inspector_disconnect_callback: options
863        .wait_for_inspector_disconnect_callback,
864      op_state: op_state.clone(),
865      validate_import_attributes_cb: options.validate_import_attributes_cb,
866      custom_module_evaluation_cb: options.custom_module_evaluation_cb,
867      eval_context_get_code_cache_cb: RefCell::new(
868        eval_context_get_code_cache_cb,
869      ),
870      eval_context_code_cache_ready_cb: RefCell::new(
871        eval_context_set_code_cache_cb,
872      ),
873      waker,
874      // Some fields are initialized later after isolate is created
875      inspector: None.into(),
876      has_inspector: false.into(),
877      cppgc_template: None.into(),
878      function_templates: Default::default(),
879      callsite_prototype: None.into(),
880      import_assertions_support: options.import_assertions_support,
881      lazy_extensions,
882    });
883
884    // ...now we're moving on to ops; set them up, create `OpCtx` for each op
885    // and get ready to actually create V8 isolate...
886    let (op_decls, mut op_method_decls) =
887      extension_set::init_ops(crate::ops_builtin::BUILTIN_OPS, &mut extensions);
888
889    let op_driver = Rc::new(OpDriverImpl::default());
890    let op_metrics_factory_fn = options.op_metrics_factory_fn.take();
891
892    let (mut op_ctxs, methods_ctx_offset) = extension_set::create_op_ctxs(
893      op_decls,
894      &mut op_method_decls,
895      op_metrics_factory_fn,
896      op_driver.clone(),
897      op_state.clone(),
898      state_rc.clone(),
899      enable_stack_trace_in_ops,
900    );
901
902    // ...ops are now almost fully set up; let's create a V8 isolate...
903    let (
904      global_template_middleware,
905      global_object_middlewares,
906      additional_references,
907    ) = extension_set::get_middlewares_and_external_refs(&mut extensions);
908
909    // Capture the extension, op and source counts
910    let extensions = extensions.iter().map(|e| e.name).collect();
911    let op_count = op_ctxs.len();
912    let source_count = sources.len();
913    let addl_refs_count = additional_references.len();
914
915    let ops_in_snapshot = sidecar_data
916      .as_ref()
917      .map(|d| d.snapshot_data.op_count)
918      .unwrap_or_default();
919    let sources_in_snapshot = sidecar_data
920      .as_ref()
921      .map(|d| d.snapshot_data.source_count)
922      .unwrap_or_default();
923
924    let snapshot_sources: Vec<&[u8]> = sidecar_data
925      .as_mut()
926      .map(|s| std::mem::take(&mut s.snapshot_data.external_strings))
927      .unwrap_or_default();
928    (
929      isolate_allocations.externalized_sources,
930      isolate_allocations.original_sources,
931    ) = bindings::externalize_sources(&mut sources, snapshot_sources);
932
933    let external_references = bindings::create_external_references(
934      &op_ctxs,
935      &additional_references,
936      &isolate_allocations.externalized_sources,
937      ops_in_snapshot,
938      sources_in_snapshot,
939    );
940
941    let has_snapshot = maybe_startup_snapshot.is_some();
942    let mut isolate = setup::create_isolate(
943      will_snapshot,
944      options.create_params.take(),
945      maybe_startup_snapshot,
946      external_references.into(),
947    );
948
949    if state_rc.import_assertions_support.has_warning() {
950      isolate.add_message_listener_with_error_level(
951        isolate_message_listener,
952        MessageErrorLevel::ALL,
953      );
954    }
955
956    let isolate_ptr = unsafe { isolate.as_raw_isolate_ptr() };
957    // ...isolate is fully set up, we can forward its pointer to the ops to finish
958    // their' setup...
959    for op_ctx in op_ctxs.iter_mut() {
960      op_ctx.isolate = isolate_ptr;
961    }
962
963    op_state.borrow_mut().put(isolate_ptr);
964
965    // ...once ops and isolate are set up, we can create a `ContextState`...
966    let context_state = Rc::new(ContextState::new(
967      op_driver.clone(),
968      isolate_ptr,
969      op_ctxs,
970      op_method_decls,
971      methods_ctx_offset,
972      op_state.borrow().external_ops_tracker.clone(),
973      unrefed_ops,
974    ));
975
976    // TODO(bartlomieju): factor out
977    // Add the task spawners to the OpState
978    let spawner = context_state
979      .task_spawner_factory
980      .clone()
981      .new_same_thread_spawner();
982    op_state.borrow_mut().put(spawner);
983    let spawner = context_state
984      .task_spawner_factory
985      .clone()
986      .new_cross_thread_spawner();
987    op_state.borrow_mut().put(spawner);
988
989    // ...and with `ContextState` available we can set up V8 context...
990    let mut snapshotted_data = None;
991    let main_context = {
992      v8::scope!(let scope, &mut isolate);
993
994      let cppgc_template = crate::cppgc::make_cppgc_template(scope);
995      state_rc
996        .cppgc_template
997        .borrow_mut()
998        .replace(v8::Global::new(scope, cppgc_template));
999
1000      let context = create_context(
1001        scope,
1002        &global_template_middleware,
1003        &global_object_middlewares,
1004        has_snapshot,
1005      );
1006
1007      // Get module map data from the snapshot
1008      if let Some(raw_data) = sidecar_data {
1009        snapshotted_data = Some(snapshot::load_snapshotted_data_from_snapshot(
1010          scope, context, raw_data,
1011        ));
1012      }
1013
1014      v8::Global::new(scope, context)
1015    };
1016
1017    let main_realm = {
1018      v8::scope_with_context!(context_scope, &mut isolate, &main_context);
1019      let scope = context_scope;
1020      let context = v8::Local::new(scope, &main_context);
1021
1022      let callsite_prototype = crate::error::make_callsite_prototype(scope);
1023      state_rc
1024        .callsite_prototype
1025        .borrow_mut()
1026        .replace(v8::Global::new(scope, callsite_prototype));
1027
1028      // ...followed by creation of `Deno.core` namespace, as well as internal
1029      // infrastructure to provide JavaScript bindings for ops...
1030      if init_mode == InitMode::New {
1031        bindings::initialize_deno_core_namespace(scope, context, init_mode);
1032        bindings::initialize_primordials_and_infra(scope)?;
1033      }
1034      // If we're creating a new runtime or there are new ops to register
1035      // set up JavaScript bindings for them.
1036      if init_mode.needs_ops_bindings() {
1037        bindings::initialize_deno_core_ops_bindings(
1038          scope,
1039          context,
1040          &context_state.op_ctxs,
1041          &context_state.op_method_decls,
1042          methods_ctx_offset,
1043          &mut state_rc.function_templates.borrow_mut(),
1044        );
1045      }
1046
1047      // SAFETY: Initialize the context state slot.
1048      unsafe {
1049        context.set_aligned_pointer_in_embedder_data(
1050          super::jsrealm::CONTEXT_STATE_SLOT_INDEX,
1051          Rc::into_raw(context_state.clone()) as *mut c_void,
1052        );
1053      }
1054
1055      let inspector = if options.inspector {
1056        Some(JsRuntimeInspector::new(
1057          isolate_ptr,
1058          scope,
1059          context,
1060          options.is_main,
1061        ))
1062      } else {
1063        None
1064      };
1065
1066      // ...now that JavaScript bindings to ops are available we can deserialize
1067      // modules stored in the snapshot (because they depend on the ops and external
1068      // references must match properly) and recreate a module map...
1069      let exception_state = context_state.exception_state.clone();
1070      let module_map = Rc::new(ModuleMap::new(
1071        loader,
1072        state_rc.source_mapper.clone(),
1073        exception_state.clone(),
1074        will_snapshot,
1075      ));
1076
1077      if let Some((snapshotted_data, mut data_store)) = snapshotted_data {
1078        *exception_state.js_handled_promise_rejection_cb.borrow_mut() =
1079          snapshotted_data
1080            .js_handled_promise_rejection_cb
1081            .map(|cb| data_store.get(scope, cb));
1082        module_map.update_with_snapshotted_data(
1083          scope,
1084          &mut data_store,
1085          snapshotted_data.module_map_data,
1086        );
1087
1088        if let Some(index) = snapshotted_data.ext_import_meta_proto {
1089          *context_state.ext_import_meta_proto.borrow_mut() =
1090            Some(data_store.get(scope, index));
1091        }
1092
1093        state_rc
1094          .function_templates
1095          .borrow_mut()
1096          .update_with_snapshotted_data(
1097            scope,
1098            &mut data_store,
1099            snapshotted_data.function_templates_data,
1100          );
1101
1102        let mut mapper = state_rc.source_mapper.borrow_mut();
1103        for (key, map) in snapshotted_data.ext_source_maps {
1104          mapper.add_ext_source_map(ModuleName::from_static(key), map.into());
1105        }
1106      }
1107
1108      if context_state.ext_import_meta_proto.borrow().is_none() {
1109        let null = v8::null(scope);
1110        let obj = v8::Object::with_prototype_and_properties(
1111          scope,
1112          null.into(),
1113          &[],
1114          &[],
1115        );
1116        *context_state.ext_import_meta_proto.borrow_mut() =
1117          Some(v8::Global::new(scope, obj));
1118      }
1119
1120      // SAFETY: Set the module map slot in the context
1121      unsafe {
1122        context.set_aligned_pointer_in_embedder_data(
1123          super::jsrealm::MODULE_MAP_SLOT_INDEX,
1124          Rc::into_raw(module_map.clone()) as *mut c_void,
1125        );
1126      }
1127
1128      // ...we are ready to create a "realm" for the context...
1129      let main_realm = {
1130        let main_realm = JsRealmInner::new(
1131          context_state,
1132          main_context,
1133          module_map.clone(),
1134          state_rc.function_templates.clone(),
1135        );
1136        // TODO(bartlomieju): why is this done in here? Maybe we can hoist it out?
1137        state_rc.has_inspector.set(inspector.is_some());
1138        *state_rc.inspector.borrow_mut() = inspector;
1139        main_realm
1140      };
1141      let main_realm = JsRealm::new(main_realm);
1142      scope.set_data(
1143        STATE_DATA_OFFSET,
1144        Rc::into_raw(state_rc.clone()) as *mut c_void,
1145      );
1146      main_realm
1147    };
1148
1149    // ...which allows us to create the `JsRuntime` instance...
1150    let mut js_runtime = JsRuntime {
1151      inner: InnerIsolateState {
1152        will_snapshot,
1153        op_count,
1154        extensions,
1155        source_count,
1156        addl_refs_count,
1157        main_realm: ManuallyDrop::new(main_realm),
1158        state: ManuallyDropRc(ManuallyDrop::new(state_rc)),
1159        v8_isolate: ManuallyDrop::new(isolate),
1160      },
1161      allocations: isolate_allocations,
1162      files_loaded_from_fs_during_snapshot: vec![],
1163      is_main_runtime: options.is_main,
1164    };
1165
1166    // ...we're almost done with the setup, all that's left is to execute
1167    // internal JS and then execute code provided by extensions...
1168    {
1169      let realm = JsRealm::clone(&js_runtime.inner.main_realm);
1170      let context_global = realm.context();
1171      let module_map = realm.0.module_map();
1172
1173      // TODO(bartlomieju): this is somewhat duplicated in `bindings::initialize_context`,
1174      // but for migration period we need to have ops available in both `Deno.core.ops`
1175      // as well as have them available in "virtual ops module"
1176      // if !matches!(
1177      //   self.init_mode,
1178      //   InitMode::FromSnapshot {
1179      //     skip_op_registration: true
1180      //   }
1181      // ) {
1182      if init_mode == InitMode::New {
1183        js_runtime
1184          .execute_virtual_ops_module(context_global, module_map.clone());
1185      }
1186
1187      if init_mode == InitMode::New {
1188        js_runtime.execute_builtin_sources(
1189          &realm,
1190          &module_map,
1191          &mut files_loaded,
1192        )?;
1193      }
1194
1195      js_runtime.store_js_callbacks(&realm, will_snapshot);
1196
1197      js_runtime.init_extension_js(
1198        &realm,
1199        &module_map,
1200        sources,
1201        options.extension_code_cache,
1202      )?;
1203    }
1204
1205    if will_snapshot {
1206      js_runtime.files_loaded_from_fs_during_snapshot = files_loaded;
1207    }
1208
1209    // ...and we've made it; `JsRuntime` is ready to execute user code.
1210    Ok(js_runtime)
1211  }
1212
1213  /// If extensions were initialized with `lazy_init`, they need to be
1214  /// fully initialized with this method.
1215  pub fn lazy_init_extensions(
1216    &self,
1217    ext_args: Vec<ExtensionArguments>,
1218  ) -> Result<(), CoreError> {
1219    if ext_args.len() != self.inner.state.lazy_extensions.len() {
1220      return Err(
1221        CoreErrorKind::ExtensionLazyInitCountMismatch(
1222          ExtensionLazyInitCountMismatchError {
1223            lazy_init_extensions_len: self.inner.state.lazy_extensions.len(),
1224            arguments_len: ext_args.len(),
1225          },
1226        )
1227        .into_box(),
1228      );
1229    }
1230
1231    let mut state = self.inner.state.op_state.borrow_mut();
1232
1233    for (mut args, expected_name) in ext_args
1234      .into_iter()
1235      .zip(self.inner.state.lazy_extensions.iter())
1236    {
1237      if args.name != *expected_name {
1238        return Err(
1239          CoreErrorKind::ExtensionLazyInitOrderMismatch(
1240            ExtensionLazyInitOrderMismatchError {
1241              expected: expected_name,
1242              actual: args.name,
1243            },
1244          )
1245          .into_box(),
1246        );
1247      }
1248
1249      let Some(f) = args.op_state_fn.take() else {
1250        continue;
1251      };
1252
1253      f(&mut state);
1254    }
1255
1256    Ok(())
1257  }
1258
1259  pub fn set_eval_context_code_cache_cbs(
1260    &self,
1261    eval_context_code_cache_cbs: Option<(
1262      EvalContextGetCodeCacheCb,
1263      EvalContextCodeCacheReadyCb,
1264    )>,
1265  ) {
1266    let (eval_context_get_code_cache_cb, eval_context_set_code_cache_cb) =
1267      eval_context_code_cache_cbs
1268        .map(|cbs| (Some(cbs.0), Some(cbs.1)))
1269        .unwrap_or_default();
1270    *self.inner.state.eval_context_get_code_cache_cb.borrow_mut() =
1271      eval_context_get_code_cache_cb;
1272    *self
1273      .inner
1274      .state
1275      .eval_context_code_cache_ready_cb
1276      .borrow_mut() = eval_context_set_code_cache_cb;
1277  }
1278
1279  #[cfg(test)]
1280  #[inline]
1281  pub(crate) fn module_map(&self) -> Rc<ModuleMap> {
1282    self.inner.main_realm.0.module_map()
1283  }
1284
1285  #[inline]
1286  pub fn main_context(&self) -> v8::Global<v8::Context> {
1287    self.inner.main_realm.0.context().clone()
1288  }
1289
1290  #[cfg(test)]
1291  pub(crate) fn main_realm(&self) -> JsRealm {
1292    JsRealm::clone(&self.inner.main_realm)
1293  }
1294
1295  #[inline]
1296  pub fn v8_isolate(&mut self) -> &mut v8::OwnedIsolate {
1297    &mut self.inner.v8_isolate
1298  }
1299
1300  #[inline]
1301  fn v8_isolate_ptr(&mut self) -> v8::UnsafeRawIsolatePtr {
1302    unsafe { self.inner.v8_isolate.as_raw_isolate_ptr() }
1303  }
1304
1305  #[inline]
1306  pub fn inspector(&self) -> Rc<JsRuntimeInspector> {
1307    self.inner.state.inspector()
1308  }
1309
1310  #[inline]
1311  pub fn wait_for_inspector_disconnect(&self) {
1312    if let Some(callback) = self
1313      .inner
1314      .state
1315      .wait_for_inspector_disconnect_callback
1316      .as_ref()
1317    {
1318      callback();
1319    }
1320  }
1321
1322  pub fn runtime_activity_stats_factory(&self) -> RuntimeActivityStatsFactory {
1323    RuntimeActivityStatsFactory {
1324      context_state: self.inner.main_realm.0.context_state.clone(),
1325      op_state: self.inner.state.op_state.clone(),
1326    }
1327  }
1328
1329  // TODO(bartlomieju): remove, instead `JsRuntimeForSnapshot::new` should return
1330  // a struct that contains this data.
1331  pub(crate) fn files_loaded_from_fs_during_snapshot(&self) -> &[&'static str] {
1332    &self.files_loaded_from_fs_during_snapshot
1333  }
1334
1335  /// Create a synthetic module - `ext:core/ops` - that exports all ops registered
1336  /// with the runtime.
1337  fn execute_virtual_ops_module(
1338    &mut self,
1339    context_global: &v8::Global<v8::Context>,
1340    module_map: Rc<ModuleMap>,
1341  ) {
1342    scope!(scope, self);
1343    let context_local = v8::Local::new(scope, context_global);
1344    let context_state = JsRealm::state_from_scope(scope);
1345    let global = context_local.global(scope);
1346    let synthetic_module_exports = create_exports_for_ops_virtual_module(
1347      &context_state.op_ctxs,
1348      &context_state.op_method_decls,
1349      context_state.methods_ctx_offset,
1350      scope,
1351      global,
1352    );
1353    let mod_id = module_map.new_synthetic_module(
1354      scope,
1355      VIRTUAL_OPS_MODULE_NAME,
1356      crate::ModuleType::JavaScript,
1357      synthetic_module_exports,
1358    );
1359    module_map.mod_evaluate_sync(scope, mod_id).unwrap();
1360  }
1361
1362  /// Executes built-in scripts and ES modules - this code is required for
1363  /// ops system to work properly, as well as providing necessary bindings
1364  /// on the `Deno.core` namespace.
1365  ///
1366  /// This is not done in [`bindings::initialize_primordials_and_infra`] because
1367  /// some of this code already relies on certain ops being available.
1368  fn execute_builtin_sources(
1369    &mut self,
1370    _realm: &JsRealm,
1371    module_map: &Rc<ModuleMap>,
1372    files_loaded: &mut Vec<&'static str>,
1373  ) -> Result<(), CoreError> {
1374    scope!(scope, self);
1375
1376    for source_file in &BUILTIN_SOURCES {
1377      let name = source_file.specifier.v8_string(scope).unwrap();
1378      let source = source_file.source.v8_string(scope).unwrap();
1379
1380      let origin = script_origin(scope, name, false, None);
1381      let script = v8::Script::compile(scope, source, Some(&origin))
1382        .ok_or(CoreModuleParseError(source_file.specifier))?;
1383      script
1384        .run(scope)
1385        .ok_or(CoreModuleExecuteError(source_file.specifier))?;
1386    }
1387
1388    for file_source in &BUILTIN_ES_MODULES {
1389      mark_as_loaded_from_fs_during_snapshot(files_loaded, &file_source.code);
1390      module_map.lazy_load_es_module_with_code(
1391        scope,
1392        file_source.specifier,
1393        file_source.load()?,
1394        None,
1395      )?;
1396    }
1397
1398    Ok(())
1399  }
1400
1401  /// Initializes JS of provided Extensions in the given realm.
1402  async fn init_extension_js_inner(
1403    &mut self,
1404    realm: &JsRealm,
1405    module_map: &Rc<ModuleMap>,
1406    loaded_sources: LoadedSources,
1407    ext_code_cache: Option<Rc<dyn ExtCodeCache>>,
1408  ) -> Result<(), CoreError> {
1409    // First, add all the lazy ESM
1410    for source in loaded_sources.lazy_esm {
1411      module_map.add_lazy_loaded_esm_source(source.specifier, source.code);
1412    }
1413
1414    // Temporarily override the loader of the `ModuleMap` so we can load
1415    // extension code.
1416
1417    // TODO(bartlomieju): maybe this should be a method on the `ModuleMap`,
1418    // instead of explicitly changing the `.loader` field?
1419    let loader = module_map.loader.borrow().clone();
1420    let mut modules = Vec::with_capacity(loaded_sources.esm.len());
1421    let mut sources = Vec::with_capacity(loaded_sources.esm.len());
1422    for esm in loaded_sources.esm {
1423      modules.push(ModuleSpecifier::parse(&esm.specifier).unwrap());
1424      sources.push((esm.specifier, esm.code));
1425    }
1426    let ext_loader =
1427      Rc::new(ExtModuleLoader::new(sources, ext_code_cache.clone()));
1428    *module_map.loader.borrow_mut() = ext_loader.clone();
1429
1430    // Next, load the extension modules as side modules (but do not execute them)
1431    for module in modules {
1432      // eprintln!("loading module: {module}");
1433      realm
1434        .load_side_es_module_from_code(self.v8_isolate(), &module, None)
1435        .await?;
1436    }
1437
1438    // Execute extension scripts
1439    for source in loaded_sources.js {
1440      match &ext_code_cache {
1441        Some(ext_code_cache) => {
1442          let specifier = ModuleSpecifier::parse(&source.specifier)?;
1443          realm.execute_script_with_cache(
1444            self.v8_isolate(),
1445            specifier,
1446            source.code,
1447            &|specifier, code| {
1448              ext_code_cache.get_code_cache_info(specifier, code, false)
1449            },
1450            &|specifier, hash, code_cache| {
1451              ext_code_cache
1452                .code_cache_ready(specifier, hash, code_cache, false)
1453            },
1454          )?;
1455        }
1456        _ => {
1457          realm.execute_script(
1458            self.v8_isolate(),
1459            source.specifier,
1460            source.code,
1461          )?;
1462        }
1463      }
1464    }
1465
1466    // ...then execute all entry points
1467    for specifier in loaded_sources.esm_entry_points {
1468      let Some(mod_id) =
1469        module_map.get_id(&specifier, RequestedModuleType::None)
1470      else {
1471        return Err(
1472          CoreErrorKind::MissingFromModuleMap(specifier.to_string()).into_box(),
1473        );
1474      };
1475
1476      let isolate = self.v8_isolate();
1477      jsrealm::context_scope!(scope, realm, isolate);
1478      module_map.mod_evaluate_sync(scope, mod_id)?;
1479      let mut cx = Context::from_waker(Waker::noop());
1480      // poll once so code cache is populated. the `ExtCodeCache` trait is sync, so
1481      // the `CodeCacheReady` futures will always finish on the first poll.
1482      let _ = module_map.poll_progress(&mut cx, scope);
1483    }
1484
1485    #[cfg(debug_assertions)]
1486    {
1487      jsrealm::context_scope!(scope, realm, self.v8_isolate());
1488      module_map.check_all_modules_evaluated(scope)?;
1489    }
1490
1491    let module_map = realm.0.module_map();
1492    *module_map.loader.borrow_mut() = loader;
1493    ext_loader.finalize()?;
1494
1495    Ok(())
1496  }
1497
1498  /// Initializes JS of provided Extensions in the given realm.
1499  fn init_extension_js(
1500    &mut self,
1501    realm: &JsRealm,
1502    module_map: &Rc<ModuleMap>,
1503    loaded_sources: LoadedSources,
1504    ext_code_cache: Option<Rc<dyn ExtCodeCache>>,
1505  ) -> Result<(), CoreError> {
1506    futures::executor::block_on(self.init_extension_js_inner(
1507      realm,
1508      module_map,
1509      loaded_sources,
1510      ext_code_cache,
1511    ))?;
1512
1513    Ok(())
1514  }
1515
1516  pub fn eval<'s, 'i, T>(
1517    scope: &mut v8::PinScope<'s, 'i>,
1518    code: &str,
1519  ) -> Option<v8::Local<'s, T>>
1520  where
1521    v8::Local<'s, T>: TryFrom<v8::Local<'s, v8::Value>, Error = v8::DataError>,
1522  {
1523    v8::escapable_handle_scope!(let scope, scope);
1524    let source = v8::String::new(scope, code).unwrap();
1525    let script = v8::Script::compile(scope, source, None).unwrap();
1526    let v = script.run(scope)?;
1527    scope.escape(v).try_into().ok()
1528  }
1529
1530  /// Grab and store JavaScript bindings to callbacks necessary for the
1531  /// JsRuntime to operate properly.
1532  fn store_js_callbacks(&mut self, realm: &JsRealm, will_snapshot: bool) {
1533    let (
1534      event_loop_tick_cb,
1535      build_custom_error_cb,
1536      run_immediate_callbacks_cb,
1537      wasm_instance_fn,
1538    ) = {
1539      scope!(scope, self);
1540      let context = realm.context();
1541      let context_local = v8::Local::new(scope, context);
1542      let global = context_local.global(scope);
1543      // TODO(bartlomieju): these probably could be captured from main realm so we don't have to
1544      // look up them again?
1545      let deno_obj: v8::Local<v8::Object> =
1546        bindings::get(scope, global, DENO, "Deno");
1547      let core_obj: v8::Local<v8::Object> =
1548        bindings::get(scope, deno_obj, CORE, "Deno.core");
1549
1550      let event_loop_tick_cb: v8::Local<v8::Function> = bindings::get(
1551        scope,
1552        core_obj,
1553        EVENT_LOOP_TICK,
1554        "Deno.core.eventLoopTick",
1555      );
1556      let build_custom_error_cb: v8::Local<v8::Function> = bindings::get(
1557        scope,
1558        core_obj,
1559        BUILD_CUSTOM_ERROR,
1560        "Deno.core.buildCustomError",
1561      );
1562      let run_immediate_callbacks_cb: v8::Local<v8::Function> = bindings::get(
1563        scope,
1564        core_obj,
1565        RUN_IMMEDIATE_CALLBACKS,
1566        "Deno.core.runImmediateCallbacks",
1567      );
1568
1569      let mut wasm_instance_fn = None;
1570      if !will_snapshot {
1571        let key = WEBASSEMBLY.v8_string(scope).unwrap();
1572        if let Some(web_assembly_obj_value) = global.get(scope, key.into()) {
1573          // NOTE(bartlomieju): This is still fallible, because in some V8 modes
1574          // WebAssembly namespace is not available (eg. `--jitless`).
1575          let maybe_web_assembly_object =
1576            TryInto::<v8::Local<v8::Object>>::try_into(web_assembly_obj_value);
1577          if let Ok(web_assembly_object) = maybe_web_assembly_object {
1578            wasm_instance_fn = Some(bindings::get::<v8::Local<v8::Function>>(
1579              scope,
1580              web_assembly_object,
1581              INSTANCE,
1582              "WebAssembly.Instance",
1583            ));
1584          }
1585        }
1586      }
1587
1588      (
1589        v8::Global::new(scope, event_loop_tick_cb),
1590        v8::Global::new(scope, build_custom_error_cb),
1591        v8::Global::new(scope, run_immediate_callbacks_cb),
1592        wasm_instance_fn.map(|f| v8::Global::new(scope, f)),
1593      )
1594    };
1595
1596    // Put global handles in the realm's ContextState
1597    let state_rc = realm.0.state();
1598    state_rc
1599      .js_event_loop_tick_cb
1600      .borrow_mut()
1601      .replace(event_loop_tick_cb);
1602    state_rc
1603      .exception_state
1604      .js_build_custom_error_cb
1605      .borrow_mut()
1606      .replace(build_custom_error_cb);
1607    state_rc
1608      .run_immediate_callbacks_cb
1609      .borrow_mut()
1610      .replace(run_immediate_callbacks_cb);
1611    if let Some(wasm_instance_fn) = wasm_instance_fn {
1612      state_rc
1613        .wasm_instance_fn
1614        .borrow_mut()
1615        .replace(wasm_instance_fn);
1616    }
1617  }
1618
1619  /// Returns the runtime's op state, which can be used to maintain ops
1620  /// and access resources between op calls.
1621  pub fn op_state(&self) -> Rc<RefCell<OpState>> {
1622    self.inner.state.op_state.clone()
1623  }
1624
1625  /// Returns the runtime's op names, ordered by OpId.
1626  pub fn op_names(&self) -> Vec<&'static str> {
1627    let state = &self.inner.main_realm.0.context_state;
1628    state.op_ctxs.iter().map(|o| o.decl.name).collect()
1629  }
1630
1631  /// Executes traditional, non-ECMAScript-module JavaScript code, This code executes in
1632  /// the global scope by default, and it is possible to maintain local JS state and invoke
1633  /// this method multiple times.
1634  ///
1635  /// `name` may be any type that implements the internal [`IntoModuleName`] trait.
1636  /// It can be a filepath or any other string, but it is required to be 7-bit ASCII, eg.
1637  ///
1638  ///   - "/some/file/path.js"
1639  ///   - "<anon>"
1640  ///   - "[native code]"
1641  ///
1642  /// The same `name` value can be used for multiple executions.
1643  ///
1644  /// The source may be any type that implements the internal [`IntoModuleCodeString`] trait, but
1645  /// it is highly recommended that embedders use the [`ascii_str!`] to generate the fastest version
1646  /// of strings for v8 to handle. If the strings are not static, you may also pass a [`String`]
1647  /// generated by the [`format!`] macro.
1648  pub fn execute_script(
1649    &mut self,
1650    name: impl IntoModuleName,
1651    source_code: impl IntoModuleCodeString,
1652  ) -> Result<v8::Global<v8::Value>, Box<JsError>> {
1653    let isolate = &mut self.inner.v8_isolate;
1654    self.inner.main_realm.execute_script(
1655      isolate,
1656      name.into_module_name(),
1657      source_code.into_module_code(),
1658    )
1659  }
1660
1661  /// Call a function and return a future resolving with the return value of the
1662  /// function. If the function returns a promise, the future will resolve only once the
1663  /// event loop resolves the underlying promise. If the future rejects, the future will
1664  /// resolve with the underlying error.
1665  ///
1666  /// The event loop must be polled seperately for this future to resolve. If the event loop
1667  /// is not polled, the future will never make progress.
1668  pub fn call(
1669    &mut self,
1670    function: &v8::Global<v8::Function>,
1671  ) -> impl Future<Output = Result<v8::Global<v8::Value>, Box<JsError>>> + use<>
1672  {
1673    self.call_with_args(function, &[])
1674  }
1675
1676  /// Call a function and returns a future resolving with the return value of the
1677  /// function. If the function returns a promise, the future will resolve only once the
1678  /// event loop resolves the underlying promise. If the future rejects, the future will
1679  /// resolve with the underlying error.
1680  ///
1681  /// The event loop must be polled seperately for this future to resolve. If the event loop
1682  /// is not polled, the future will never make progress.
1683  pub fn scoped_call(
1684    scope: &mut v8::PinScope,
1685    function: &v8::Global<v8::Function>,
1686  ) -> impl Future<Output = Result<v8::Global<v8::Value>, Box<JsError>>> + use<>
1687  {
1688    Self::scoped_call_with_args(scope, function, &[])
1689  }
1690
1691  /// Call a function and returns a future resolving with the return value of the
1692  /// function. If the function returns a promise, the future will resolve only once the
1693  /// event loop resolves the underlying promise. If the future rejects, the future will
1694  /// resolve with the underlying error.
1695  ///
1696  /// The event loop must be polled seperately for this future to resolve. If the event loop
1697  /// is not polled, the future will never make progress.
1698  pub fn call_with_args(
1699    &mut self,
1700    function: &v8::Global<v8::Function>,
1701    args: &[v8::Global<v8::Value>],
1702  ) -> impl Future<Output = Result<v8::Global<v8::Value>, Box<JsError>>> + use<>
1703  {
1704    scope!(scope, self);
1705    Self::scoped_call_with_args(scope, function, args)
1706  }
1707
1708  /// Call a function and returns a future resolving with the return value of the
1709  /// function. If the function returns a promise, the future will resolve only once the
1710  /// event loop resolves the underlying promise. If the future rejects, the future will
1711  /// resolve with the underlying error.
1712  ///
1713  /// The event loop must be polled seperately for this future to resolve. If the event loop
1714  /// is not polled, the future will never make progress.
1715  pub fn scoped_call_with_args(
1716    scope: &mut v8::PinScope,
1717    function: &v8::Global<v8::Function>,
1718    args: &[v8::Global<v8::Value>],
1719  ) -> impl Future<Output = Result<v8::Global<v8::Value>, Box<JsError>>> + use<>
1720  {
1721    v8::tc_scope!(let scope, scope);
1722    let cb = function.open(scope);
1723    let this = v8::undefined(scope).into();
1724    let promise = if args.is_empty() {
1725      cb.call(scope, this, &[])
1726    } else {
1727      let mut local_args: SmallVec<[v8::Local<v8::Value>; 8]> =
1728        SmallVec::with_capacity(args.len());
1729      for v in args {
1730        local_args.push(v8::Local::new(scope, v));
1731      }
1732      cb.call(scope, this, &local_args)
1733    };
1734
1735    if promise.is_none() {
1736      if scope.is_execution_terminating() {
1737        let undefined = v8::undefined(scope).into();
1738        return RcPromiseFuture::new(exception_to_err_result(
1739          scope, undefined, false, true,
1740        ));
1741      }
1742      let exception = scope.exception().unwrap();
1743      return RcPromiseFuture::new(exception_to_err_result(
1744        scope, exception, false, true,
1745      ));
1746    }
1747    let promise = promise.unwrap();
1748    if !promise.is_promise() {
1749      return RcPromiseFuture::new(Ok(v8::Global::new(scope, promise)));
1750    }
1751    let promise = v8::Local::<v8::Promise>::try_from(promise).unwrap();
1752    Self::resolve_promise_inner(scope, promise)
1753  }
1754
1755  /// Call a function. If it returns a promise, run the event loop until that
1756  /// promise is settled. If the promise rejects or there is an uncaught error
1757  /// in the event loop, return `Err(error)`. Or return `Ok(<await returned>)`.
1758  #[deprecated = "Use call"]
1759  pub async fn call_and_await(
1760    &mut self,
1761    function: &v8::Global<v8::Function>,
1762  ) -> Result<v8::Global<v8::Value>, CoreError> {
1763    let call = self.call(function);
1764    self
1765      .with_event_loop_promise(call, PollEventLoopOptions::default())
1766      .await
1767  }
1768
1769  /// Call a function with args. If it returns a promise, run the event loop until that
1770  /// promise is settled. If the promise rejects or there is an uncaught error
1771  /// in the event loop, return `Err(error)`. Or return `Ok(<await returned>)`.
1772  #[deprecated = "Use call_with_args"]
1773  pub async fn call_with_args_and_await(
1774    &mut self,
1775    function: &v8::Global<v8::Function>,
1776    args: &[v8::Global<v8::Value>],
1777  ) -> Result<v8::Global<v8::Value>, CoreError> {
1778    let call = self.call_with_args(function, args);
1779    self
1780      .with_event_loop_promise(call, PollEventLoopOptions::default())
1781      .await
1782  }
1783
1784  /// Returns the namespace object of a module.
1785  ///
1786  /// This is only available after module evaluation has completed.
1787  /// This function panics if module has not been instantiated.
1788  pub fn get_module_namespace(
1789    &mut self,
1790    module_id: ModuleId,
1791  ) -> Result<v8::Global<v8::Object>, CoreError> {
1792    let isolate = &mut self.inner.v8_isolate;
1793    self
1794      .inner
1795      .main_realm
1796      .get_module_namespace(isolate, module_id)
1797  }
1798
1799  /// Registers a callback on the isolate when the memory limits are approached.
1800  /// Use this to prevent V8 from crashing the process when reaching the limit.
1801  ///
1802  /// Calls the closure with the current heap limit and the initial heap limit.
1803  /// The return value of the closure is set as the new limit.
1804  pub fn add_near_heap_limit_callback<C>(&mut self, cb: C)
1805  where
1806    C: FnMut(usize, usize) -> usize + 'static,
1807  {
1808    let boxed_cb = Box::new(RefCell::new(cb));
1809    let data = boxed_cb.as_ptr() as *mut c_void;
1810
1811    let prev = self
1812      .allocations
1813      .near_heap_limit_callback_data
1814      .replace((boxed_cb, near_heap_limit_callback::<C>));
1815    if let Some((_, prev_cb)) = prev {
1816      self
1817        .v8_isolate()
1818        .remove_near_heap_limit_callback(prev_cb, 0);
1819    }
1820
1821    self
1822      .v8_isolate()
1823      .add_near_heap_limit_callback(near_heap_limit_callback::<C>, data);
1824  }
1825
1826  pub fn remove_near_heap_limit_callback(&mut self, heap_limit: usize) {
1827    if let Some((_, cb)) = self.allocations.near_heap_limit_callback_data.take()
1828    {
1829      self
1830        .v8_isolate()
1831        .remove_near_heap_limit_callback(cb, heap_limit);
1832    }
1833  }
1834
1835  fn pump_v8_message_loop(
1836    &self,
1837    scope: &mut v8::PinScope,
1838  ) -> Result<(), Box<JsError>> {
1839    while v8::Platform::pump_message_loop(
1840      &v8::V8::get_current_platform(),
1841      scope,
1842      false, // don't block if there are no tasks
1843    ) {
1844      // do nothing
1845    }
1846
1847    v8::tc_scope!(let tc_scope, scope);
1848
1849    tc_scope.perform_microtask_checkpoint();
1850    match tc_scope.exception() {
1851      None => Ok(()),
1852      Some(exception) => {
1853        exception_to_err_result(tc_scope, exception, false, true)
1854      }
1855    }
1856  }
1857
1858  pub fn maybe_init_inspector(&mut self) {
1859    let inspector = &mut self.inner.state.inspector.borrow_mut();
1860    if inspector.is_some() {
1861      return;
1862    }
1863
1864    let context = self.main_context();
1865    let isolate_ptr = unsafe { self.inner.v8_isolate.as_raw_isolate_ptr() };
1866    v8::scope_with_context!(
1867      scope,
1868      self.inner.v8_isolate.as_mut(),
1869      context.clone(),
1870    );
1871    let context = v8::Local::new(scope, context);
1872
1873    self.inner.state.has_inspector.set(true);
1874    **inspector = Some(JsRuntimeInspector::new(
1875      isolate_ptr,
1876      scope,
1877      context,
1878      self.is_main_runtime,
1879    ));
1880  }
1881
1882  /// Waits for the given value to resolve while polling the event loop.
1883  ///
1884  /// This future resolves when either the value is resolved or the event loop runs to
1885  /// completion.
1886  pub fn resolve(
1887    &mut self,
1888    promise: v8::Global<v8::Value>,
1889  ) -> impl Future<Output = Result<v8::Global<v8::Value>, Box<JsError>>> + use<>
1890  {
1891    scope!(scope, self);
1892    Self::scoped_resolve(scope, promise)
1893  }
1894
1895  /// Waits for the given value to resolve while polling the event loop.
1896  ///
1897  /// This future resolves when either the value is resolved or the event loop runs to
1898  /// completion.
1899  pub fn scoped_resolve(
1900    scope: &mut v8::PinScope,
1901    promise: v8::Global<v8::Value>,
1902  ) -> impl Future<Output = Result<v8::Global<v8::Value>, Box<JsError>>> + use<>
1903  {
1904    let promise = v8::Local::new(scope, promise);
1905    if !promise.is_promise() {
1906      return RcPromiseFuture::new(Ok(v8::Global::new(scope, promise)));
1907    }
1908    let promise = v8::Local::<v8::Promise>::try_from(promise).unwrap();
1909    Self::resolve_promise_inner(scope, promise)
1910  }
1911
1912  /// Waits for the given value to resolve while polling the event loop.
1913  ///
1914  /// This future resolves when either the value is resolved or the event loop runs to
1915  /// completion.
1916  #[deprecated = "Use resolve"]
1917  pub async fn resolve_value(
1918    &mut self,
1919    global: v8::Global<v8::Value>,
1920  ) -> Result<v8::Global<v8::Value>, CoreError> {
1921    let resolve = self.resolve(global);
1922    self
1923      .with_event_loop_promise(resolve, PollEventLoopOptions::default())
1924      .await
1925  }
1926
1927  /// Given a promise, returns a future that resolves when it does.
1928  fn resolve_promise_inner<'s, 'i>(
1929    scope: &mut v8::PinScope<'s, 'i>,
1930    promise: v8::Local<'s, v8::Promise>,
1931  ) -> RcPromiseFuture {
1932    let future = RcPromiseFuture::default();
1933    let f = future.clone();
1934    watch_promise(scope, promise, move |scope, _rv, res| {
1935      let res = match res {
1936        Ok(l) => Ok(v8::Global::new(scope, l)),
1937        Err(e) => exception_to_err_result(scope, e, true, true),
1938      };
1939      f.0.resolved.set(Some(res));
1940      if let Some(waker) = f.0.waker.take() {
1941        waker.wake();
1942      }
1943    });
1944
1945    future
1946  }
1947
1948  /// Runs event loop to completion
1949  ///
1950  /// This future resolves when:
1951  ///  - there are no more pending dynamic imports
1952  ///  - there are no more pending ops
1953  ///  - there are no more active inspector sessions (only if
1954  ///    `PollEventLoopOptions.wait_for_inspector` is set to true)
1955  pub async fn run_event_loop(
1956    &mut self,
1957    poll_options: PollEventLoopOptions,
1958  ) -> Result<(), CoreError> {
1959    poll_fn(|cx| self.poll_event_loop(cx, poll_options)).await
1960  }
1961
1962  /// A utility function that run provided future concurrently with the event loop.
1963  ///
1964  /// If the event loop resolves while polling the future, it return an error with the text
1965  /// `Promise resolution is still pending but the event loop has already resolved`
1966  pub async fn with_event_loop_promise<'fut, T, E>(
1967    &mut self,
1968    mut fut: impl Future<Output = Result<T, E>> + Unpin + 'fut,
1969    poll_options: PollEventLoopOptions,
1970  ) -> Result<T, CoreError>
1971  where
1972    CoreError: From<E>,
1973  {
1974    // Manually implement tokio::select
1975    poll_fn(|cx| {
1976      if let Poll::Ready(t) = fut.poll_unpin(cx) {
1977        return Poll::Ready(t.map_err(|e| e.into()));
1978      }
1979      if let Poll::Ready(t) = self.poll_event_loop(cx, poll_options) {
1980        t?;
1981        if let Poll::Ready(t) = fut.poll_unpin(cx) {
1982          return Poll::Ready(t.map_err(|e| e.into()));
1983        }
1984        return Poll::Ready(Err(
1985          CoreErrorKind::PendingPromiseResolution.into_box(),
1986        ));
1987      }
1988      Poll::Pending
1989    })
1990    .await
1991  }
1992
1993  /// A utility function that run provided future concurrently with the event loop.
1994  ///
1995  /// If the event loop resolves while polling the future, it will continue to be polled,
1996  /// regardless of whether it returned an error or success.
1997  ///
1998  /// Useful for interacting with local inspector session.
1999  pub async fn with_event_loop_future<'fut, T, E>(
2000    &mut self,
2001    mut fut: impl Future<Output = Result<T, E>> + Unpin + 'fut,
2002    poll_options: PollEventLoopOptions,
2003  ) -> Result<T, E> {
2004    // Manually implement tokio::select
2005    poll_fn(|cx| {
2006      if let Poll::Ready(t) = fut.poll_unpin(cx) {
2007        return Poll::Ready(t);
2008      }
2009      if let Poll::Ready(t) = self.poll_event_loop(cx, poll_options) {
2010        // TODO(mmastrac): We need to ignore this error for things like the repl to behave as
2011        // they did before, but this is definitely not correct. It's just something we're
2012        // relying on. :(
2013        _ = t;
2014      }
2015      Poll::Pending
2016    })
2017    .await
2018  }
2019
2020  /// Runs a single tick of event loop
2021  ///
2022  /// If `PollEventLoopOptions.wait_for_inspector` is set to true, the event
2023  /// loop will return `Poll::Pending` if there are active inspector sessions.
2024  pub fn poll_event_loop(
2025    &mut self,
2026    cx: &mut Context,
2027    poll_options: PollEventLoopOptions,
2028  ) -> Poll<Result<(), CoreError>> {
2029    // SAFETY: We know this isolate is valid and non-null at this time
2030    let mut isolate =
2031      unsafe { v8::Isolate::from_raw_isolate_ptr(self.v8_isolate_ptr()) };
2032    v8::scope!(let isolate_scope, &mut isolate);
2033    let context =
2034      v8::Local::new(isolate_scope, self.inner.main_realm.context());
2035    let mut scope = v8::ContextScope::new(isolate_scope, context);
2036    self.poll_event_loop_inner(cx, &mut scope, poll_options)
2037  }
2038
2039  fn poll_event_loop_inner(
2040    &self,
2041    cx: &mut Context,
2042    scope: &mut v8::PinScope,
2043    poll_options: PollEventLoopOptions,
2044  ) -> Poll<Result<(), CoreError>> {
2045    let has_inspector = self.inner.state.has_inspector.get();
2046    self.inner.state.waker.register(cx.waker());
2047
2048    if has_inspector {
2049      // We poll the inspector first.
2050      self.inspector().poll_sessions_from_event_loop(cx);
2051    }
2052
2053    if poll_options.pump_v8_message_loop {
2054      self.pump_v8_message_loop(scope)?;
2055    }
2056
2057    let realm = &self.inner.main_realm;
2058    let modules = &realm.0.module_map;
2059    let context_state = &realm.0.context_state;
2060    let exception_state = &context_state.exception_state;
2061
2062    modules.poll_progress(cx, scope)?;
2063
2064    // Resolve async ops, run all next tick callbacks and macrotasks callbacks
2065    // and only then check for any promise exceptions (`unhandledrejection`
2066    // handlers are run in macrotasks callbacks so we need to let them run
2067    // first).
2068    let (dispatched_ops, did_work) = Self::do_js_event_loop_tick_realm(
2069      cx,
2070      scope,
2071      context_state,
2072      exception_state,
2073    )?;
2074    exception_state.check_exception_condition(scope)?;
2075
2076    // Get the pending state from the main realm, or all realms
2077    let pending_state =
2078      EventLoopPendingState::new(scope, context_state, modules);
2079
2080    if !pending_state.is_pending() {
2081      if has_inspector {
2082        let inspector = self.inspector();
2083        let sessions_state = inspector.sessions_state();
2084
2085        if poll_options.wait_for_inspector && sessions_state.has_active {
2086          if sessions_state.has_blocking {
2087            return Poll::Pending;
2088          }
2089
2090          if sessions_state.has_nonblocking_wait_for_disconnect {
2091            let context = self.main_context();
2092            inspector.context_destroyed(scope, context);
2093            self.wait_for_inspector_disconnect();
2094            return Poll::Pending;
2095          }
2096        }
2097      }
2098
2099      return Poll::Ready(Ok(()));
2100    }
2101
2102    if !did_work
2103      && !context_state.timers.has_pending()
2104      && pending_state.has_refed_immediates > 0
2105    {
2106      Self::do_js_run_immediate_callbacks(scope, context_state)?;
2107    }
2108
2109    // Check if more async ops have been dispatched
2110    // during this turn of event loop.
2111    // If there are any pending background tasks, we also wake the runtime to
2112    // make sure we don't miss them.
2113    // TODO(andreubotella) The event loop will spin as long as there are pending
2114    // background tasks. We should look into having V8 notify us when a
2115    // background task is done.
2116    #[allow(clippy::suspicious_else_formatting, clippy::if_same_then_else)]
2117    {
2118      if pending_state.has_pending_background_tasks
2119        || pending_state.has_tick_scheduled
2120        || pending_state.has_outstanding_immediates
2121        || pending_state.has_refed_immediates > 0
2122        || pending_state.has_pending_promise_events
2123      {
2124        self.inner.state.waker.wake();
2125      } else
2126      // If ops were dispatched we may have progress on pending modules that we should re-check
2127      if (pending_state.has_pending_module_evaluation
2128        || pending_state.has_pending_dyn_module_evaluation)
2129        && dispatched_ops
2130      {
2131        self.inner.state.waker.wake();
2132      }
2133    }
2134
2135    if pending_state.has_pending_module_evaluation {
2136      if pending_state.has_pending_ops
2137        || pending_state.has_pending_dyn_imports
2138        || pending_state.has_pending_dyn_module_evaluation
2139        || pending_state.has_pending_background_tasks
2140        || pending_state.has_pending_external_ops
2141        || pending_state.has_tick_scheduled
2142        || pending_state.has_refed_immediates > 0
2143      {
2144        // pass, will be polled again
2145      } else {
2146        return Poll::Ready(Err(
2147          CoreErrorKind::Js(find_and_report_stalled_level_await_in_any_realm(
2148            scope, &realm.0,
2149          ))
2150          .into_box(),
2151        ));
2152      }
2153    }
2154
2155    if pending_state.has_pending_dyn_module_evaluation {
2156      if pending_state.has_pending_ops
2157        || pending_state.has_pending_dyn_imports
2158        || pending_state.has_pending_background_tasks
2159        || pending_state.has_pending_external_ops
2160        || pending_state.has_tick_scheduled
2161        || pending_state.has_refed_immediates > 0
2162      {
2163        // pass, will be polled again
2164      } else if realm.modules_idle() {
2165        return Poll::Ready(Err(
2166          CoreErrorKind::Js(find_and_report_stalled_level_await_in_any_realm(
2167            scope, &realm.0,
2168          ))
2169          .into_box(),
2170        ));
2171      } else {
2172        // Delay the above error by one spin of the event loop. A dynamic import
2173        // evaluation may complete during this, in which case the counter will
2174        // reset.
2175        realm.increment_modules_idle();
2176        self.inner.state.waker.wake();
2177      }
2178    }
2179
2180    Poll::Pending
2181  }
2182}
2183
2184fn find_and_report_stalled_level_await_in_any_realm(
2185  scope: &mut v8::PinScope,
2186  inner_realm: &JsRealmInner,
2187) -> Box<JsError> {
2188  let module_map = inner_realm.module_map();
2189  let messages = module_map.find_stalled_top_level_await(scope);
2190
2191  if !messages.is_empty() {
2192    // We are gonna print only a single message to provide a nice formatting
2193    // with source line of offending promise shown. Once user fixed it, then
2194    // they will get another error message for the next promise (but this
2195    // situation is gonna be very rare, if ever happening).
2196    let msg = v8::Local::new(scope, &messages[0]);
2197    let js_error = JsError::from_v8_message(scope, msg);
2198    return js_error;
2199  }
2200
2201  unreachable!("Expected at least one stalled top-level await");
2202}
2203
2204fn create_context<'s, 'i>(
2205  scope: &mut v8::PinScope<'s, 'i, ()>,
2206  global_template_middlewares: &[GlobalTemplateMiddlewareFn],
2207  global_object_middlewares: &[GlobalObjectMiddlewareFn],
2208  has_snapshot: bool,
2209) -> v8::Local<'s, v8::Context> {
2210  let context = if has_snapshot {
2211    // Try to load the 1st index first, embedder may have used 0th for something else (like node:vm).
2212    v8::Context::from_snapshot(scope, 1, Default::default()).unwrap_or_else(
2213      || v8::Context::from_snapshot(scope, 0, Default::default()).unwrap(),
2214    )
2215  } else {
2216    // Set up the global object template and create context from it.
2217    let mut global_object_template = v8::ObjectTemplate::new(scope);
2218    for middleware in global_template_middlewares {
2219      global_object_template = middleware(scope, global_object_template);
2220    }
2221
2222    global_object_template.set_internal_field_count(2);
2223    v8::Context::new(
2224      scope,
2225      v8::ContextOptions {
2226        global_template: Some(global_object_template),
2227        ..Default::default()
2228      },
2229    )
2230  };
2231
2232  let scope = &mut v8::ContextScope::new(scope, context);
2233
2234  let global = context.global(scope);
2235  for middleware in global_object_middlewares {
2236    middleware(scope, global);
2237  }
2238  context
2239}
2240
2241impl JsRuntimeForSnapshot {
2242  /// Create a new runtime, panicking if the process fails.
2243  pub fn new(options: RuntimeOptions) -> JsRuntimeForSnapshot {
2244    match Self::try_new(options) {
2245      Ok(runtime) => runtime,
2246      Err(err) => {
2247        panic!("Failed to initialize JsRuntime for snapshotting: {:?}", err);
2248      }
2249    }
2250  }
2251
2252  /// Try to create a new runtime, returning an error if the process fails.
2253  pub fn try_new(
2254    mut options: RuntimeOptions,
2255  ) -> Result<JsRuntimeForSnapshot, CoreError> {
2256    setup::init_v8(
2257      options.v8_platform.take(),
2258      true,
2259      options.unsafe_expose_natives_and_gc(),
2260      options.import_assertions_support.is_enabled(),
2261    );
2262
2263    let runtime = JsRuntime::new_inner(options, true)?;
2264    Ok(JsRuntimeForSnapshot(runtime))
2265  }
2266
2267  /// Takes a snapshot and consumes the runtime.
2268  ///
2269  /// `Error` can usually be downcast to `JsError`.
2270  pub fn snapshot(mut self) -> Box<[u8]> {
2271    // Ensure there are no live inspectors to prevent crashes.
2272    self.inner.prepare_for_cleanup();
2273    let original_sources =
2274      std::mem::take(&mut self.0.allocations.original_sources);
2275    let external_strings = original_sources
2276      .iter()
2277      .map(|s| s.as_str().as_bytes())
2278      .collect();
2279    let realm = JsRealm::clone(&self.inner.main_realm);
2280
2281    // Set the context to be snapshot's default context
2282    {
2283      jsrealm::context_scope!(scope, realm, self.v8_isolate());
2284      let default_context = v8::Context::new(scope, Default::default());
2285      scope.set_default_context(default_context);
2286
2287      let local_context = v8::Local::new(scope, realm.context());
2288      scope.add_context(local_context);
2289    }
2290
2291    // Borrow the source maps during the snapshot to avoid copies
2292    let source_maps = self
2293      .inner
2294      .state
2295      .source_mapper
2296      .borrow_mut()
2297      .take_ext_source_maps();
2298    let mut ext_source_maps = HashMap::with_capacity(source_maps.len());
2299    for (k, v) in &source_maps {
2300      ext_source_maps.insert(k.as_static_str().unwrap(), v.as_ref());
2301    }
2302
2303    // Serialize the module map and store its data in the snapshot.
2304    // TODO(mmastrac): This should deconstruct the realm into sidecar data rather than
2305    // extracting it from the realm and then tearing the realm down. IE, this should
2306    // probably be a method on `JsRealm` with a self-consuming parameter signature:
2307    // `fn into_sidecar_data(self) -> ...`.
2308    let sidecar_data = {
2309      let mut data_store = SnapshotStoreDataStore::default();
2310      let module_map_data = {
2311        let module_map = realm.0.module_map();
2312        module_map.serialize_for_snapshotting(&mut data_store)
2313      };
2314      let function_templates_data = {
2315        let function_templates = realm.0.function_templates();
2316        let f = std::mem::take(&mut *function_templates.borrow_mut());
2317
2318        f.serialize_for_snapshotting(&mut data_store)
2319      };
2320      let maybe_js_handled_promise_rejection_cb = {
2321        let context_state = &realm.0.context_state;
2322        let exception_state = &context_state.exception_state;
2323        exception_state
2324          .js_handled_promise_rejection_cb
2325          .borrow()
2326          .clone()
2327      }
2328      .map(|cb| data_store.register(cb));
2329
2330      let ext_import_meta_proto = realm
2331        .0
2332        .context_state
2333        .ext_import_meta_proto
2334        .borrow()
2335        .clone()
2336        .map(|p| data_store.register(p));
2337
2338      let snapshotted_data = SnapshottedData {
2339        module_map_data,
2340        function_templates_data,
2341        op_count: self.inner.op_count,
2342        addl_refs_count: self.inner.addl_refs_count,
2343        source_count: self.inner.source_count,
2344        extensions: self.inner.extensions.clone(),
2345        js_handled_promise_rejection_cb: maybe_js_handled_promise_rejection_cb,
2346        ext_import_meta_proto,
2347        ext_source_maps,
2348        external_strings,
2349      };
2350
2351      jsrealm::context_scope!(scope, realm, self.v8_isolate());
2352      snapshot::store_snapshotted_data_for_snapshot(
2353        scope,
2354        realm.context().clone(),
2355        snapshotted_data,
2356        data_store,
2357      )
2358    };
2359    drop(realm);
2360
2361    let v8_data = self
2362      .0
2363      .inner
2364      .prepare_for_snapshot()
2365      .create_blob(v8::FunctionCodeHandling::Keep)
2366      .unwrap();
2367
2368    snapshot::serialize(v8_data, sidecar_data)
2369  }
2370}
2371
2372#[derive(Clone, Copy, PartialEq, Eq, Debug)]
2373pub(crate) struct EventLoopPendingState {
2374  has_pending_ops: bool,
2375  has_pending_refed_ops: bool,
2376  has_pending_dyn_imports: bool,
2377  has_pending_dyn_module_evaluation: bool,
2378  has_pending_module_evaluation: bool,
2379  has_pending_background_tasks: bool,
2380  has_tick_scheduled: bool,
2381  has_pending_promise_events: bool,
2382  has_pending_external_ops: bool,
2383  has_outstanding_immediates: bool,
2384  has_refed_immediates: u32,
2385}
2386
2387impl EventLoopPendingState {
2388  /// Collect event loop state from all the sub-states.
2389  pub fn new(
2390    scope: &mut v8::PinScope<()>,
2391    state: &ContextState,
2392    modules: &ModuleMap,
2393  ) -> Self {
2394    let num_unrefed_ops = state.unrefed_ops.borrow().len();
2395    let num_pending_ops = state.pending_ops.len();
2396    let has_pending_tasks = state.task_spawner_factory.has_pending_tasks();
2397    let has_pending_timers = !state.timers.is_empty();
2398    let has_pending_refed_timers = state.timers.has_pending_timers();
2399    let has_pending_dyn_imports = modules.has_pending_dynamic_imports();
2400    let has_pending_dyn_module_evaluation =
2401      modules.has_pending_dyn_module_evaluation();
2402    let has_pending_module_evaluation = modules.has_pending_module_evaluation();
2403    let has_pending_promise_events = !state
2404      .exception_state
2405      .pending_promise_rejections
2406      .borrow()
2407      .is_empty()
2408      || !state
2409        .exception_state
2410        .pending_handled_promise_rejections
2411        .borrow()
2412        .is_empty();
2413    let has_pending_refed_ops = has_pending_tasks
2414      || has_pending_refed_timers
2415      || num_pending_ops > num_unrefed_ops;
2416    let (has_outstanding_immediates, has_refed_immediates) = {
2417      let info = state.immediate_info.borrow();
2418      (info.has_outstanding, info.ref_count)
2419    };
2420    EventLoopPendingState {
2421      has_pending_ops: has_pending_refed_ops
2422        || has_pending_timers
2423        || (num_pending_ops > 0),
2424      has_pending_refed_ops,
2425      has_pending_dyn_imports,
2426      has_pending_dyn_module_evaluation,
2427      has_pending_module_evaluation,
2428      has_pending_background_tasks: scope.has_pending_background_tasks(),
2429      has_tick_scheduled: state.has_next_tick_scheduled.get(),
2430      has_pending_promise_events,
2431      has_pending_external_ops: state.external_ops_tracker.has_pending_ops(),
2432      has_outstanding_immediates,
2433      has_refed_immediates,
2434    }
2435  }
2436
2437  /// Collect event loop state from all the states stored in the scope.
2438  pub fn new_from_scope(scope: &mut v8::PinScope) -> Self {
2439    let module_map = JsRealm::module_map_from(scope);
2440    let context_state = JsRealm::state_from_scope(scope);
2441    Self::new(scope, &context_state, &module_map)
2442  }
2443
2444  pub fn is_pending(&self) -> bool {
2445    self.has_pending_refed_ops
2446      || self.has_pending_dyn_imports
2447      || self.has_pending_dyn_module_evaluation
2448      || self.has_pending_module_evaluation
2449      || self.has_pending_background_tasks
2450      || self.has_tick_scheduled
2451      || self.has_refed_immediates > 0
2452      || self.has_pending_promise_events
2453      || self.has_pending_external_ops
2454  }
2455}
2456
2457extern "C" fn near_heap_limit_callback<F>(
2458  data: *mut c_void,
2459  current_heap_limit: usize,
2460  initial_heap_limit: usize,
2461) -> usize
2462where
2463  F: FnMut(usize, usize) -> usize,
2464{
2465  // SAFETY: The data is a pointer to the Rust callback function. It is stored
2466  // in `JsRuntime::allocations` and thus is guaranteed to outlive the isolate.
2467  let callback = unsafe { &mut *(data as *mut F) };
2468  callback(current_heap_limit, initial_heap_limit)
2469}
2470
2471impl JsRuntimeState {
2472  pub(crate) fn inspector(&self) -> Rc<JsRuntimeInspector> {
2473    self.inspector.borrow().as_ref().unwrap().clone()
2474  }
2475
2476  /// Called by `bindings::host_import_module_dynamically_callback`
2477  /// after initiating new dynamic import load.
2478  pub fn notify_new_dynamic_import(&self) {
2479    // Notify event loop to poll again soon.
2480    self.waker.wake();
2481  }
2482
2483  /// Performs an action with the inspector, if we have one
2484  pub(crate) fn with_inspector<T>(
2485    &self,
2486    mut f: impl FnMut(&JsRuntimeInspector) -> T,
2487  ) -> Option<T> {
2488    // Fast path
2489    if !self.has_inspector.get() {
2490      return None;
2491    }
2492    self
2493      .inspector
2494      .borrow()
2495      .as_ref()
2496      .map(|inspector| f(inspector))
2497  }
2498}
2499
2500// Related to module loading
2501impl JsRuntime {
2502  #[cfg(test)]
2503  pub(crate) fn instantiate_module(
2504    &mut self,
2505    id: ModuleId,
2506  ) -> Result<(), v8::Global<v8::Value>> {
2507    let isolate = &mut *self.inner.v8_isolate;
2508    let realm = JsRealm::clone(&self.inner.main_realm);
2509    jsrealm::context_scope!(scope, realm, isolate);
2510    realm.instantiate_module(scope, id)
2511  }
2512
2513  /// Evaluates an already instantiated ES module.
2514  ///
2515  /// Returns a future that resolves when module promise resolves.
2516  /// Implementors must manually call [`JsRuntime::run_event_loop`] to drive
2517  /// module evaluation future.
2518  ///
2519  /// Modules with top-level await are treated like promises, so a `throw` in the top-level
2520  /// block of a module is treated as an unhandled rejection. These rejections are provided to
2521  /// the unhandled promise rejection handler, which has the opportunity to pass them off to
2522  /// error-handling code. If those rejections are not handled (indicated by a `false` return
2523  /// from that unhandled promise rejection handler), then the runtime will terminate.
2524  ///
2525  /// The future provided by `mod_evaluate` will only return errors in the case where
2526  /// the runtime is shutdown and no longer available to provide unhandled rejection
2527  /// information.
2528  ///
2529  /// This function panics if module has not been instantiated.
2530  pub fn mod_evaluate(
2531    &mut self,
2532    id: ModuleId,
2533  ) -> impl Future<Output = Result<(), CoreError>> + use<> {
2534    let isolate = &mut *self.inner.v8_isolate;
2535    let realm = &self.inner.main_realm;
2536    jsrealm::context_scope!(scope, realm, isolate);
2537    self.inner.main_realm.0.module_map.mod_evaluate(scope, id)
2538  }
2539
2540  /// Asynchronously load specified module and all of its dependencies.
2541  ///
2542  /// The module will be marked as "main", and because of that
2543  /// "import.meta.main" will return true when checked inside that module.
2544  ///
2545  /// The source may be any type that implements the internal [`IntoModuleCodeString`] trait, but
2546  /// it is highly recommended that embedders use the [`ascii_str!`] to generate the fastest version
2547  /// of strings for v8 to handle. If the strings are not static, you may also pass a [`String`]
2548  /// generated by the [`format!`] macro.
2549  ///
2550  /// User must call [`JsRuntime::mod_evaluate`] with returned `ModuleId`
2551  /// manually after load is finished.
2552  pub async fn load_main_es_module_from_code(
2553    &mut self,
2554    specifier: &ModuleSpecifier,
2555    code: impl IntoModuleCodeString,
2556  ) -> Result<ModuleId, CoreError> {
2557    let isolate = &mut self.inner.v8_isolate;
2558    self
2559      .inner
2560      .main_realm
2561      .load_main_es_module_from_code(
2562        isolate,
2563        specifier,
2564        Some(code.into_module_code()),
2565      )
2566      .await
2567  }
2568
2569  /// Asynchronously load specified module and all of its dependencies, retrieving
2570  /// the module from the supplied [`ModuleLoader`].
2571  ///
2572  /// The module will be marked as "main", and because of that
2573  /// "import.meta.main" will return true when checked inside that module.
2574  ///
2575  /// User must call [`JsRuntime::mod_evaluate`] with returned `ModuleId`
2576  /// manually after load is finished.
2577  pub async fn load_main_es_module(
2578    &mut self,
2579    specifier: &ModuleSpecifier,
2580  ) -> Result<ModuleId, CoreError> {
2581    let isolate = &mut self.inner.v8_isolate;
2582    self
2583      .inner
2584      .main_realm
2585      .load_main_es_module_from_code(isolate, specifier, None)
2586      .await
2587  }
2588
2589  /// Asynchronously load specified ES module and all of its dependencies from the
2590  /// provided source.
2591  ///
2592  /// This method is meant to be used when loading some utility code that
2593  /// might be later imported by the main module (ie. an entry point module).
2594  ///
2595  /// The source may be any type that implements the internal [`IntoModuleCodeString`] trait, but
2596  /// it is highly recommended that embedders use the [`ascii_str!`] to generate the fastest version
2597  /// of strings for v8 to handle. If the strings are not static, you may also pass a [`String`]
2598  /// generated by the [`format!`] macro.
2599  ///
2600  /// User must call [`JsRuntime::mod_evaluate`] with returned `ModuleId`
2601  /// manually after load is finished.
2602  pub async fn load_side_es_module_from_code(
2603    &mut self,
2604    specifier: &ModuleSpecifier,
2605    code: impl IntoModuleCodeString,
2606  ) -> Result<ModuleId, CoreError> {
2607    let isolate = &mut self.inner.v8_isolate;
2608    self
2609      .inner
2610      .main_realm
2611      .load_side_es_module_from_code(
2612        isolate,
2613        specifier,
2614        Some(code.into_module_code()),
2615      )
2616      .await
2617  }
2618
2619  /// Asynchronously load specified ES module and all of its dependencies, retrieving
2620  /// the module from the supplied [`ModuleLoader`].
2621  ///
2622  /// This method is meant to be used when loading some utility code that
2623  /// might be later imported by the main module (ie. an entry point module).
2624  ///
2625  /// User must call [`JsRuntime::mod_evaluate`] with returned `ModuleId`
2626  /// manually after load is finished.
2627  pub async fn load_side_es_module(
2628    &mut self,
2629    specifier: &ModuleSpecifier,
2630  ) -> Result<ModuleId, CoreError> {
2631    let isolate = &mut self.inner.v8_isolate;
2632    self
2633      .inner
2634      .main_realm
2635      .load_side_es_module_from_code(isolate, specifier, None)
2636      .await
2637  }
2638
2639  /// Load and evaluate an ES module provided the specifier and source code.
2640  ///
2641  /// The module should not have Top-Level Await (that is, it should be
2642  /// possible to evaluate it synchronously).
2643  ///
2644  /// It is caller's responsibility to ensure that not duplicate specifiers are
2645  /// passed to this method.
2646  pub fn lazy_load_es_module_with_code(
2647    &mut self,
2648    specifier: impl IntoModuleName,
2649    code: impl IntoModuleCodeString,
2650  ) -> Result<v8::Global<v8::Value>, CoreError> {
2651    let isolate = &mut self.inner.v8_isolate;
2652    self.inner.main_realm.lazy_load_es_module_with_code(
2653      isolate,
2654      specifier.into_module_name(),
2655      code.into_module_code(),
2656    )
2657  }
2658
2659  fn do_js_run_immediate_callbacks<'s, 'i>(
2660    scope: &mut v8::PinScope<'s, 'i>,
2661    context_state: &ContextState,
2662  ) -> Result<(), Box<JsError>> {
2663    v8::tc_scope!(let tc_scope, scope);
2664
2665    let undefined = v8::undefined(tc_scope).into();
2666    let run_immediate_callbacks_cb =
2667      context_state.run_immediate_callbacks_cb.borrow();
2668    let run_immediate_callbacks_cb =
2669      run_immediate_callbacks_cb.as_ref().unwrap().open(tc_scope);
2670
2671    run_immediate_callbacks_cb.call(tc_scope, undefined, &[]);
2672
2673    if let Some(exception) = tc_scope.exception() {
2674      let e: Result<(), Box<JsError>> =
2675        exception_to_err_result(tc_scope, exception, false, true);
2676      return e;
2677    }
2678    Ok(())
2679  }
2680
2681  fn do_js_event_loop_tick_realm<'s, 'i>(
2682    cx: &mut Context,
2683    scope: &mut v8::PinScope<'s, 'i>,
2684    context_state: &ContextState,
2685    exception_state: &ExceptionState,
2686  ) -> Result<(bool, bool), Box<JsError>> {
2687    let mut dispatched_ops = false;
2688    let mut did_work = false;
2689
2690    // Poll any pending task spawner tasks. Note that we need to poll separately because otherwise
2691    // Rust will extend the lifetime of the borrow longer than we expect.
2692    let mut retries = 3;
2693    while let Poll::Ready(tasks) =
2694      context_state.task_spawner_factory.poll_inner(cx)
2695    {
2696      // TODO(mmastrac): we are using this flag
2697      dispatched_ops = true;
2698      for task in tasks {
2699        task(scope);
2700      }
2701      // We may need to perform a microtask checkpoint here
2702      scope.perform_microtask_checkpoint();
2703
2704      // We don't want tasks that spawn other tasks to starve the event loop, so break
2705      // after three times around and allow the remainder of the event loop to spin.
2706      retries -= 1;
2707      if retries == 0 {
2708        cx.waker().wake_by_ref();
2709        break;
2710      }
2711    }
2712
2713    // We return async responses to JS in bounded batches. Note that because
2714    // we're passing these to JS as arguments, it is possible to overflow the
2715    // JS stack by just passing too many.
2716    const MAX_VEC_SIZE_FOR_OPS: usize = 1024;
2717
2718    // each batch is a flat vector of tuples:
2719    // `[promise_id1, op_result1, promise_id2, op_result2, ...]`
2720    // promise_id is a simple integer, op_result is an ops::OpResult
2721    // which contains a value OR an error, encoded as a tuple.
2722    // This batch is received in JS via the special `arguments` variable
2723    // and then each tuple is used to resolve or reject promises
2724    let mut args: SmallVec<[v8::Local<v8::Value>; 32]> =
2725      SmallVec::with_capacity(32);
2726
2727    loop {
2728      if args.len() >= MAX_VEC_SIZE_FOR_OPS {
2729        // We have too many, bail for now but re-wake the waker
2730        cx.waker().wake_by_ref();
2731        break;
2732      }
2733
2734      let Poll::Ready((promise_id, op_id, res)) =
2735        context_state.pending_ops.poll_ready(cx)
2736      else {
2737        break;
2738      };
2739
2740      let res = res.unwrap(scope);
2741
2742      {
2743        let op_ctx = &context_state.op_ctxs[op_id as usize];
2744        if op_ctx.metrics_enabled() {
2745          if res.is_ok() {
2746            dispatch_metrics_async(op_ctx, OpMetricsEvent::CompletedAsync);
2747          } else {
2748            dispatch_metrics_async(op_ctx, OpMetricsEvent::ErrorAsync);
2749          }
2750        }
2751      }
2752
2753      context_state.unrefed_ops.borrow_mut().remove(&promise_id);
2754      context_state
2755        .activity_traces
2756        .complete(RuntimeActivityType::AsyncOp, promise_id as _);
2757      dispatched_ops |= true;
2758      did_work |= true;
2759      args.push(v8::Integer::new(scope, promise_id).into());
2760      args.push(v8::Boolean::new(scope, res.is_ok()).into());
2761      args.push(res.unwrap_or_else(std::convert::identity));
2762    }
2763
2764    let undefined: v8::Local<v8::Value> = v8::undefined(scope).into();
2765    let has_tick_scheduled = context_state.has_next_tick_scheduled.get();
2766    dispatched_ops |= has_tick_scheduled;
2767
2768    while let Some((promise, result)) = exception_state
2769      .pending_handled_promise_rejections
2770      .borrow_mut()
2771      .pop_front()
2772    {
2773      if let Some(handler) = exception_state
2774        .js_handled_promise_rejection_cb
2775        .borrow()
2776        .as_ref()
2777      {
2778        let function = handler.open(scope);
2779
2780        let args = [
2781          v8::Local::new(scope, promise).into(),
2782          v8::Local::new(scope, result),
2783        ];
2784        function.call(scope, undefined, &args);
2785      }
2786    }
2787
2788    let rejections = if !exception_state
2789      .pending_promise_rejections
2790      .borrow_mut()
2791      .is_empty()
2792    {
2793      // Avoid holding the pending rejection lock longer than necessary
2794      let mut pending_rejections =
2795        exception_state.pending_promise_rejections.borrow_mut();
2796      let mut rejections = VecDeque::default();
2797      std::mem::swap(&mut *pending_rejections, &mut rejections);
2798      drop(pending_rejections);
2799
2800      let arr = v8::Array::new(scope, (rejections.len() * 2) as i32);
2801      let mut index = 0;
2802      for rejection in rejections.into_iter() {
2803        let value = v8::Local::new(scope, rejection.0);
2804        arr.set_index(scope, index, value.into());
2805        index += 1;
2806        let value = v8::Local::new(scope, rejection.1);
2807        arr.set_index(scope, index, value);
2808        index += 1;
2809      }
2810      arr.into()
2811    } else {
2812      undefined
2813    };
2814
2815    args.push(rejections);
2816
2817    // TODO(mmastrac): timer dispatch should be done via direct function call, but we will have to start
2818    // storing the exception-reporting callback.
2819    let timers = match context_state.timers.poll_timers(cx) {
2820      Poll::Ready(timers) => {
2821        did_work |= true;
2822        let traces_enabled = context_state.activity_traces.is_enabled();
2823        let arr = v8::Array::new(scope, (timers.len() * 3) as _);
2824        #[allow(clippy::needless_range_loop)]
2825        for i in 0..timers.len() {
2826          if traces_enabled {
2827            // Timer and interval traces both use RuntimeActivityType::Timer
2828            context_state
2829              .activity_traces
2830              .complete(RuntimeActivityType::Timer, timers[i].0 as _);
2831          }
2832          // depth, id, function
2833          let value = v8::Integer::new(scope, timers[i].1.1 as _);
2834          arr.set_index(scope, (i * 3) as _, value.into());
2835          let value = v8::Number::new(scope, timers[i].0 as _);
2836          arr.set_index(scope, (i * 3 + 1) as _, value.into());
2837          let value = v8::Local::new(scope, timers[i].1.0.clone());
2838          arr.set_index(scope, (i * 3 + 2) as _, value.into());
2839        }
2840        arr.into()
2841      }
2842      _ => undefined,
2843    };
2844    args.push(timers);
2845
2846    let has_tick_scheduled = v8::Boolean::new(scope, has_tick_scheduled);
2847    args.push(has_tick_scheduled.into());
2848
2849    v8::tc_scope!(let tc_scope, scope);
2850
2851    let js_event_loop_tick_cb = context_state.js_event_loop_tick_cb.borrow();
2852    let js_event_loop_tick_cb =
2853      js_event_loop_tick_cb.as_ref().unwrap().open(tc_scope);
2854
2855    js_event_loop_tick_cb.call(tc_scope, undefined, args.as_slice());
2856
2857    if let Some(exception) = tc_scope.exception() {
2858      return exception_to_err_result(tc_scope, exception, false, true);
2859    }
2860
2861    if tc_scope.has_terminated() || tc_scope.is_execution_terminating() {
2862      return Ok((false, false));
2863    }
2864
2865    Ok((dispatched_ops, did_work))
2866  }
2867}
2868
2869fn mark_as_loaded_from_fs_during_snapshot(
2870  files_loaded: &mut Vec<&'static str>,
2871  source: &ExtensionFileSourceCode,
2872) {
2873  #[allow(deprecated)]
2874  if let ExtensionFileSourceCode::LoadedFromFsDuringSnapshot(path) = source {
2875    files_loaded.push(path);
2876  }
2877}