use std::cell::RefCell; use std::collections::VecDeque; pub(crate) struct UncaughtException { pub(crate) value: v8::Global, pub(crate) filename: String, pub(crate) line: u32, pub(crate) column: u32, } type ExceptionSlot = RefCell>; extern "C" fn uncaught_exception(message: v8::Local, exception: v8::Local) { // V8 reports microtask exceptions to message listeners instead of the enclosing TryCatch. // SAFETY: V8 invokes this listener with a valid message from the active isolate. v8::callback_scope!(unsafe scope, message); let exception = UncaughtException { value: v8::Global::new(scope, exception), filename: message .get_script_resource_name(scope) .map(|name| name.to_rust_string_lossy(scope)) .unwrap_or_default(), line: message .get_line_number(scope) .unwrap_or_default() .try_into() .unwrap_or_default(), column: message .get_start_column() .saturating_add(1) .try_into() .unwrap_or_default(), }; scope .get_slot::() .unwrap() .borrow_mut() .push_back(exception); } pub(crate) fn clear(isolate: &mut v8::Isolate) { isolate.remove_slot::(); } pub(crate) fn external_references() -> Vec { vec![v8::ExternalReference { pointer: uncaught_exception as *mut std::ffi::c_void, }] } pub(crate) fn init(isolate: &mut v8::Isolate) { isolate.set_slot::(RefCell::new(VecDeque::new())); } /// Message listeners belong to the isolate heap, and V8 serializes them into snapshots. pub(crate) fn add_listener(isolate: &mut v8::Isolate) { isolate.add_message_listener(uncaught_exception); } pub(crate) fn enqueue<'s>( scope: &mut v8::PinScope<'s, '_>, args: v8::FunctionCallbackArguments<'s>, _result: v8::ReturnValue, ) { let Ok(callback) = v8::Local::::try_from(args.get(0)) else { let message = v8::String::new(scope, "Expected a microtask callback").unwrap(); let error = v8::Exception::type_error(scope, message); scope.throw_exception(error); return; }; scope.enqueue_microtask(callback); } pub(crate) fn process_exceptions(scope: &mut v8::PinScope) -> Option> { let mut fatal = None; loop { let exceptions = std::mem::take(&mut *scope.get_slot::().unwrap().borrow_mut()); if exceptions.is_empty() { return fatal; } for exception in exceptions { let error = v8::Local::new(scope, &exception.value); if !crate::reporting::report_exception_with_location( scope, error, &exception.filename, exception.line, exception.column, ) && fatal.is_none() { fatal = Some(v8::Global::new(scope, error)); } } crate::op::checkpoint(scope); } }