pub mod binding;
pub mod command;
pub mod converter;
pub mod err;
pub mod exception;
pub mod hook;
pub mod loader;
pub mod module;
pub mod pending;
pub mod resource;
pub mod transpiler;
#[cfg(test)]
mod command_tests;
#[cfg(test)]
mod converter_tests;
#[cfg(test)]
mod module_tests;
use crate::buf::BufferManagerArc;
use crate::cfg::path_cfg::PATH_CONFIG;
use crate::chan;
use crate::chan::JsMessage;
use crate::chan::MasterMessage;
use crate::cli::CliOptions;
use crate::cmdltext::CmdlineTextArc;
use crate::hl::ColorSchemeManagerArc;
use crate::prelude::*;
use crate::structural_id_impl;
use crate::syntax::SyntaxManagerArc;
use crate::ui::tree::TreeArc;
pub use boost::*;
use command::CommandManagerArc;
use err::JsError;
use err::report_js_error;
use exception::ExceptionState;
use exception::PromiseRejectionEntry;
use hook::module_resolve_cb;
use module::ImportKind;
use module::ImportMap;
use module::ModuleMap;
use module::ModuleStatus;
use module::fetch_module;
use module::fetch_module_tree;
use module::resolve_import;
use pending::TaskCallback;
use pending::TimerCallback;
use resource::ResourceTableArc;
pub use snapshot::*;
use std::rc::Rc;
use std::sync::Once;
use tokio::sync::mpsc::UnboundedReceiver;
use tokio::sync::mpsc::UnboundedSender;
use tokio::time::Instant;
pub fn v8_version() -> &'static str {
v8::VERSION_STRING
}
pub trait JsFuture {
fn run(&mut self, scope: &mut v8::PinScope);
}
structural_id_impl!(i32, TimerId, 1);
structural_id_impl!(usize, TaskId, 1);
pub struct SnapshotData {
pub value: &'static [u8],
}
impl SnapshotData {
pub fn new(value: &'static [u8]) -> Self {
SnapshotData { value }
}
}
pub fn init_v8_platform(snapshot: bool, user_v8_flags: Option<&[String]>) {
static V8_INIT: Once = Once::new();
V8_INIT.call_once(move || {
#[cfg(feature = "icudata")]
{
v8::icu::set_common_data_77(deno_core_icudata::ICU_DATA).unwrap();
}
let mut flags = String::from(concat!(
" --no-validate-asm",
" --turbo-fast-api-calls",
" --harmony-temporal",
" --js-float16array",
" --js-explicit-resource-management",
));
if snapshot {
flags.push_str(" --predictable --random-seed=42");
}
if let Some(user_flags) = user_v8_flags
&& !user_flags.is_empty()
{
let user_flags = user_flags.join(" ");
let user_flags = format!(" {user_flags}");
flags.push_str(user_flags.as_str());
}
v8::V8::set_flags_from_string(&flags);
let platform = v8::new_default_platform(0, false).make_shared();
v8::V8::initialize_platform(platform);
v8::V8::initialize();
});
}
fn init_v8_isolate(isolate: &mut v8::OwnedIsolate) {
isolate.set_microtasks_policy(v8::MicrotasksPolicy::Explicit);
isolate.set_capture_stack_trace_for_uncaught_exceptions(true, 10);
isolate.set_promise_reject_callback(hook::promise_reject_cb);
isolate.set_host_import_module_dynamically_callback(
hook::host_import_module_dynamically_cb,
);
isolate.set_host_initialize_import_meta_object_callback(
hook::host_initialize_import_meta_object_cb,
);
}
fn init_builtin_modules(scope: &mut v8::PinScope) {
static BUILTIN_MODULES: [( &str, &str); 2] = [
(
"00__web.js",
include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/src/js/runtime/00__web.min.js"
)),
),
(
"01__rsvim.js",
include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/src/js/runtime/01__rsvim.min.js"
)),
),
];
for module in BUILTIN_MODULES.iter() {
let filename = module.0;
let source = module.1;
v8::tc_scope!(let tc_scope, scope);
let module = fetch_module(tc_scope, filename, Some(source)).unwrap();
let _ = module
.instantiate_module(tc_scope, module_resolve_cb)
.unwrap();
let _ = module.evaluate(tc_scope);
trace!(
"|init_builtin_modules| ModuleMap evaluated {:?}, status {:?}",
filename,
module.get_status()
);
if module.get_status() == v8::ModuleStatus::Errored {
let exception = module.get_exception();
let exception = JsError::from_v8_exception(tc_scope, exception, None);
error!(
"Failed to evaluate builtin modules: {filename}, error: {exception:?}"
);
std::process::exit(1);
}
}
}
pub mod snapshot {
use super::*;
pub struct JsRuntimeStateForSnapshot {
pub context: Option<v8::Global<v8::Context>>,
}
rc_refcell_ptr!(JsRuntimeStateForSnapshot);
pub struct JsRuntimeForSnapshot {
pub isolate: Option<v8::OwnedIsolate>,
pub state: JsRuntimeStateForSnapshotRc,
}
impl Drop for JsRuntimeForSnapshot {
fn drop(&mut self) {
debug_assert_eq!(Rc::strong_count(&self.state), 1);
}
}
impl JsRuntimeForSnapshot {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
init_v8_platform(true, None);
let mut isolate =
v8::Isolate::snapshot_creator(None, Some(v8::CreateParams::default()));
let context: v8::Global<v8::Context> = {
v8::scope!(scope, &mut *isolate);
let context = v8::Context::new(scope, Default::default());
v8::Global::new(scope, context)
};
let state = {
v8::scope_with_context!(scope, &mut *isolate, context.clone());
init_builtin_modules(scope);
JsRuntimeStateForSnapshot::to_rc(JsRuntimeStateForSnapshot {
context: Some(context),
})
};
isolate.set_slot(state.clone());
JsRuntimeForSnapshot {
isolate: Some(isolate),
state,
}
}
pub fn create_snapshot(mut self) -> v8::StartupData {
{
let context = self.context();
v8::scope_with_context!(
scope,
self.isolate.as_mut().unwrap(),
context.clone()
);
let context = v8::Local::new(scope, context);
scope.set_default_context(context);
}
{
let state_rc = self.get_state();
state_rc.borrow_mut().context.take();
}
let snapshot_creator = self.isolate.take().unwrap();
snapshot_creator
.create_blob(v8::FunctionCodeHandling::Keep)
.unwrap()
}
}
impl JsRuntimeForSnapshot {
pub fn context(&self) -> v8::Global<v8::Context> {
self.get_state().borrow().context.as_ref().unwrap().clone()
}
pub fn state(isolate: &v8::Isolate) -> JsRuntimeStateForSnapshotRc {
isolate
.get_slot::<JsRuntimeStateForSnapshotRc>()
.unwrap()
.clone()
}
pub fn get_state(&self) -> JsRuntimeStateForSnapshotRc {
Self::state(self.isolate.as_ref().unwrap())
}
}
}
pub mod boost {
use super::*;
#[derive(Debug, Default, Clone)]
pub struct JsRuntimeOptions {
pub root: Option<String>,
pub import_map: Option<ImportMap>,
pub test_mode: bool,
pub v8_flags: Vec<String>,
}
pub struct JsRuntimeState {
pub context: v8::Global<v8::Context>,
pub module_map: ModuleMap,
pub pending_timers: FoldMap<TimerId, TimerCallback>,
pub pending_import_loaders: FoldMap<TaskId, TaskCallback>,
pub pending_tasks: FoldMap<TaskId, TaskCallback>,
pub pending_futures: Vec<Box<dyn JsFuture>>,
pub startup_moment: Instant,
pub time_origin: u128,
pub exceptions: ExceptionState,
pub options: JsRuntimeOptions,
pub master_tx: UnboundedSender<MasterMessage>,
pub jsrt_rx: UnboundedReceiver<JsMessage>,
pub cli_opts: CliOptions,
pub tree: TreeArc,
pub buffer_manager: BufferManagerArc,
pub cmdline_text: CmdlineTextArc,
pub syntax_manager: SyntaxManagerArc,
pub colorscheme_manager: ColorSchemeManagerArc,
pub command_manager: CommandManagerArc,
pub resource_table: ResourceTableArc,
}
rc_refcell_ptr!(JsRuntimeState);
pub struct JsRuntime {
pub isolate: v8::OwnedIsolate,
#[allow(unused)]
pub state: JsRuntimeStateRc,
}
impl std::fmt::Debug for JsRuntime {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "JsRuntime")
}
}
impl JsRuntime {
pub fn new(
options: JsRuntimeOptions,
snapshot: SnapshotData,
startup_moment: Instant,
time_origin: u128,
master_tx: UnboundedSender<MasterMessage>,
jsrt_rx: UnboundedReceiver<JsMessage>,
cli_opts: CliOptions,
tree: TreeArc,
buffer_manager: BufferManagerArc,
cmdline_text: CmdlineTextArc,
syntax_manager: SyntaxManagerArc,
colorscheme_manager: ColorSchemeManagerArc,
command_manager: CommandManagerArc,
resource_table: ResourceTableArc,
) -> Self {
init_v8_platform(false, Some(&options.v8_flags));
let mut isolate = {
let create_params = v8::CreateParams::default();
let create_params = create_params.snapshot_blob(snapshot.value.into());
v8::Isolate::new(create_params)
};
init_v8_isolate(&mut isolate);
let context: v8::Global<v8::Context> = {
v8::scope!(scope, &mut *isolate);
let context = binding::create_new_context(scope);
v8::Global::new(scope, context)
};
let state = JsRuntimeState::to_rc(JsRuntimeState {
context,
module_map: ModuleMap::new(),
pending_timers: FoldMap::new(),
pending_import_loaders: FoldMap::new(),
pending_tasks: FoldMap::new(),
pending_futures: vec![],
startup_moment,
time_origin,
exceptions: ExceptionState::new(),
options,
master_tx,
jsrt_rx,
cli_opts,
tree,
buffer_manager,
cmdline_text,
syntax_manager,
colorscheme_manager,
command_manager,
resource_table,
});
isolate.set_slot(state.clone());
JsRuntime {
isolate,
state,
}
}
pub fn new_without_snapshot(
options: JsRuntimeOptions,
startup_moment: Instant,
time_origin: u128,
master_tx: UnboundedSender<MasterMessage>,
jsrt_rx: UnboundedReceiver<JsMessage>,
cli_opt: CliOptions,
tree: TreeArc,
buffer_manager: BufferManagerArc,
cmdline_text: CmdlineTextArc,
syntax_manager: SyntaxManagerArc,
colorscheme_manager: ColorSchemeManagerArc,
command_manager: CommandManagerArc,
resource_table: ResourceTableArc,
) -> Self {
init_v8_platform(false, Some(&options.v8_flags));
let mut isolate = v8::Isolate::new(v8::CreateParams::default());
init_v8_isolate(&mut isolate);
let context: v8::Global<v8::Context> = {
v8::scope!(scope, &mut isolate);
let context = binding::create_new_context(scope);
v8::Global::new(scope, context)
};
let state = JsRuntimeState::to_rc(JsRuntimeState {
context,
module_map: ModuleMap::new(),
pending_timers: FoldMap::new(),
pending_import_loaders: FoldMap::new(),
pending_tasks: FoldMap::new(),
pending_futures: vec![],
startup_moment,
time_origin,
exceptions: ExceptionState::new(),
options,
master_tx,
jsrt_rx,
cli_opts: cli_opt,
tree,
buffer_manager,
cmdline_text,
syntax_manager,
colorscheme_manager,
command_manager,
resource_table,
});
isolate.set_slot(state.clone());
let mut runtime = JsRuntime {
isolate,
state,
};
runtime.with_scope(init_builtin_modules);
runtime
}
fn with_scope<F>(&mut self, func: F)
where
F: FnOnce(&mut v8::PinScope),
{
let context = self.context();
v8::scope_with_context!(scope, &mut self.isolate, context);
func(scope);
}
pub fn execute_module(&mut self, filename: &str, source: Option<&str>) {
self.with_scope(|scope| execute_module(scope, filename, source));
}
pub fn tick_event_loop(&mut self) {
self.with_scope(run_next_tick_callbacks);
self.fast_forward_imports();
self.run_pending_futures();
trace!(
"|JsRuntime::tick_event_loop| has_promise_rejections:{:?}, has_pending_background_tasks:{:?}, has_pending_imports:{:?}({:?}), has_pending_import_loaders:{:?}({:?}), has_unresolved_imports:{:?}({:?})",
self.has_promise_rejections(),
self.isolate.has_pending_background_tasks(),
self.has_pending_imports(),
self.pending_imports_count(),
self.has_pending_import_loaders(),
self.pending_import_loaders_count(),
self.has_unresolved_imports(),
self.unresolved_imports_count(),
);
if self.has_promise_rejections()
|| self.isolate.has_pending_background_tasks()
|| (self.unresolved_imports_count()
> self.pending_import_loaders_count())
|| (!self.has_unresolved_imports() && self.has_pending_imports())
{
chan::send_to_master(
self.get_state().borrow().master_tx.clone(),
MasterMessage::TickAgainReq,
);
}
}
fn run_pending_futures(&mut self) {
let context = self.context();
v8::scope_with_context!(scope, &mut self.isolate, context);
let state_rc = Self::state(scope);
let mut messages: Vec<JsMessage> = vec![];
{
let mut state = state_rc.borrow_mut();
while let Ok(msg) = state.jsrt_rx.try_recv() {
messages.push(msg);
}
}
for msg in messages {
match msg {
JsMessage::TimeoutResp(resp) => {
trace!("Recv TimeResp:{:?}", resp.timer_id);
let maybe_timer_cb =
state_rc.borrow_mut().pending_timers.remove(&resp.timer_id);
if let Some(mut timer_cb) = maybe_timer_cb {
timer_cb();
if resp.repeated {
let mut state = state_rc.borrow_mut();
pending::create_timer(
&mut state,
resp.timer_id,
resp.delay,
resp.repeated,
timer_cb,
);
}
}
}
JsMessage::ExCommandReq(req) => {
trace!("Recv ExCommandReq:{:?}", req.payload);
let mut state = state_rc.borrow_mut();
let command_manager = state.command_manager.clone();
let command_manager = lock!(command_manager);
if let Some(command_cb) = command_manager.parse(&req) {
state.pending_futures.push(Box::new(command_cb));
} else {
report_js_error(&state, TheErr::CommandNotFound(req.payload));
}
}
JsMessage::LoadImportResp(resp) => {
trace!("Recv LoadImportResp:{:?}", resp.task_id);
debug_assert!(
state_rc
.borrow()
.pending_import_loaders
.contains_key(&resp.task_id)
);
let mut loader_cb = state_rc
.borrow_mut()
.pending_import_loaders
.remove(&resp.task_id)
.unwrap();
loader_cb(resp.maybe_source);
}
JsMessage::TickAgainResp => trace!("Recv TickAgainResp"),
JsMessage::FsOpenResp(resp) => {
trace!("Recv FsOpenResp:{:?}", resp.task_id);
debug_assert!(
state_rc.borrow().pending_tasks.contains_key(&resp.task_id)
);
let mut open_cb = state_rc
.borrow_mut()
.pending_tasks
.remove(&resp.task_id)
.unwrap();
open_cb(resp.maybe_result);
}
JsMessage::FsReadResp(resp) => {
trace!("Recv FsReadResp:{:?}", resp.task_id);
debug_assert!(
state_rc.borrow().pending_tasks.contains_key(&resp.task_id)
);
let mut read_cb = state_rc
.borrow_mut()
.pending_tasks
.remove(&resp.task_id)
.unwrap();
read_cb(resp.maybe_result);
}
JsMessage::FsWriteResp(resp) => {
trace!("Recv FsWriteResp:{:?}", resp.task_id);
debug_assert!(
state_rc.borrow().pending_tasks.contains_key(&resp.task_id)
);
let mut write_cb = state_rc
.borrow_mut()
.pending_tasks
.remove(&resp.task_id)
.unwrap();
write_cb(resp.maybe_result);
}
JsMessage::FsReadFileResp(resp) => {
trace!("Recv FsReadFileResp:{:?}", resp.task_id);
debug_assert!(
state_rc.borrow().pending_tasks.contains_key(&resp.task_id)
);
let mut read_cb = state_rc
.borrow_mut()
.pending_tasks
.remove(&resp.task_id)
.unwrap();
read_cb(resp.maybe_result);
}
JsMessage::FsReadTextFileResp(resp) => {
trace!("Recv FsReadTextFileResp:{:?}", resp.task_id);
debug_assert!(
state_rc.borrow().pending_tasks.contains_key(&resp.task_id)
);
let mut read_cb = state_rc
.borrow_mut()
.pending_tasks
.remove(&resp.task_id)
.unwrap();
read_cb(resp.maybe_result);
}
JsMessage::LoadTreeSitterParserResp(resp) => {
trace!("Recv LoadTreeSitterGrammarResp:{:?}", resp.task_id);
debug_assert!(
state_rc.borrow().pending_tasks.contains_key(&resp.task_id)
);
let mut write_cb = state_rc
.borrow_mut()
.pending_tasks
.remove(&resp.task_id)
.unwrap();
write_cb(resp.maybe_result);
}
}
}
let futures: Vec<Box<dyn JsFuture>> =
state_rc.borrow_mut().pending_futures.drain(..).collect();
for mut fut in futures {
fut.run(scope);
if let Some(exception) = check_exceptions(scope) {
trace!("Got exceptions when running pending futures: {exception:?}");
let state = state_rc.borrow();
report_js_error(&state, TheErr::JsError(Box::new(exception)));
}
run_next_tick_callbacks(scope);
}
}
fn fast_forward_imports(&mut self) {
let context = self.context();
v8::scope_with_context!(scope, &mut self.isolate, context);
let state_rc = JsRuntime::state(scope);
let mut ready_imports = vec![];
{
let mut state = state_rc.borrow_mut();
let state_ref = &mut *state;
let seen_modules = &mut state_ref.module_map.seen;
let pending_graphs = &mut state_ref.module_map.pending;
pending_graphs.retain(|graph_rc| {
let graph = graph_rc.borrow();
let graph_root = graph.root_rc();
let mut graph_root = graph_root.borrow_mut();
if let Some(message) = graph_root.exception_mut().take() {
let exception = v8::String::new(scope, &message).unwrap();
let exception = v8::Exception::error(scope, exception);
match graph.kind().clone() {
ImportKind::Static => unreachable!(),
ImportKind::Dynamic(main_promise) => {
for promise in
[main_promise].iter().chain(graph.same_origin().iter())
{
promise.open(scope).reject(scope, exception);
}
}
}
trace!(
"|JsRuntime::fast_forward_imports| ModuleMap failed {:?}, error {:?}",
graph_root.path(), message
);
return false;
}
if graph_root.status() != ModuleStatus::Ready {
graph_root.fast_forward(seen_modules);
return true;
}
ready_imports.push(Rc::clone(graph_rc));
trace!(
"|JsRuntime::fast_forward_imports| ModuleMap resolved {:?}",
graph_root.path()
);
false
});
}
for graph_rc in ready_imports {
v8::tc_scope!(let tc_scope, scope);
let graph = graph_rc.borrow();
let path = graph.root_rc().borrow().path().clone();
let module = state_rc.borrow().module_map.get(&path).unwrap();
let module = v8::Local::new(tc_scope, module);
if module
.instantiate_module(tc_scope, module_resolve_cb)
.is_none()
{
assert!(tc_scope.has_caught());
let exception = tc_scope.exception().unwrap();
let exception = JsError::from_v8_exception(tc_scope, exception, None);
let state = state_rc.borrow();
report_js_error(&state, TheErr::JsError(Box::new(exception)));
continue;
}
let _ = module.evaluate(tc_scope);
trace!(
"|JsRuntime::fast_forward_imports| ModuleMap evaluated {:?}, status: {:?}",
path,
module.get_status()
);
let is_root_module = !graph.root_rc().borrow().is_dynamic_import();
if module.get_status() == v8::ModuleStatus::Errored && is_root_module {
let exception = module.get_exception();
let exception = v8::Global::new(tc_scope, exception);
let mut state = state_rc.borrow_mut();
state.exceptions.capture_exception(exception.clone());
state.exceptions.remove_promise_rejection_entry(&exception);
drop(state);
if let Some(error) = check_exceptions(tc_scope) {
let state = state_rc.borrow();
report_js_error(&state, TheErr::JsError(Box::new(error)));
continue;
}
}
if let ImportKind::Dynamic(main_promise) = graph.kind().clone() {
let namespace = module.get_module_namespace();
for promise in [main_promise].iter().chain(graph.same_origin().iter())
{
promise.open(tc_scope).resolve(tc_scope, namespace);
}
}
}
run_next_tick_callbacks(scope);
}
}
impl JsRuntime {
pub fn has_promise_rejections(&mut self) -> bool {
self.get_state().borrow().exceptions.has_promise_rejection()
}
pub fn has_pending_imports(&mut self) -> bool {
let state_rc = self.get_state();
let state = state_rc.borrow();
!state.module_map.pending.is_empty()
}
pub fn pending_imports_count(&mut self) -> usize {
let state_rc = self.get_state();
let state = state_rc.borrow();
state.module_map.pending.len()
}
pub fn unresolved_imports_count(&mut self) -> usize {
let state_rc = self.get_state();
let state = state_rc.borrow();
state
.module_map
.seen
.iter()
.filter(|(_, v)| **v != ModuleStatus::Ready)
.count()
}
pub fn has_unresolved_imports(&mut self) -> bool {
let state_rc = self.get_state();
let state = state_rc.borrow();
state
.module_map
.seen
.iter()
.any(|(_, v)| *v != ModuleStatus::Ready)
}
pub fn has_pending_import_loaders(&mut self) -> bool {
let state_rc = self.get_state();
let state = state_rc.borrow();
!state.pending_import_loaders.is_empty()
}
pub fn pending_import_loaders_count(&mut self) -> usize {
let state_rc = self.get_state();
let state = state_rc.borrow();
state.pending_import_loaders.len()
}
}
impl JsRuntime {
pub fn state(isolate: &v8::Isolate) -> JsRuntimeStateRc {
isolate.get_slot::<JsRuntimeStateRc>().unwrap().clone()
}
pub fn get_state(&self) -> JsRuntimeStateRc {
Self::state(&self.isolate)
}
pub fn context(&mut self) -> v8::Global<v8::Context> {
let state_rc = self.get_state();
let state = state_rc.borrow();
state.context.clone()
}
}
}
pub fn execute_module<'s, 'b>(
scope: &mut v8::PinScope<'s, 'b>,
filename: &str,
source: Option<&str>,
) {
let state_rc = JsRuntime::state(scope);
let path = if source.is_some() {
filename.to_string()
} else {
let base = PATH_CONFIG.config_home().to_path_buf();
match resolve_import(&base.to_string_lossy(), filename, None) {
Ok(specifier) => specifier,
Err(e) => {
let state = state_rc.borrow_mut();
report_js_error(&state, e);
return;
}
}
};
v8::tc_scope!(let tc_scope, scope);
let module = match fetch_module_tree(tc_scope, filename, source) {
Some(module) => module,
None => {
assert!(tc_scope.has_caught());
let exception = tc_scope.exception().unwrap();
let exception = JsError::from_v8_exception(tc_scope, exception, None);
let state = state_rc.borrow_mut();
report_js_error(&state, TheErr::JsError(Box::new(exception)));
return;
}
};
if module
.instantiate_module(tc_scope, module_resolve_cb)
.is_none()
{
assert!(tc_scope.has_caught());
let exception = tc_scope.exception().unwrap();
let exception = JsError::from_v8_exception(tc_scope, exception, None);
let state = state_rc.borrow_mut();
report_js_error(&state, TheErr::JsError(Box::new(exception)));
return;
}
let _ = module.evaluate(tc_scope);
trace!(
"|execute_module| ModuleMap evaluated {:?}, status {:?}",
path,
module.get_status()
);
if module.get_status() == v8::ModuleStatus::Errored {
let exception = module.get_exception();
let exception = v8::Global::new(tc_scope, exception);
let state_rc = JsRuntime::state(tc_scope);
let mut state = state_rc.borrow_mut();
state.exceptions.capture_exception(exception.clone());
state.exceptions.remove_promise_rejection_entry(&exception);
drop(state);
if let Some(error) = check_exceptions(tc_scope) {
let state = state_rc.borrow();
report_js_error(&state, TheErr::JsError(Box::new(error)));
}
}
}
fn run_next_tick_callbacks(scope: &mut v8::PinScope) {
v8::tc_scope!(let tc_scope, scope);
tc_scope.perform_microtask_checkpoint();
}
pub fn check_exceptions(scope: &mut v8::PinScope) -> Option<JsError> {
let state_rc = JsRuntime::state(scope);
let maybe_exception = state_rc.borrow_mut().exceptions.exception.take();
if let Some(exception) = maybe_exception {
let state = state_rc.borrow();
let exception = v8::Local::new(scope, exception);
if let Some(callback) = state.exceptions.uncaught_exception_cb.as_ref() {
let callback = v8::Local::new(scope, callback);
let undefined = v8::undefined(scope).into();
let origin = v8::String::new(scope, "uncaughtException").unwrap();
v8::tc_scope!(let tc_scope, scope);
drop(state);
callback.call(tc_scope, undefined, &[exception, origin.into()]);
if tc_scope.has_caught() {
let exception = tc_scope.exception().unwrap();
let exception = v8::Local::new(tc_scope, exception);
let error = JsError::from_v8_exception(tc_scope, exception, None);
return Some(error);
}
return None;
}
let error = JsError::from_v8_exception(scope, exception, None);
return Some(error);
}
let promise_rejections: Vec<PromiseRejectionEntry> = state_rc
.borrow_mut()
.exceptions
.promise_rejections
.drain(..)
.collect();
for (promise, exception) in promise_rejections.iter() {
let state = state_rc.borrow_mut();
let promise = v8::Local::new(scope, promise);
let exception = v8::Local::new(scope, exception);
if let Some(callback) = state.exceptions.unhandled_rejection_cb.as_ref() {
let callback = v8::Local::new(scope, callback);
let undefined = v8::undefined(scope).into();
v8::tc_scope!(let tc_scope, scope);
drop(state);
callback.call(tc_scope, undefined, &[exception, promise.into()]);
if tc_scope.has_caught() {
let exception = tc_scope.exception().unwrap();
let exception = v8::Local::new(tc_scope, exception);
let error = JsError::from_v8_exception(tc_scope, exception, None);
return Some(error);
}
continue;
}
if let Some(callback) = state.exceptions.uncaught_exception_cb.as_ref() {
let callback = v8::Local::new(scope, callback);
let undefined = v8::undefined(scope).into();
let origin = v8::String::new(scope, "unhandledRejection").unwrap();
v8::tc_scope!(let tc_scope, scope);
drop(state);
callback.call(tc_scope, undefined, &[exception, origin.into()]);
if tc_scope.has_caught() {
let exception = tc_scope.exception().unwrap();
let exception = v8::Local::new(tc_scope, exception);
let error = JsError::from_v8_exception(tc_scope, exception, None);
return Some(error);
}
continue;
}
let prefix = Some("(in promise) ");
let error = JsError::from_v8_exception(scope, exception, prefix);
return Some(error);
}
None
}