use super::bindings;
use super::exception_state::ExceptionState;
#[cfg(test)]
use super::op_driver::OpDriver;
use crate::error::exception_to_err_result;
use crate::module_specifier::ModuleSpecifier;
use crate::modules::IntoModuleCodeString;
use crate::modules::IntoModuleName;
use crate::modules::ModuleCodeString;
use crate::modules::ModuleId;
use crate::modules::ModuleMap;
use crate::modules::ModuleName;
use crate::ops::OpCtx;
use crate::stats::RuntimeActivityTraces;
use crate::tasks::V8TaskSpawnerFactory;
use crate::web_timeout::WebTimers;
use crate::GetErrorClassFn;
use anyhow::Error;
use futures::stream::StreamExt;
use std::cell::Cell;
use std::cell::RefCell;
use std::collections::HashSet;
use std::hash::BuildHasherDefault;
use std::hash::Hasher;
use std::rc::Rc;
use std::sync::Arc;
use v8::Handle;
#[derive(Default)]
pub(crate) struct IdentityHasher(u64);
impl Hasher for IdentityHasher {
fn write_i32(&mut self, i: i32) {
self.0 = i as u64;
}
fn finish(&self) -> u64 {
self.0
}
fn write(&mut self, _bytes: &[u8]) {
unreachable!()
}
}
pub(crate) type OpDriverImpl = super::op_driver::FuturesUnorderedDriver;
pub(crate) struct ContextState {
pub(crate) task_spawner_factory: Arc<V8TaskSpawnerFactory>,
pub(crate) timers: WebTimers<(v8::Global<v8::Function>, u32)>,
pub(crate) js_event_loop_tick_cb:
RefCell<Option<Rc<v8::Global<v8::Function>>>>,
pub(crate) js_wasm_streaming_cb:
RefCell<Option<Rc<v8::Global<v8::Function>>>>,
pub(crate) wasm_instantiate_fn: RefCell<Option<Rc<v8::Global<v8::Function>>>>,
pub(crate) unrefed_ops:
RefCell<HashSet<i32, BuildHasherDefault<IdentityHasher>>>,
pub(crate) activity_traces: RuntimeActivityTraces,
pub(crate) pending_ops: Rc<OpDriverImpl>,
pub(crate) op_ctxs: Box<[OpCtx]>,
pub(crate) isolate: Option<*mut v8::OwnedIsolate>,
pub(crate) exception_state: Rc<ExceptionState>,
pub(crate) has_next_tick_scheduled: Cell<bool>,
pub(crate) get_error_class_fn: GetErrorClassFn,
}
impl ContextState {
pub(crate) fn new(
op_driver: Rc<OpDriverImpl>,
isolate_ptr: *mut v8::OwnedIsolate,
get_error_class_fn: GetErrorClassFn,
op_ctxs: Box<[OpCtx]>,
) -> Self {
Self {
isolate: Some(isolate_ptr),
get_error_class_fn,
exception_state: Default::default(),
has_next_tick_scheduled: Default::default(),
js_event_loop_tick_cb: Default::default(),
js_wasm_streaming_cb: Default::default(),
wasm_instantiate_fn: Default::default(),
activity_traces: Default::default(),
op_ctxs,
pending_ops: op_driver,
task_spawner_factory: Default::default(),
timers: Default::default(),
unrefed_ops: Default::default(),
}
}
}
#[derive(Clone)]
#[repr(transparent)]
pub(crate) struct JsRealm(pub(crate) JsRealmInner);
#[derive(Clone)]
pub(crate) struct JsRealmInner {
pub(crate) context_state: Rc<ContextState>,
context: Rc<v8::Global<v8::Context>>,
pub(crate) module_map: Rc<ModuleMap>,
}
impl JsRealmInner {
pub(crate) fn new(
context_state: Rc<ContextState>,
context: v8::Global<v8::Context>,
module_map: Rc<ModuleMap>,
) -> Self {
Self {
context_state,
context: context.into(),
module_map,
}
}
#[inline(always)]
pub fn context(&self) -> &v8::Global<v8::Context> {
&self.context
}
#[inline(always)]
pub(crate) fn state(&self) -> Rc<ContextState> {
self.context_state.clone()
}
#[inline(always)]
pub(crate) fn module_map(&self) -> Rc<ModuleMap> {
self.module_map.clone()
}
#[inline(always)]
pub fn handle_scope<'s>(
&self,
isolate: &'s mut v8::Isolate,
) -> v8::HandleScope<'s> {
v8::HandleScope::with_context(isolate, &*self.context)
}
pub fn destroy(self) {
let state = self.state();
let raw_ptr = self.state().isolate.unwrap();
let isolate = unsafe { raw_ptr.as_mut().unwrap() };
state.exception_state.prepare_to_destroy();
std::mem::take(&mut *state.js_event_loop_tick_cb.borrow_mut());
std::mem::take(&mut *state.js_wasm_streaming_cb.borrow_mut());
self.context().open(isolate).clear_all_slots(isolate);
}
}
impl JsRealm {
pub(crate) fn new(inner: JsRealmInner) -> Self {
Self(inner)
}
#[inline(always)]
pub(crate) fn state_from_scope(
scope: &mut v8::HandleScope,
) -> Rc<ContextState> {
let context = scope.get_current_context();
context.get_slot::<Rc<ContextState>>(scope).unwrap().clone()
}
#[inline(always)]
pub(crate) fn module_map_from(scope: &mut v8::HandleScope) -> Rc<ModuleMap> {
let context = scope.get_current_context();
context.get_slot::<Rc<ModuleMap>>(scope).unwrap().clone()
}
#[inline(always)]
pub(crate) fn exception_state_from_scope(
scope: &mut v8::HandleScope,
) -> Rc<ExceptionState> {
let context = scope.get_current_context();
context
.get_slot::<Rc<ContextState>>(scope)
.unwrap()
.exception_state
.clone()
}
#[cfg(test)]
#[inline(always)]
pub fn num_pending_ops(&self) -> usize {
self.0.context_state.pending_ops.len()
}
#[cfg(test)]
#[inline(always)]
pub fn num_unrefed_ops(&self) -> usize {
self.0.context_state.unrefed_ops.borrow().len()
}
#[inline(always)]
pub fn handle_scope<'s>(
&self,
isolate: &'s mut v8::Isolate,
) -> v8::HandleScope<'s> {
self.0.handle_scope(isolate)
}
#[inline(always)]
pub fn context(&self) -> &v8::Global<v8::Context> {
self.0.context()
}
pub(crate) fn context_ptr(&self) -> *mut v8::Context {
unsafe { self.0.context.get_unchecked() as *const _ as _ }
}
pub fn execute_script(
&self,
isolate: &mut v8::Isolate,
name: impl IntoModuleName,
source_code: impl IntoModuleCodeString,
) -> Result<v8::Global<v8::Value>, Error> {
let scope = &mut self.0.handle_scope(isolate);
let source = source_code.into_module_code().v8_string(scope);
let name = name.into_module_name().v8_string(scope);
let origin = bindings::script_origin(scope, name);
let tc_scope = &mut v8::TryCatch::new(scope);
let script = match v8::Script::compile(tc_scope, source, Some(&origin)) {
Some(script) => script,
None => {
let exception = tc_scope.exception().unwrap();
return exception_to_err_result(tc_scope, exception, false, false);
}
};
match script.run(tc_scope) {
Some(value) => {
let value_handle = v8::Global::new(tc_scope, value);
Ok(value_handle)
}
None => {
assert!(tc_scope.has_caught());
let exception = tc_scope.exception().unwrap();
exception_to_err_result(tc_scope, exception, false, false)
}
}
}
pub fn get_module_namespace(
&self,
isolate: &mut v8::Isolate,
module_id: ModuleId,
) -> Result<v8::Global<v8::Object>, Error> {
self
.0
.module_map()
.get_module_namespace(&mut self.handle_scope(isolate), module_id)
}
pub(crate) fn instantiate_module(
&self,
scope: &mut v8::HandleScope,
id: ModuleId,
) -> Result<(), v8::Global<v8::Value>> {
self.0.module_map().instantiate_module(scope, id)
}
pub(crate) fn modules_idle(&self) -> bool {
self.0.module_map.dyn_module_evaluate_idle_counter.get() > 1
}
pub(crate) fn increment_modules_idle(&self) {
let count = &self.0.module_map.dyn_module_evaluate_idle_counter;
count.set(count.get() + 1)
}
pub(crate) async fn load_main_es_module_from_code(
&self,
isolate: &mut v8::Isolate,
specifier: &ModuleSpecifier,
code: Option<ModuleCodeString>,
) -> Result<ModuleId, Error> {
let module_map_rc = self.0.module_map();
if let Some(code) = code {
let scope = &mut self.handle_scope(isolate);
module_map_rc
.new_es_module(scope, true, specifier.to_owned(), code, false)
.map_err(|e| e.into_any_error(scope, false, false))?;
}
let mut load =
ModuleMap::load_main(module_map_rc.clone(), &specifier).await?;
while let Some(load_result) = load.next().await {
let (request, info) = load_result?;
let scope = &mut self.handle_scope(isolate);
load
.register_and_recurse(scope, &request, info)
.map_err(|e| e.into_any_error(scope, false, false))?;
}
let root_id = load.root_module_id.expect("Root module should be loaded");
let scope = &mut self.handle_scope(isolate);
self.instantiate_module(scope, root_id).map_err(|e| {
let exception = v8::Local::new(scope, e);
exception_to_err_result::<()>(scope, exception, false, false).unwrap_err()
})?;
Ok(root_id)
}
pub(crate) async fn load_side_es_module_from_code(
&self,
isolate: &mut v8::Isolate,
specifier: &ModuleSpecifier,
code: Option<ModuleCodeString>,
) -> Result<ModuleId, Error> {
let module_map_rc = self.0.module_map();
if let Some(code) = code {
let specifier = specifier.to_owned();
let scope = &mut self.handle_scope(isolate);
module_map_rc
.new_es_module(scope, false, specifier, code, false)
.map_err(|e| e.into_any_error(scope, false, false))?;
}
let mut load =
ModuleMap::load_side(module_map_rc.clone(), &specifier).await?;
while let Some(load_result) = load.next().await {
let (request, info) = load_result?;
let scope = &mut self.handle_scope(isolate);
load
.register_and_recurse(scope, &request, info)
.map_err(|e| e.into_any_error(scope, false, false))?;
}
let root_id = load.root_module_id.expect("Root module should be loaded");
let scope = &mut self.handle_scope(isolate);
self.instantiate_module(scope, root_id).map_err(|e| {
let exception = v8::Local::new(scope, e);
exception_to_err_result::<()>(scope, exception, false, false).unwrap_err()
})?;
Ok(root_id)
}
pub(crate) fn lazy_load_es_module_with_code(
&self,
isolate: &mut v8::Isolate,
module_specifier: ModuleName,
code: ModuleCodeString,
) -> Result<v8::Global<v8::Value>, Error> {
let module_map_rc = self.0.module_map();
let scope = &mut self.handle_scope(isolate);
module_map_rc.lazy_load_es_module_with_code(
scope,
module_specifier.as_str(),
code,
)
}
}