use super::bindings;
use super::bindings::create_exports_for_ops_virtual_module;
use super::bindings::watch_promise;
use super::exception_state::ExceptionState;
use super::jsrealm::JsRealmInner;
use super::op_driver::OpDriver;
use super::setup;
use super::snapshot;
use super::stats::RuntimeActivityStatsFactory;
use super::v8_static_strings::*;
use super::SnapshotStoreDataStore;
use super::SnapshottedData;
use crate::ascii_str;
use crate::ascii_str_include;
use crate::error::exception_to_err_result;
use crate::error::AnyError;
use crate::error::GetErrorClassFn;
use crate::error::JsError;
use crate::extension_set;
use crate::extension_set::LoadedSources;
use crate::extensions::GlobalObjectMiddlewareFn;
use crate::extensions::GlobalTemplateMiddlewareFn;
use crate::include_js_files;
use crate::inspector::JsRuntimeInspector;
use crate::module_specifier::ModuleSpecifier;
use crate::modules::default_import_meta_resolve_cb;
use crate::modules::CustomModuleEvaluationCb;
use crate::modules::ExtModuleLoader;
use crate::modules::ImportMetaResolveCallback;
use crate::modules::IntoModuleCodeString;
use crate::modules::IntoModuleName;
use crate::modules::ModuleId;
use crate::modules::ModuleLoader;
use crate::modules::ModuleMap;
use crate::modules::ModuleName;
use crate::modules::RequestedModuleType;
use crate::modules::ValidateImportAttributesCb;
use crate::ops_metrics::dispatch_metrics_async;
use crate::ops_metrics::OpMetricsFactoryFn;
use crate::runtime::ContextState;
use crate::runtime::JsRealm;
use crate::runtime::OpDriverImpl;
use crate::source_map::SourceMapData;
use crate::source_map::SourceMapGetter;
use crate::source_map::SourceMapper;
use crate::stats::RuntimeActivityType;
use crate::Extension;
use crate::ExtensionFileSource;
use crate::ExtensionFileSourceCode;
use crate::FastStaticString;
use crate::FastString;
use crate::FeatureChecker;
use crate::ModuleCodeString;
use crate::NoopModuleLoader;
use crate::OpMetricsEvent;
use crate::OpState;
use anyhow::anyhow;
use anyhow::bail;
use anyhow::Context as _;
use anyhow::Error;
use futures::future::poll_fn;
use futures::task::AtomicWaker;
use futures::Future;
use futures::FutureExt;
use smallvec::SmallVec;
use std::any::Any;
use std::cell::Cell;
use std::cell::RefCell;
use std::collections::HashMap;
use std::collections::VecDeque;
use std::ffi::c_void;
use std::mem::ManuallyDrop;
use std::ops::Deref;
use std::ops::DerefMut;
use std::pin::Pin;
use std::rc::Rc;
use std::sync::Arc;
use std::sync::Mutex;
use std::task::Context;
use std::task::Poll;
use std::task::Waker;
use v8::Isolate;
pub type WaitForInspectorDisconnectCallback = Box<dyn Fn()>;
const STATE_DATA_OFFSET: u32 = 0;
pub type ExtensionTranspiler =
dyn Fn(
ModuleName,
ModuleCodeString,
) -> Result<(ModuleCodeString, Option<SourceMapData>), AnyError>;
#[derive(Default)]
pub(crate) struct IsolateAllocations {
pub(crate) external_refs: Option<Box<v8::ExternalReferences>>,
pub(crate) externalized_sources: Box<[v8::OneByteConst]>,
pub(crate) original_sources: Box<[FastString]>,
pub(crate) near_heap_limit_callback_data:
Option<(Box<RefCell<dyn Any>>, v8::NearHeapLimitCallback)>,
}
pub(crate) struct ManuallyDropRc<T>(ManuallyDrop<Rc<T>>);
impl<T> ManuallyDropRc<T> {
#[allow(unused)]
pub fn clone(&self) -> Rc<T> {
self.0.deref().clone()
}
}
impl<T> Deref for ManuallyDropRc<T> {
type Target = Rc<T>;
fn deref(&self) -> &Self::Target {
self.0.deref()
}
}
impl<T> DerefMut for ManuallyDropRc<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.0.deref_mut()
}
}
pub(crate) struct InnerIsolateState {
will_snapshot: bool,
extension_count: usize,
op_count: usize,
source_count: usize,
addl_refs_count: usize,
main_realm: ManuallyDrop<JsRealm>,
pub(crate) state: ManuallyDropRc<JsRuntimeState>,
v8_isolate: ManuallyDrop<v8::OwnedIsolate>,
cpp_heap: ManuallyDrop<v8::UniqueRef<v8::cppgc::Heap>>,
}
impl InnerIsolateState {
pub fn prepare_for_cleanup(&mut self) {
self.main_realm.0.context_state.pending_ops.shutdown();
let inspector = self.state.inspector.take();
self.state.op_state.borrow_mut().clear();
if let Some(inspector) = inspector {
assert_eq!(
Rc::strong_count(&inspector),
1,
"The inspector must be dropped before the runtime"
);
}
}
pub fn cleanup(&mut self) {
self.prepare_for_cleanup();
let state_ptr = self.v8_isolate.get_data(STATE_DATA_OFFSET);
_ = unsafe { Rc::from_raw(state_ptr as *const JsRuntimeState) };
unsafe {
ManuallyDrop::take(&mut self.main_realm).0.destroy();
}
debug_assert_eq!(Rc::strong_count(&self.state), 1);
}
pub fn prepare_for_snapshot(mut self) -> v8::OwnedIsolate {
self.cleanup();
let (state, _cpp_heap, isolate) = unsafe {
(
ManuallyDrop::take(&mut self.state.0),
ManuallyDrop::take(&mut self.cpp_heap),
ManuallyDrop::take(&mut self.v8_isolate),
)
};
std::mem::forget(self);
drop(state);
isolate
}
}
impl Drop for InnerIsolateState {
fn drop(&mut self) {
self.cleanup();
unsafe {
ManuallyDrop::drop(&mut self.state.0);
if self.will_snapshot {
eprintln!("WARNING: v8::OwnedIsolate for snapshot was leaked");
} else {
ManuallyDrop::drop(&mut self.cpp_heap);
ManuallyDrop::drop(&mut self.v8_isolate);
}
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(crate) enum InitMode {
New,
FromSnapshot {
skip_op_registration: bool,
},
}
impl InitMode {
fn from_options(options: &RuntimeOptions) -> Self {
match options.startup_snapshot {
None => Self::New,
Some(_) => Self::FromSnapshot {
skip_op_registration: options.skip_op_registration,
},
}
}
#[inline]
pub fn needs_ops_bindings(&self) -> bool {
!matches!(
self,
InitMode::FromSnapshot {
skip_op_registration: true
}
)
}
}
#[derive(Default)]
struct PromiseFuture {
resolved: Cell<Option<Result<v8::Global<v8::Value>, Error>>>,
waker: Cell<Option<Waker>>,
}
#[derive(Clone, Default)]
struct RcPromiseFuture(Rc<PromiseFuture>);
impl RcPromiseFuture {
pub fn new(res: Result<v8::Global<v8::Value>, Error>) -> Self {
Self(Rc::new(PromiseFuture {
resolved: Some(res).into(),
..Default::default()
}))
}
}
impl Future for RcPromiseFuture {
type Output = Result<v8::Global<v8::Value>, Error>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
if let Some(resolved) = this.0.resolved.take() {
Poll::Ready(resolved)
} else {
this.0.waker.set(Some(cx.waker().clone()));
Poll::Pending
}
}
}
static VIRTUAL_OPS_MODULE_NAME: FastStaticString = ascii_str!("ext:core/ops");
pub(crate) struct InternalSourceFile {
pub specifier: FastStaticString,
pub source: FastStaticString,
}
macro_rules! internal_source_file {
($str_:literal) => {{
InternalSourceFile {
specifier: ascii_str!(concat!("ext:core/", $str_)),
source: ascii_str_include!(concat!("../", $str_)),
}
}};
}
pub(crate) static CONTEXT_SETUP_SOURCES: [InternalSourceFile; 2] = [
internal_source_file!("00_primordials.js"),
internal_source_file!("00_infra.js"),
];
pub(crate) static BUILTIN_SOURCES: [InternalSourceFile; 2] = [
internal_source_file!("01_core.js"),
internal_source_file!("02_error.js"),
];
pub(crate) static BUILTIN_ES_MODULES: [ExtensionFileSource; 1] =
include_js_files!(core "mod.js",);
#[cfg(test)]
pub(crate) const NO_OF_BUILTIN_MODULES: usize = 2;
pub struct JsRuntime {
pub(crate) inner: InnerIsolateState,
pub(crate) allocations: IsolateAllocations,
files_loaded_from_fs_during_snapshot: Vec<&'static str>,
is_main_runtime: bool,
}
pub struct JsRuntimeForSnapshot(JsRuntime);
impl Deref for JsRuntimeForSnapshot {
type Target = JsRuntime;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for JsRuntimeForSnapshot {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
pub struct CrossIsolateStore<T>(Arc<Mutex<CrossIsolateStoreInner<T>>>);
struct CrossIsolateStoreInner<T> {
map: HashMap<u32, T>,
last_id: u32,
}
impl<T> CrossIsolateStore<T> {
pub(crate) fn insert(&self, value: T) -> u32 {
let mut store = self.0.lock().unwrap();
let last_id = store.last_id;
store.map.insert(last_id, value);
store.last_id += 1;
last_id
}
pub(crate) fn take(&self, id: u32) -> Option<T> {
let mut store = self.0.lock().unwrap();
store.map.remove(&id)
}
}
impl<T> Default for CrossIsolateStore<T> {
fn default() -> Self {
CrossIsolateStore(Arc::new(Mutex::new(CrossIsolateStoreInner {
map: Default::default(),
last_id: 0,
})))
}
}
impl<T> Clone for CrossIsolateStore<T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
pub type SharedArrayBufferStore =
CrossIsolateStore<v8::SharedRef<v8::BackingStore>>;
pub type CompiledWasmModuleStore = CrossIsolateStore<v8::CompiledWasmModule>;
pub struct JsRuntimeState {
pub(crate) source_mapper: RefCell<SourceMapper<Rc<dyn SourceMapGetter>>>,
pub(crate) op_state: Rc<RefCell<OpState>>,
pub(crate) shared_array_buffer_store: Option<SharedArrayBufferStore>,
pub(crate) compiled_wasm_module_store: Option<CompiledWasmModuleStore>,
wait_for_inspector_disconnect_callback:
Option<WaitForInspectorDisconnectCallback>,
pub(crate) validate_import_attributes_cb: Option<ValidateImportAttributesCb>,
pub(crate) custom_module_evaluation_cb: Option<CustomModuleEvaluationCb>,
waker: Arc<AtomicWaker>,
inspector: RefCell<Option<Rc<RefCell<JsRuntimeInspector>>>>,
has_inspector: Cell<bool>,
}
#[derive(Default)]
pub struct RuntimeOptions {
pub source_map_getter: Option<Rc<dyn SourceMapGetter>>,
pub get_error_class_fn: Option<GetErrorClassFn>,
pub module_loader: Option<Rc<dyn ModuleLoader>>,
pub extension_transpiler: Option<Rc<ExtensionTranspiler>>,
pub op_metrics_factory_fn: Option<OpMetricsFactoryFn>,
pub extensions: Vec<Extension>,
pub startup_snapshot: Option<&'static [u8]>,
pub skip_op_registration: bool,
pub create_params: Option<v8::CreateParams>,
pub v8_platform: Option<v8::SharedRef<v8::Platform>>,
pub shared_array_buffer_store: Option<SharedArrayBufferStore>,
pub compiled_wasm_module_store: Option<CompiledWasmModuleStore>,
pub inspector: bool,
pub is_main: bool,
#[cfg(any(test, feature = "unsafe_runtime_options"))]
pub unsafe_expose_natives_and_gc: bool,
pub feature_checker: Option<Arc<FeatureChecker>>,
pub validate_import_attributes_cb: Option<ValidateImportAttributesCb>,
pub import_meta_resolve_callback: Option<ImportMetaResolveCallback>,
pub wait_for_inspector_disconnect_callback:
Option<WaitForInspectorDisconnectCallback>,
pub custom_module_evaluation_cb: Option<CustomModuleEvaluationCb>,
}
impl RuntimeOptions {
#[cfg(any(test, feature = "unsafe_runtime_options"))]
fn unsafe_expose_natives_and_gc(&self) -> bool {
self.unsafe_expose_natives_and_gc
}
#[cfg(not(any(test, feature = "unsafe_runtime_options")))]
fn unsafe_expose_natives_and_gc(&self) -> bool {
false
}
}
#[derive(Copy, Clone, Debug)]
pub struct PollEventLoopOptions {
pub wait_for_inspector: bool,
pub pump_v8_message_loop: bool,
}
impl Default for PollEventLoopOptions {
fn default() -> Self {
Self {
wait_for_inspector: false,
pump_v8_message_loop: true,
}
}
}
#[derive(Default)]
pub struct CreateRealmOptions {
pub module_loader: Option<Rc<dyn ModuleLoader>>,
}
impl JsRuntime {
#[cfg(not(any(test, feature = "unsafe_runtime_options")))]
pub fn init_platform(v8_platform: Option<v8::SharedRef<v8::Platform>>) {
setup::init_v8(v8_platform, cfg!(test), false);
}
#[cfg(any(test, feature = "unsafe_runtime_options"))]
pub fn init_platform(
v8_platform: Option<v8::SharedRef<v8::Platform>>,
expose_natives: bool,
) {
setup::init_v8(v8_platform, cfg!(test), expose_natives);
}
pub fn new(mut options: RuntimeOptions) -> JsRuntime {
setup::init_v8(
options.v8_platform.take(),
cfg!(test),
options.unsafe_expose_natives_and_gc(),
);
match JsRuntime::new_inner(options, false) {
Ok(runtime) => runtime,
Err(err) => {
panic!("Failed to initialize a JsRuntime: {:?}", err);
}
}
}
pub(crate) fn state_from(isolate: &v8::Isolate) -> Rc<JsRuntimeState> {
let state_ptr = isolate.get_data(STATE_DATA_OFFSET);
let state_rc =
unsafe { Rc::from_raw(state_ptr as *const JsRuntimeState) };
let state = state_rc.clone();
std::mem::forget(state_rc);
state
}
pub fn op_state_from(isolate: &v8::Isolate) -> Rc<RefCell<OpState>> {
let state = Self::state_from(isolate);
state.op_state.clone()
}
pub(crate) fn has_more_work(scope: &mut v8::HandleScope) -> bool {
EventLoopPendingState::new_from_scope(scope).is_pending()
}
fn new_inner(
mut options: RuntimeOptions,
will_snapshot: bool,
) -> Result<JsRuntime, Error> {
let init_mode = InitMode::from_options(&options);
let mut extensions = std::mem::take(&mut options.extensions);
let mut isolate_allocations = IsolateAllocations::default();
let mut op_state = OpState::new(options.feature_checker.take());
extension_set::setup_op_state(&mut op_state, &mut extensions);
let mut files_loaded = Vec::with_capacity(128);
let mut source_mapper: SourceMapper<Rc<dyn SourceMapGetter>> =
SourceMapper::new(options.source_map_getter);
let mut sources = extension_set::into_sources(
options.extension_transpiler.as_deref(),
&extensions,
&mut source_mapper,
|source| {
mark_as_loaded_from_fs_during_snapshot(&mut files_loaded, &source.code)
},
)?;
let waker = op_state.waker.clone();
let op_state = Rc::new(RefCell::new(op_state));
let state_rc = Rc::new(JsRuntimeState {
source_mapper: RefCell::new(source_mapper),
shared_array_buffer_store: options.shared_array_buffer_store,
compiled_wasm_module_store: options.compiled_wasm_module_store,
wait_for_inspector_disconnect_callback: options
.wait_for_inspector_disconnect_callback,
op_state: op_state.clone(),
validate_import_attributes_cb: options.validate_import_attributes_cb,
custom_module_evaluation_cb: options.custom_module_evaluation_cb,
waker,
inspector: None.into(),
has_inspector: false.into(),
});
let op_decls =
extension_set::init_ops(crate::ops_builtin::BUILTIN_OPS, &mut extensions);
let op_driver = Rc::new(OpDriverImpl::default());
let op_metrics_factory_fn = options.op_metrics_factory_fn.take();
let get_error_class_fn = options.get_error_class_fn.unwrap_or(&|_| "Error");
let mut op_ctxs = extension_set::create_op_ctxs(
op_decls,
op_metrics_factory_fn,
op_driver.clone(),
op_state.clone(),
state_rc.clone(),
get_error_class_fn,
);
let (
global_template_middleware,
global_object_middlewares,
additional_references,
) = extension_set::get_middlewares_and_external_refs(&mut extensions);
let extension_count = extensions.len();
let op_count = op_ctxs.len();
let source_count = sources.len();
let addl_refs_count = additional_references.len();
let (maybe_startup_snapshot, mut sidecar_data) = options
.startup_snapshot
.take()
.map(snapshot::deconstruct)
.unzip();
let ops_in_snapshot = sidecar_data
.as_ref()
.map(|d| d.snapshot_data.op_count)
.unwrap_or_default();
let sources_in_snapshot = sidecar_data
.as_ref()
.map(|d| d.snapshot_data.source_count)
.unwrap_or_default();
let snapshot_sources: Vec<&[u8]> = sidecar_data
.as_mut()
.map(|s| std::mem::take(&mut s.snapshot_data.external_strings))
.unwrap_or_default();
(
isolate_allocations.externalized_sources,
isolate_allocations.original_sources,
) = bindings::externalize_sources(&mut sources, snapshot_sources);
isolate_allocations.external_refs =
Some(Box::new(bindings::create_external_references(
&op_ctxs,
&additional_references,
&isolate_allocations.externalized_sources,
ops_in_snapshot,
sources_in_snapshot,
)));
let external_refs: &v8::ExternalReferences =
isolate_allocations.external_refs.as_ref().unwrap().as_ref();
let external_refs_static = unsafe { std::mem::transmute(external_refs) };
let mut isolate = setup::create_isolate(
will_snapshot,
options.create_params.take(),
maybe_startup_snapshot,
external_refs_static,
);
let cpp_heap = setup::init_cppgc(&mut isolate);
for op_ctx in op_ctxs.iter_mut() {
op_ctx.isolate = isolate.as_mut() as *mut Isolate;
}
let isolate_ptr = setup::create_isolate_ptr();
isolate = unsafe {
isolate_ptr.write(isolate);
isolate_ptr.read()
};
op_state.borrow_mut().put(isolate_ptr);
let context_state = Rc::new(ContextState::new(
op_driver.clone(),
isolate_ptr,
options.get_error_class_fn.unwrap_or(&|_| "Error"),
op_ctxs,
));
let spawner = context_state
.task_spawner_factory
.clone()
.new_same_thread_spawner();
op_state.borrow_mut().put(spawner);
let spawner = context_state
.task_spawner_factory
.clone()
.new_cross_thread_spawner();
op_state.borrow_mut().put(spawner);
let mut snapshotted_data = None;
let main_context = {
let scope = &mut v8::HandleScope::new(&mut isolate);
let context = create_context(
scope,
&global_template_middleware,
&global_object_middlewares,
);
if let Some(raw_data) = sidecar_data {
snapshotted_data = Some(snapshot::load_snapshotted_data_from_snapshot(
scope, context, raw_data,
));
}
v8::Global::new(scope, context)
};
let mut context_scope: v8::HandleScope =
v8::HandleScope::with_context(&mut isolate, &main_context);
let scope = &mut context_scope;
let context = v8::Local::new(scope, &main_context);
if init_mode == InitMode::New {
bindings::initialize_deno_core_namespace(scope, context, init_mode);
bindings::initialize_primordials_and_infra(scope)?;
}
if init_mode.needs_ops_bindings() {
bindings::initialize_deno_core_ops_bindings(
scope,
context,
&context_state.op_ctxs,
);
}
context.set_slot(scope, context_state.clone());
let inspector = if options.inspector {
Some(JsRuntimeInspector::new(scope, context, options.is_main))
} else {
None
};
let loader = options
.module_loader
.unwrap_or_else(|| Rc::new(NoopModuleLoader));
let import_meta_resolve_cb = options
.import_meta_resolve_callback
.unwrap_or_else(|| Box::new(default_import_meta_resolve_cb));
let exception_state = context_state.exception_state.clone();
let module_map = Rc::new(ModuleMap::new(
loader,
exception_state.clone(),
import_meta_resolve_cb,
));
if let Some((snapshotted_data, mut data_store)) = snapshotted_data {
*exception_state.js_handled_promise_rejection_cb.borrow_mut() =
snapshotted_data
.js_handled_promise_rejection_cb
.map(|cb| data_store.get(scope, cb));
module_map.update_with_snapshotted_data(
scope,
&mut data_store,
snapshotted_data.module_map_data,
);
let mut mapper = state_rc.source_mapper.borrow_mut();
for (key, map) in snapshotted_data.ext_source_maps {
mapper.ext_source_maps.insert(key, map.into());
}
}
context.set_slot(scope, module_map.clone());
let main_realm = {
let main_realm =
JsRealmInner::new(context_state, main_context, module_map.clone());
state_rc.has_inspector.set(inspector.is_some());
*state_rc.inspector.borrow_mut() = inspector;
main_realm
};
let main_realm = JsRealm::new(main_realm);
scope.set_data(
STATE_DATA_OFFSET,
Rc::into_raw(state_rc.clone()) as *mut c_void,
);
drop(context_scope);
let mut js_runtime = JsRuntime {
inner: InnerIsolateState {
will_snapshot,
extension_count,
op_count,
source_count,
addl_refs_count,
main_realm: ManuallyDrop::new(main_realm),
state: ManuallyDropRc(ManuallyDrop::new(state_rc)),
v8_isolate: ManuallyDrop::new(isolate),
cpp_heap: ManuallyDrop::new(cpp_heap),
},
allocations: isolate_allocations,
files_loaded_from_fs_during_snapshot: vec![],
is_main_runtime: options.is_main,
};
{
let realm = JsRealm::clone(&js_runtime.inner.main_realm);
let context_global = realm.context();
let module_map = realm.0.module_map();
if init_mode == InitMode::New {
js_runtime
.execute_virtual_ops_module(context_global, module_map.clone());
}
if init_mode == InitMode::New {
js_runtime.execute_builtin_sources(
&realm,
&module_map,
&mut files_loaded,
)?;
}
js_runtime.store_js_callbacks(&realm, will_snapshot);
js_runtime.init_extension_js(&realm, &module_map, sources)?;
}
if will_snapshot {
js_runtime.files_loaded_from_fs_during_snapshot = files_loaded;
}
Ok(js_runtime)
}
#[cfg(test)]
#[inline]
pub(crate) fn module_map(&mut self) -> Rc<ModuleMap> {
self.inner.main_realm.0.module_map()
}
#[inline]
pub fn main_context(&self) -> v8::Global<v8::Context> {
self.inner.main_realm.0.context().clone()
}
#[cfg(test)]
pub(crate) fn main_realm(&self) -> JsRealm {
JsRealm::clone(&self.inner.main_realm)
}
#[inline]
pub fn v8_isolate(&mut self) -> &mut v8::OwnedIsolate {
&mut self.inner.v8_isolate
}
#[inline]
fn v8_isolate_ptr(&mut self) -> *mut v8::Isolate {
&mut **self.inner.v8_isolate as _
}
#[inline]
pub fn inspector(&mut self) -> Rc<RefCell<JsRuntimeInspector>> {
self.inner.state.inspector()
}
#[inline]
pub fn wait_for_inspector_disconnect(&mut self) {
if let Some(callback) = self
.inner
.state
.wait_for_inspector_disconnect_callback
.as_ref()
{
callback();
}
}
pub fn runtime_activity_stats_factory(&self) -> RuntimeActivityStatsFactory {
RuntimeActivityStatsFactory {
context_state: self.inner.main_realm.0.context_state.clone(),
op_state: self.inner.state.op_state.clone(),
}
}
pub(crate) fn files_loaded_from_fs_during_snapshot(&self) -> &[&'static str] {
&self.files_loaded_from_fs_during_snapshot
}
#[inline]
pub fn handle_scope(&mut self) -> v8::HandleScope {
let isolate = &mut self.inner.v8_isolate;
self.inner.main_realm.handle_scope(isolate)
}
#[inline(always)]
fn with_context_scope<'s, T>(
isolate: *mut v8::Isolate,
context: *mut v8::Context,
f: impl FnOnce(&mut v8::HandleScope<'s>) -> T,
) -> T {
let mut isolate_scope =
v8::HandleScope::new(unsafe { isolate.as_mut().unwrap_unchecked() });
let context = unsafe { std::mem::transmute(context) };
let mut scope = v8::ContextScope::new(&mut isolate_scope, context);
f(&mut scope)
}
fn execute_virtual_ops_module(
&mut self,
context_global: &v8::Global<v8::Context>,
module_map: Rc<ModuleMap>,
) {
let scope = &mut self.handle_scope();
let context_local = v8::Local::new(scope, context_global);
let context_state = JsRealm::state_from_scope(scope);
let global = context_local.global(scope);
let synthetic_module_exports = create_exports_for_ops_virtual_module(
&context_state.op_ctxs,
scope,
global,
);
let mod_id = module_map
.new_synthetic_module(
scope,
VIRTUAL_OPS_MODULE_NAME,
crate::ModuleType::JavaScript,
synthetic_module_exports,
)
.unwrap();
module_map.mod_evaluate_sync(scope, mod_id).unwrap();
}
fn execute_builtin_sources(
&mut self,
realm: &JsRealm,
module_map: &Rc<ModuleMap>,
files_loaded: &mut Vec<&'static str>,
) -> Result<(), Error> {
let scope = &mut realm.handle_scope(self.v8_isolate());
for source_file in &BUILTIN_SOURCES {
let name = source_file.specifier.v8_string(scope);
let source = source_file.source.v8_string(scope);
let origin = bindings::script_origin(scope, name);
let script = v8::Script::compile(scope, source, Some(&origin))
.with_context(|| {
format!("Failed to parse {}", source_file.specifier)
})?;
script.run(scope).with_context(|| {
format!("Failed to execute {}", source_file.specifier)
})?;
}
for file_source in &BUILTIN_ES_MODULES {
mark_as_loaded_from_fs_during_snapshot(files_loaded, &file_source.code);
module_map.lazy_load_es_module_with_code(
scope,
file_source.specifier,
file_source.load()?,
)?;
}
Ok(())
}
async fn init_extension_js_inner(
&mut self,
realm: &JsRealm,
module_map: &Rc<ModuleMap>,
loaded_sources: LoadedSources,
) -> Result<(), Error> {
for source in loaded_sources.lazy_esm {
module_map.add_lazy_loaded_esm_source(source.specifier, source.code);
}
let loader = module_map.loader.borrow().clone();
let mut modules = Vec::with_capacity(loaded_sources.esm.len());
let mut sources = Vec::with_capacity(loaded_sources.esm.len());
for esm in loaded_sources.esm {
modules.push(ModuleSpecifier::parse(&esm.specifier).unwrap());
sources.push((esm.specifier, esm.code));
}
let ext_loader = Rc::new(ExtModuleLoader::new(sources)?);
*module_map.loader.borrow_mut() = ext_loader.clone();
for module in modules {
realm
.load_side_es_module_from_code(self.v8_isolate(), &module, None)
.await?;
}
for source in loaded_sources.js {
realm.execute_script(self.v8_isolate(), source.specifier, source.code)?;
}
for specifier in loaded_sources.esm_entry_points {
let Some(mod_id) =
module_map.get_id(&specifier, RequestedModuleType::None)
else {
bail!("{} not present in the module map", specifier);
};
let isolate = self.v8_isolate();
let scope = &mut realm.handle_scope(isolate);
module_map.mod_evaluate_sync(scope, mod_id)?;
}
#[cfg(debug_assertions)]
{
let mut scope = realm.handle_scope(self.v8_isolate());
module_map.check_all_modules_evaluated(&mut scope)?;
}
let module_map = realm.0.module_map();
*module_map.loader.borrow_mut() = loader;
Rc::try_unwrap(ext_loader)
.map_err(drop)
.unwrap()
.finalize()?;
Ok(())
}
fn init_extension_js(
&mut self,
realm: &JsRealm,
module_map: &Rc<ModuleMap>,
loaded_sources: LoadedSources,
) -> Result<(), Error> {
futures::executor::block_on(self.init_extension_js_inner(
realm,
module_map,
loaded_sources,
))?;
Ok(())
}
pub fn eval<'s, T>(
scope: &mut v8::HandleScope<'s>,
code: &str,
) -> Option<v8::Local<'s, T>>
where
v8::Local<'s, T>: TryFrom<v8::Local<'s, v8::Value>, Error = v8::DataError>,
{
let scope = &mut v8::EscapableHandleScope::new(scope);
let source = v8::String::new(scope, code).unwrap();
let script = v8::Script::compile(scope, source, None).unwrap();
let v = script.run(scope)?;
scope.escape(v).try_into().ok()
}
fn store_js_callbacks(&mut self, realm: &JsRealm, will_snapshot: bool) {
let (event_loop_tick_cb, build_custom_error_cb, wasm_instantiate_fn) = {
let scope = &mut realm.handle_scope(self.v8_isolate());
let context = realm.context();
let context_local = v8::Local::new(scope, context);
let global = context_local.global(scope);
let deno_obj: v8::Local<v8::Object> =
bindings::get(scope, global, DENO, "Deno");
let core_obj: v8::Local<v8::Object> =
bindings::get(scope, deno_obj, CORE, "Deno.core");
let event_loop_tick_cb: v8::Local<v8::Function> = bindings::get(
scope,
core_obj,
EVENT_LOOP_TICK,
"Deno.core.eventLoopTick",
);
let build_custom_error_cb: v8::Local<v8::Function> = bindings::get(
scope,
core_obj,
BUILD_CUSTOM_ERROR,
"Deno.core.buildCustomError",
);
let mut wasm_instantiate_fn = None;
if !will_snapshot {
let web_assembly_object: v8::Local<v8::Object> =
bindings::get(scope, global, WEBASSEMBLY, "WebAssembly");
wasm_instantiate_fn = Some(bindings::get::<v8::Local<v8::Function>>(
scope,
web_assembly_object,
INSTANTIATE,
"WebAssembly.instantiate",
));
}
(
v8::Global::new(scope, event_loop_tick_cb),
v8::Global::new(scope, build_custom_error_cb),
wasm_instantiate_fn.map(|f| v8::Global::new(scope, f)),
)
};
let state_rc = realm.0.state();
state_rc
.js_event_loop_tick_cb
.borrow_mut()
.replace(Rc::new(event_loop_tick_cb));
state_rc
.exception_state
.js_build_custom_error_cb
.borrow_mut()
.replace(Rc::new(build_custom_error_cb));
if let Some(wasm_instantiate_fn) = wasm_instantiate_fn {
state_rc
.wasm_instantiate_fn
.borrow_mut()
.replace(Rc::new(wasm_instantiate_fn));
}
}
pub fn op_state(&mut self) -> Rc<RefCell<OpState>> {
self.inner.state.op_state.clone()
}
pub fn op_names(&self) -> Vec<&'static str> {
let state = &self.inner.main_realm.0.context_state;
state.op_ctxs.iter().map(|o| o.decl.name).collect()
}
pub fn execute_script(
&mut self,
name: &'static str,
source_code: impl IntoModuleCodeString,
) -> Result<v8::Global<v8::Value>, Error> {
let isolate = &mut self.inner.v8_isolate;
self.inner.main_realm.execute_script(
isolate,
FastString::from_static(name),
source_code.into_module_code(),
)
}
pub fn call(
&mut self,
function: &v8::Global<v8::Function>,
) -> impl Future<Output = Result<v8::Global<v8::Value>, Error>> {
self.call_with_args(function, &[])
}
pub fn scoped_call(
scope: &mut v8::HandleScope,
function: &v8::Global<v8::Function>,
) -> impl Future<Output = Result<v8::Global<v8::Value>, Error>> {
Self::scoped_call_with_args(scope, function, &[])
}
pub fn call_with_args(
&mut self,
function: &v8::Global<v8::Function>,
args: &[v8::Global<v8::Value>],
) -> impl Future<Output = Result<v8::Global<v8::Value>, Error>> {
let scope = &mut self.handle_scope();
Self::scoped_call_with_args(scope, function, args)
}
pub fn scoped_call_with_args(
scope: &mut v8::HandleScope,
function: &v8::Global<v8::Function>,
args: &[v8::Global<v8::Value>],
) -> impl Future<Output = Result<v8::Global<v8::Value>, Error>> {
let scope = &mut v8::TryCatch::new(scope);
let cb = function.open(scope);
let this = v8::undefined(scope).into();
let promise = if args.is_empty() {
cb.call(scope, this, &[])
} else {
let mut local_args: SmallVec<[v8::Local<v8::Value>; 8]> =
SmallVec::with_capacity(args.len());
for v in args {
local_args.push(v8::Local::new(scope, v));
}
cb.call(scope, this, &local_args)
};
if promise.is_none() {
if scope.is_execution_terminating() {
let undefined = v8::undefined(scope).into();
return RcPromiseFuture::new(exception_to_err_result(
scope, undefined, false, true,
));
}
let exception = scope.exception().unwrap();
return RcPromiseFuture::new(exception_to_err_result(
scope, exception, false, true,
));
}
let promise = promise.unwrap();
if !promise.is_promise() {
return RcPromiseFuture::new(Ok(v8::Global::new(scope, promise)));
}
let promise = v8::Local::<v8::Promise>::try_from(promise).unwrap();
Self::resolve_promise_inner(scope, promise)
}
#[deprecated = "Use call"]
pub async fn call_and_await(
&mut self,
function: &v8::Global<v8::Function>,
) -> Result<v8::Global<v8::Value>, Error> {
let call = self.call(function);
self
.with_event_loop_promise(call, PollEventLoopOptions::default())
.await
}
#[deprecated = "Use call_with_args"]
pub async fn call_with_args_and_await(
&mut self,
function: &v8::Global<v8::Function>,
args: &[v8::Global<v8::Value>],
) -> Result<v8::Global<v8::Value>, Error> {
let call = self.call_with_args(function, args);
self
.with_event_loop_promise(call, PollEventLoopOptions::default())
.await
}
pub fn get_module_namespace(
&mut self,
module_id: ModuleId,
) -> Result<v8::Global<v8::Object>, Error> {
let isolate = &mut self.inner.v8_isolate;
self
.inner
.main_realm
.get_module_namespace(isolate, module_id)
}
pub fn add_near_heap_limit_callback<C>(&mut self, cb: C)
where
C: FnMut(usize, usize) -> usize + 'static,
{
let boxed_cb = Box::new(RefCell::new(cb));
let data = boxed_cb.as_ptr() as *mut c_void;
let prev = self
.allocations
.near_heap_limit_callback_data
.replace((boxed_cb, near_heap_limit_callback::<C>));
if let Some((_, prev_cb)) = prev {
self
.v8_isolate()
.remove_near_heap_limit_callback(prev_cb, 0);
}
self
.v8_isolate()
.add_near_heap_limit_callback(near_heap_limit_callback::<C>, data);
}
pub fn remove_near_heap_limit_callback(&mut self, heap_limit: usize) {
if let Some((_, cb)) = self.allocations.near_heap_limit_callback_data.take()
{
self
.v8_isolate()
.remove_near_heap_limit_callback(cb, heap_limit);
}
}
fn pump_v8_message_loop(
&mut self,
scope: &mut v8::HandleScope,
) -> Result<(), Error> {
while v8::Platform::pump_message_loop(
&v8::V8::get_current_platform(),
scope,
false, ) {
}
let tc_scope = &mut v8::TryCatch::new(scope);
tc_scope.perform_microtask_checkpoint();
match tc_scope.exception() {
None => Ok(()),
Some(exception) => {
exception_to_err_result(tc_scope, exception, false, true)
}
}
}
pub fn maybe_init_inspector(&mut self) {
let inspector = &mut self.inner.state.inspector.borrow_mut();
if inspector.is_some() {
return;
}
let context = self.main_context();
let scope = &mut v8::HandleScope::with_context(
self.inner.v8_isolate.as_mut(),
context.clone(),
);
let context = v8::Local::new(scope, context);
self.inner.state.has_inspector.set(true);
**inspector = Some(JsRuntimeInspector::new(
scope,
context,
self.is_main_runtime,
));
}
pub fn resolve(
&mut self,
promise: v8::Global<v8::Value>,
) -> impl Future<Output = Result<v8::Global<v8::Value>, Error>> {
let scope = &mut self.handle_scope();
Self::scoped_resolve(scope, promise)
}
pub fn scoped_resolve(
scope: &mut v8::HandleScope,
promise: v8::Global<v8::Value>,
) -> impl Future<Output = Result<v8::Global<v8::Value>, Error>> {
let promise = v8::Local::new(scope, promise);
if !promise.is_promise() {
return RcPromiseFuture::new(Ok(v8::Global::new(scope, promise)));
}
let promise = v8::Local::<v8::Promise>::try_from(promise).unwrap();
Self::resolve_promise_inner(scope, promise)
}
#[deprecated = "Use resolve"]
pub async fn resolve_value(
&mut self,
global: v8::Global<v8::Value>,
) -> Result<v8::Global<v8::Value>, Error> {
let resolve = self.resolve(global);
self
.with_event_loop_promise(resolve, PollEventLoopOptions::default())
.await
}
fn resolve_promise_inner<'s>(
scope: &mut v8::HandleScope<'s>,
promise: v8::Local<'s, v8::Promise>,
) -> RcPromiseFuture {
let future = RcPromiseFuture::default();
let f = future.clone();
watch_promise(scope, promise, move |scope, _rv, res| {
let res = match res {
Ok(l) => Ok(v8::Global::new(scope, l)),
Err(e) => exception_to_err_result(scope, e, true, true),
};
f.0.resolved.set(Some(res));
if let Some(waker) = f.0.waker.take() {
waker.wake();
}
});
future
}
pub async fn run_event_loop(
&mut self,
poll_options: PollEventLoopOptions,
) -> Result<(), Error> {
poll_fn(|cx| self.poll_event_loop(cx, poll_options)).await
}
pub async fn with_event_loop_promise<'fut, T, E>(
&mut self,
mut fut: impl Future<Output = Result<T, E>> + Unpin + 'fut,
poll_options: PollEventLoopOptions,
) -> Result<T, AnyError>
where
AnyError: From<E>,
{
poll_fn(|cx| {
if let Poll::Ready(t) = fut.poll_unpin(cx) {
return Poll::Ready(t.map_err(|e| e.into()));
}
if let Poll::Ready(t) = self.poll_event_loop(cx, poll_options) {
t?;
if let Poll::Ready(t) = fut.poll_unpin(cx) {
return Poll::Ready(t.map_err(|e| e.into()));
}
return Poll::Ready(Err(anyhow!("Promise resolution is still pending but the event loop has already resolved.")));
}
Poll::Pending
}).await
}
pub async fn with_event_loop_future<'fut, T, E>(
&mut self,
mut fut: impl Future<Output = Result<T, E>> + Unpin + 'fut,
poll_options: PollEventLoopOptions,
) -> Result<T, AnyError>
where
AnyError: From<E>,
{
poll_fn(|cx| {
if let Poll::Ready(t) = fut.poll_unpin(cx) {
return Poll::Ready(t.map_err(|e| e.into()));
}
if let Poll::Ready(t) = self.poll_event_loop(cx, poll_options) {
_ = t;
}
Poll::Pending
})
.await
}
pub fn poll_event_loop(
&mut self,
cx: &mut Context,
poll_options: PollEventLoopOptions,
) -> Poll<Result<(), Error>> {
let isolate = self.v8_isolate_ptr();
Self::with_context_scope(
isolate,
self.inner.main_realm.context_ptr(),
move |scope| self.poll_event_loop_inner(cx, scope, poll_options),
)
}
fn poll_event_loop_inner(
&mut self,
cx: &mut Context,
scope: &mut v8::HandleScope,
poll_options: PollEventLoopOptions,
) -> Poll<Result<(), Error>> {
let has_inspector = self.inner.state.has_inspector.get();
self.inner.state.waker.register(cx.waker());
if has_inspector {
let _ = self.inspector().borrow().poll_sessions(Some(cx)).unwrap();
}
if poll_options.pump_v8_message_loop {
self.pump_v8_message_loop(scope)?;
}
let realm = &self.inner.main_realm;
let modules = &realm.0.module_map;
let context_state = &realm.0.context_state;
let exception_state = &context_state.exception_state;
modules.poll_progress(cx, scope)?;
let dispatched_ops = Self::do_js_event_loop_tick_realm(
cx,
scope,
context_state,
exception_state,
)?;
exception_state.check_exception_condition(scope)?;
let pending_state =
EventLoopPendingState::new(scope, context_state, modules);
if !pending_state.is_pending() {
if has_inspector {
let inspector = self.inspector();
let has_active_sessions = inspector.borrow().has_active_sessions();
let has_blocking_sessions = inspector.borrow().has_blocking_sessions();
if poll_options.wait_for_inspector && has_active_sessions {
if !has_blocking_sessions {
let context = self.main_context();
inspector.borrow_mut().context_destroyed(scope, context);
self.wait_for_inspector_disconnect();
}
return Poll::Pending;
}
}
return Poll::Ready(Ok(()));
}
#[allow(clippy::suspicious_else_formatting, clippy::if_same_then_else)]
{
if pending_state.has_pending_background_tasks
|| pending_state.has_tick_scheduled
|| pending_state.has_pending_promise_events
{
self.inner.state.waker.wake();
} else
if (pending_state.has_pending_module_evaluation
|| pending_state.has_pending_dyn_module_evaluation)
&& dispatched_ops
{
self.inner.state.waker.wake();
}
}
if pending_state.has_pending_module_evaluation {
if pending_state.has_pending_ops
|| pending_state.has_pending_dyn_imports
|| pending_state.has_pending_dyn_module_evaluation
|| pending_state.has_pending_background_tasks
|| pending_state.has_tick_scheduled
{
} else {
return Poll::Ready(Err(
find_and_report_stalled_level_await_in_any_realm(scope, &realm.0),
));
}
}
if pending_state.has_pending_dyn_module_evaluation {
if pending_state.has_pending_ops
|| pending_state.has_pending_dyn_imports
|| pending_state.has_pending_background_tasks
|| pending_state.has_tick_scheduled
{
} else if realm.modules_idle() {
return Poll::Ready(Err(
find_and_report_stalled_level_await_in_any_realm(scope, &realm.0),
));
} else {
realm.increment_modules_idle();
self.inner.state.waker.wake();
}
}
Poll::Pending
}
}
fn find_and_report_stalled_level_await_in_any_realm(
scope: &mut v8::HandleScope,
inner_realm: &JsRealmInner,
) -> Error {
let module_map = inner_realm.module_map();
let messages = module_map.find_stalled_top_level_await(scope);
if !messages.is_empty() {
let msg = v8::Local::new(scope, &messages[0]);
let js_error = JsError::from_v8_message(scope, msg);
return js_error.into();
}
unreachable!("Expected at least one stalled top-level await");
}
fn create_context<'a>(
scope: &mut v8::HandleScope<'a, ()>,
global_template_middlewares: &[GlobalTemplateMiddlewareFn],
global_object_middlewares: &[GlobalObjectMiddlewareFn],
) -> v8::Local<'a, v8::Context> {
let mut global_object_template = v8::ObjectTemplate::new(scope);
for middleware in global_template_middlewares {
global_object_template = middleware(scope, global_object_template);
}
let context = v8::Context::new_from_template(scope, global_object_template);
let scope = &mut v8::ContextScope::new(scope, context);
let global_wrapper = context.global(scope);
let real_global = global_wrapper
.get_prototype(scope)
.unwrap()
.to_object(scope)
.unwrap();
for middleware in global_object_middlewares {
middleware(scope, real_global);
}
context
}
impl JsRuntimeForSnapshot {
pub fn new(mut options: RuntimeOptions) -> JsRuntimeForSnapshot {
setup::init_v8(
options.v8_platform.take(),
true,
options.unsafe_expose_natives_and_gc(),
);
let runtime = match JsRuntime::new_inner(options, true) {
Ok(r) => r,
Err(err) => {
panic!("Failed to initialize JsRuntime for snapshotting: {:?}", err);
}
};
JsRuntimeForSnapshot(runtime)
}
pub fn snapshot(mut self) -> Box<[u8]> {
self.inner.prepare_for_cleanup();
let externals_count =
self.0.allocations.external_refs.as_ref().unwrap().len() as _;
let original_sources =
std::mem::take(&mut self.0.allocations.original_sources);
let external_strings = original_sources
.iter()
.map(|s| s.as_str().as_bytes())
.collect();
let realm = JsRealm::clone(&self.inner.main_realm);
{
let mut scope = realm.handle_scope(self.v8_isolate());
let local_context = v8::Local::new(&mut scope, realm.context());
scope.set_default_context(local_context);
}
let source_maps = std::mem::take(
&mut self.inner.state.source_mapper.borrow_mut().ext_source_maps,
);
let mut ext_source_maps = HashMap::with_capacity(source_maps.len());
for (k, v) in &source_maps {
ext_source_maps.insert(k.clone(), v.as_ref());
}
let sidecar_data = {
let mut data_store = SnapshotStoreDataStore::default();
let module_map_data = {
let module_map = realm.0.module_map();
module_map.serialize_for_snapshotting(&mut data_store)
};
let maybe_js_handled_promise_rejection_cb = {
let context_state = &realm.0.context_state;
let exception_state = &context_state.exception_state;
exception_state
.js_handled_promise_rejection_cb
.borrow()
.clone()
}
.map(|cb| data_store.register(cb));
let snapshotted_data = SnapshottedData {
module_map_data,
externals_count,
op_count: self.inner.op_count,
addl_refs_count: self.inner.addl_refs_count,
source_count: self.inner.source_count,
extension_count: self.inner.extension_count,
js_handled_promise_rejection_cb: maybe_js_handled_promise_rejection_cb,
ext_source_maps,
external_strings,
};
let mut scope = realm.handle_scope(self.v8_isolate());
snapshot::store_snapshotted_data_for_snapshot(
&mut scope,
realm.context().clone(),
snapshotted_data,
data_store,
)
};
drop(realm);
let v8_data = self
.0
.inner
.prepare_for_snapshot()
.create_blob(v8::FunctionCodeHandling::Keep)
.unwrap();
snapshot::serialize(v8_data, sidecar_data)
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub(crate) struct EventLoopPendingState {
has_pending_ops: bool,
has_pending_refed_ops: bool,
has_pending_dyn_imports: bool,
has_pending_dyn_module_evaluation: bool,
has_pending_module_evaluation: bool,
has_pending_background_tasks: bool,
has_tick_scheduled: bool,
has_pending_promise_events: bool,
}
impl EventLoopPendingState {
pub fn new(
scope: &mut v8::HandleScope<()>,
state: &ContextState,
modules: &ModuleMap,
) -> Self {
let num_unrefed_ops = state.unrefed_ops.borrow().len();
let num_pending_ops = state.pending_ops.len();
let has_pending_tasks = state.task_spawner_factory.has_pending_tasks();
let has_pending_timers = !state.timers.is_empty();
let has_pending_refed_timers = state.timers.has_pending_timers();
let has_pending_dyn_imports = modules.has_pending_dynamic_imports();
let has_pending_dyn_module_evaluation =
modules.has_pending_dyn_module_evaluation();
let has_pending_module_evaluation = modules.has_pending_module_evaluation();
let has_pending_promise_events = !state
.exception_state
.pending_promise_rejections
.borrow()
.is_empty()
|| !state
.exception_state
.pending_handled_promise_rejections
.borrow()
.is_empty();
let has_pending_refed_ops = has_pending_tasks
|| has_pending_refed_timers
|| num_pending_ops > num_unrefed_ops;
EventLoopPendingState {
has_pending_ops: has_pending_refed_ops
|| has_pending_timers
|| (num_pending_ops > 0),
has_pending_refed_ops,
has_pending_dyn_imports,
has_pending_dyn_module_evaluation,
has_pending_module_evaluation,
has_pending_background_tasks: scope.has_pending_background_tasks(),
has_tick_scheduled: state.has_next_tick_scheduled.get(),
has_pending_promise_events,
}
}
pub fn new_from_scope(scope: &mut v8::HandleScope) -> Self {
let module_map = JsRealm::module_map_from(scope);
let context_state = JsRealm::state_from_scope(scope);
Self::new(scope, &context_state, &module_map)
}
pub fn is_pending(&self) -> bool {
self.has_pending_refed_ops
|| self.has_pending_dyn_imports
|| self.has_pending_dyn_module_evaluation
|| self.has_pending_module_evaluation
|| self.has_pending_background_tasks
|| self.has_tick_scheduled
|| self.has_pending_promise_events
}
}
extern "C" fn near_heap_limit_callback<F>(
data: *mut c_void,
current_heap_limit: usize,
initial_heap_limit: usize,
) -> usize
where
F: FnMut(usize, usize) -> usize,
{
let callback = unsafe { &mut *(data as *mut F) };
callback(current_heap_limit, initial_heap_limit)
}
impl JsRuntimeState {
pub(crate) fn inspector(&self) -> Rc<RefCell<JsRuntimeInspector>> {
self.inspector.borrow().as_ref().unwrap().clone()
}
pub fn notify_new_dynamic_import(&self) {
self.waker.wake();
}
pub(crate) fn with_inspector<T>(
&self,
mut f: impl FnMut(&JsRuntimeInspector) -> T,
) -> Option<T> {
if !self.has_inspector.get() {
return None;
}
self
.inspector
.borrow()
.as_ref()
.map(|inspector| f(&inspector.borrow()))
}
}
impl JsRuntime {
#[cfg(test)]
pub(crate) fn instantiate_module(
&mut self,
id: ModuleId,
) -> Result<(), v8::Global<v8::Value>> {
let isolate = &mut self.inner.v8_isolate;
let realm = JsRealm::clone(&self.inner.main_realm);
let scope = &mut realm.handle_scope(isolate);
realm.instantiate_module(scope, id)
}
pub fn mod_evaluate(
&mut self,
id: ModuleId,
) -> impl Future<Output = Result<(), Error>> {
let isolate = &mut self.inner.v8_isolate;
let realm = &self.inner.main_realm;
let scope = &mut realm.handle_scope(isolate);
self.inner.main_realm.0.module_map.mod_evaluate(scope, id)
}
pub async fn load_main_es_module_from_code(
&mut self,
specifier: &ModuleSpecifier,
code: impl IntoModuleCodeString,
) -> Result<ModuleId, Error> {
let isolate = &mut self.inner.v8_isolate;
self
.inner
.main_realm
.load_main_es_module_from_code(
isolate,
specifier,
Some(code.into_module_code()),
)
.await
}
pub async fn load_main_es_module(
&mut self,
specifier: &ModuleSpecifier,
) -> Result<ModuleId, Error> {
let isolate = &mut self.inner.v8_isolate;
self
.inner
.main_realm
.load_main_es_module_from_code(isolate, specifier, None)
.await
}
pub async fn load_side_es_module_from_code(
&mut self,
specifier: &ModuleSpecifier,
code: impl IntoModuleCodeString,
) -> Result<ModuleId, Error> {
let isolate = &mut self.inner.v8_isolate;
self
.inner
.main_realm
.load_side_es_module_from_code(
isolate,
specifier,
Some(code.into_module_code()),
)
.await
}
pub async fn load_side_es_module(
&mut self,
specifier: &ModuleSpecifier,
) -> Result<ModuleId, Error> {
let isolate = &mut self.inner.v8_isolate;
self
.inner
.main_realm
.load_side_es_module_from_code(isolate, specifier, None)
.await
}
pub fn lazy_load_es_module_with_code(
&mut self,
specifier: impl IntoModuleName,
code: impl IntoModuleCodeString,
) -> Result<v8::Global<v8::Value>, Error> {
let isolate = &mut self.inner.v8_isolate;
self.inner.main_realm.lazy_load_es_module_with_code(
isolate,
specifier.into_module_name(),
code.into_module_code(),
)
}
fn do_js_event_loop_tick_realm(
cx: &mut Context,
scope: &mut v8::HandleScope,
context_state: &ContextState,
exception_state: &ExceptionState,
) -> Result<bool, Error> {
let mut dispatched_ops = false;
let tasks = context_state.task_spawner_factory.poll_inner(cx);
if let Poll::Ready(tasks) = tasks {
dispatched_ops = true;
for task in tasks {
task(scope);
}
}
const MAX_VEC_SIZE_FOR_OPS: usize = 1024;
let mut args: SmallVec<[v8::Local<v8::Value>; 32]> =
SmallVec::with_capacity(32);
loop {
if args.len() >= MAX_VEC_SIZE_FOR_OPS {
cx.waker().wake_by_ref();
break;
}
let Poll::Ready((promise_id, op_id, res)) =
context_state.pending_ops.poll_ready(cx)
else {
break;
};
let res = res.unwrap(scope, context_state.get_error_class_fn);
{
let op_ctx = &context_state.op_ctxs[op_id as usize];
if op_ctx.metrics_enabled() {
if res.is_ok() {
dispatch_metrics_async(op_ctx, OpMetricsEvent::CompletedAsync);
} else {
dispatch_metrics_async(op_ctx, OpMetricsEvent::ErrorAsync);
}
}
}
context_state.unrefed_ops.borrow_mut().remove(&promise_id);
context_state
.activity_traces
.complete(RuntimeActivityType::AsyncOp, promise_id as _);
dispatched_ops |= true;
args.push(v8::Integer::new(scope, promise_id).into());
args.push(res.unwrap_or_else(std::convert::identity));
}
let undefined: v8::Local<v8::Value> = v8::undefined(scope).into();
let has_tick_scheduled = context_state.has_next_tick_scheduled.get();
dispatched_ops |= has_tick_scheduled;
while let Some((promise, result)) = exception_state
.pending_handled_promise_rejections
.borrow_mut()
.pop_front()
{
if let Some(handler) = exception_state
.js_handled_promise_rejection_cb
.borrow()
.as_ref()
{
let function = handler.open(scope);
let args = [
v8::Local::new(scope, promise).into(),
v8::Local::new(scope, result),
];
function.call(scope, undefined, &args);
}
}
let rejections = if !exception_state
.pending_promise_rejections
.borrow_mut()
.is_empty()
{
let mut pending_rejections =
exception_state.pending_promise_rejections.borrow_mut();
let mut rejections = VecDeque::default();
std::mem::swap(&mut *pending_rejections, &mut rejections);
drop(pending_rejections);
let arr = v8::Array::new(scope, (rejections.len() * 2) as i32);
let mut index = 0;
for rejection in rejections.into_iter() {
let value = v8::Local::new(scope, rejection.0);
arr.set_index(scope, index, value.into());
index += 1;
let value = v8::Local::new(scope, rejection.1);
arr.set_index(scope, index, value);
index += 1;
}
arr.into()
} else {
undefined
};
args.push(rejections);
let timers =
if let Poll::Ready(timers) = context_state.timers.poll_timers(cx) {
let traces_enabled = context_state.activity_traces.is_enabled();
let arr = v8::Array::new(scope, (timers.len() * 3) as _);
#[allow(clippy::needless_range_loop)]
for i in 0..timers.len() {
if traces_enabled {
context_state
.activity_traces
.complete(RuntimeActivityType::Timer, timers[i].0 as _);
}
let value = v8::Integer::new(scope, timers[i].1 .1 as _);
arr.set_index(scope, (i * 3) as _, value.into());
let value = v8::Number::new(scope, timers[i].0 as _);
arr.set_index(scope, (i * 3 + 1) as _, value.into());
let value = v8::Local::new(scope, timers[i].1 .0.clone());
arr.set_index(scope, (i * 3 + 2) as _, value.into());
}
arr.into()
} else {
undefined
};
args.push(timers);
let has_tick_scheduled = v8::Boolean::new(scope, has_tick_scheduled);
args.push(has_tick_scheduled.into());
let tc_scope = &mut v8::TryCatch::new(scope);
let js_event_loop_tick_cb = context_state.js_event_loop_tick_cb.borrow();
let js_event_loop_tick_cb =
js_event_loop_tick_cb.as_ref().unwrap().open(tc_scope);
js_event_loop_tick_cb.call(tc_scope, undefined, args.as_slice());
if let Some(exception) = tc_scope.exception() {
return exception_to_err_result(tc_scope, exception, false, true);
}
if tc_scope.has_terminated() || tc_scope.is_execution_terminating() {
return Ok(false);
}
Ok(dispatched_ops)
}
}
fn mark_as_loaded_from_fs_during_snapshot(
files_loaded: &mut Vec<&'static str>,
source: &ExtensionFileSourceCode,
) {
#[allow(deprecated)]
if let ExtensionFileSourceCode::LoadedFromFsDuringSnapshot(path) = source {
files_loaded.push(path);
}
}