[oden-js] Track rejected promises, panic on unhandled rejections
This commit is contained in:
parent
17c701a7d6
commit
22327a71b3
4 changed files with 108 additions and 14 deletions
|
|
@ -16,7 +16,7 @@ pub use atom::{Atom, AtomRef};
|
||||||
pub use class::{Class, ClassID};
|
pub use class::{Class, ClassID};
|
||||||
pub use context::{Context, ContextRef, EvalFlags};
|
pub use context::{Context, ContextRef, EvalFlags};
|
||||||
pub use conversion::*;
|
pub use conversion::*;
|
||||||
pub use promise::Promise;
|
pub use promise::{DefaultRejectedPromiseTracker, Promise, RejectedPromiseTracker};
|
||||||
pub use runtime::Runtime;
|
pub use runtime::Runtime;
|
||||||
pub use value::{Value, ValueRef, ValueType};
|
pub use value::{Value, ValueRef, ValueType};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{ContextRef, ValueResult};
|
use crate::{ContextRef, ValueRef, ValueResult};
|
||||||
use std::sync::atomic::{AtomicU64, Ordering};
|
use std::sync::atomic::{AtomicU64, Ordering};
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
|
|
||||||
|
|
@ -72,3 +72,53 @@ impl Drop for Promise {
|
||||||
assert!(self.complete);
|
assert!(self.complete);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait RejectedPromiseTracker {
|
||||||
|
fn on_rejected_promise(
|
||||||
|
&self,
|
||||||
|
ctx: &ContextRef,
|
||||||
|
promise: &ValueRef,
|
||||||
|
reason: &ValueRef,
|
||||||
|
is_handled: bool,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> RejectedPromiseTracker for T
|
||||||
|
where
|
||||||
|
T: Fn(&ContextRef, &ValueRef, &ValueRef, bool) -> (),
|
||||||
|
{
|
||||||
|
fn on_rejected_promise(
|
||||||
|
&self,
|
||||||
|
ctx: &ContextRef,
|
||||||
|
promise: &ValueRef,
|
||||||
|
reason: &ValueRef,
|
||||||
|
is_handled: bool,
|
||||||
|
) {
|
||||||
|
self(ctx, promise, reason, is_handled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DefaultRejectedPromiseTracker {}
|
||||||
|
|
||||||
|
impl DefaultRejectedPromiseTracker {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
DefaultRejectedPromiseTracker {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RejectedPromiseTracker for DefaultRejectedPromiseTracker {
|
||||||
|
fn on_rejected_promise(
|
||||||
|
&self,
|
||||||
|
ctx: &ContextRef,
|
||||||
|
_promise: &ValueRef,
|
||||||
|
reason: &ValueRef,
|
||||||
|
is_handled: bool,
|
||||||
|
) {
|
||||||
|
if !is_handled {
|
||||||
|
let reason_str = reason.to_string(ctx).expect(
|
||||||
|
"Unhandled rejected promise: reason unknown: unable to convert reason to string",
|
||||||
|
);
|
||||||
|
panic!("Unhandled rejected promise: {reason_str}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
module::loader::{load_module, DefaultModuleLoader, ModuleLoader},
|
module::loader::{load_module, DefaultModuleLoader, ModuleLoader},
|
||||||
promise::{PromiseEvent, PromiseHandle},
|
promise::{PromiseEvent, PromiseHandle},
|
||||||
ContextRef, Promise, Result, Value,
|
ContextRef, DefaultRejectedPromiseTracker, Promise, RejectedPromiseTracker, Result, Value,
|
||||||
|
ValueRef,
|
||||||
};
|
};
|
||||||
use oden_js_sys as sys;
|
use oden_js_sys as sys;
|
||||||
use std::cell::{RefCell, RefMut};
|
use std::cell::{RefCell, RefMut};
|
||||||
|
|
@ -45,20 +46,19 @@ struct PrivateState {
|
||||||
promise_send: Sender<(PromiseHandle, PromiseEvent)>,
|
promise_send: Sender<(PromiseHandle, PromiseEvent)>,
|
||||||
promise_recv: Receiver<(PromiseHandle, PromiseEvent)>,
|
promise_recv: Receiver<(PromiseHandle, PromiseEvent)>,
|
||||||
promise_table: HashMap<PromiseHandle, PromiseEntry>, // !
|
promise_table: HashMap<PromiseHandle, PromiseEntry>, // !
|
||||||
|
rejection_tracker: Arc<Box<dyn RejectedPromiseTracker>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PrivateState {
|
impl PrivateState {
|
||||||
pub fn new<T>(loader: T) -> Box<RefCell<Self>>
|
pub fn new() -> Box<RefCell<Self>> {
|
||||||
where
|
|
||||||
T: ModuleLoader + 'static,
|
|
||||||
{
|
|
||||||
let (send, recv) = channel();
|
let (send, recv) = channel();
|
||||||
Box::new(RefCell::new(PrivateState {
|
Box::new(RefCell::new(PrivateState {
|
||||||
refs: 1,
|
refs: 1,
|
||||||
loader: Arc::new(Box::new(loader)),
|
loader: Arc::new(Box::new(DefaultModuleLoader::new())),
|
||||||
promise_send: send,
|
promise_send: send,
|
||||||
promise_recv: recv,
|
promise_recv: recv,
|
||||||
promise_table: HashMap::new(),
|
promise_table: HashMap::new(),
|
||||||
|
rejection_tracker: Arc::new(Box::new(DefaultRejectedPromiseTracker::new())),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,6 +92,28 @@ impl PrivateState {
|
||||||
let context = ContextRef::from_raw(ctx);
|
let context = ContextRef::from_raw(ctx);
|
||||||
load_module(&context, path, &loader)
|
load_module(&context, path, &loader)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn promise_rejection_tracker(
|
||||||
|
ctx: *mut sys::JSContext,
|
||||||
|
promise: sys::JSValue,
|
||||||
|
reason: sys::JSValue,
|
||||||
|
is_handled: i32,
|
||||||
|
opaque: *mut std::os::raw::c_void,
|
||||||
|
) {
|
||||||
|
let ctx = ContextRef::from_raw(ctx);
|
||||||
|
let promise = ValueRef::from_raw(promise);
|
||||||
|
let reason = ValueRef::from_raw(reason);
|
||||||
|
let is_handled = is_handled != 0;
|
||||||
|
let handler = unsafe {
|
||||||
|
let ptr = opaque as *const RefCell<PrivateState>;
|
||||||
|
ptr.as_ref()
|
||||||
|
.expect("We already know this runtime is one of ours!")
|
||||||
|
.borrow()
|
||||||
|
.rejection_tracker
|
||||||
|
.clone()
|
||||||
|
};
|
||||||
|
handler.on_rejected_promise(&ctx, &promise, &reason, is_handled);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -101,16 +123,17 @@ pub struct Runtime {
|
||||||
|
|
||||||
impl Runtime {
|
impl Runtime {
|
||||||
pub fn new() -> Runtime {
|
pub fn new() -> Runtime {
|
||||||
Self::with_loader(DefaultModuleLoader::new())
|
let state = PrivateState::new();
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_loader<TLoader: ModuleLoader + 'static>(loader: TLoader) -> Runtime {
|
|
||||||
let state = PrivateState::new(loader);
|
|
||||||
let rt = unsafe {
|
let rt = unsafe {
|
||||||
let rt = sys::JS_NewRuntime();
|
let rt = sys::JS_NewRuntime();
|
||||||
let state = Box::into_raw(state) as *mut _;
|
let state = Box::into_raw(state) as *mut _;
|
||||||
sys::JS_SetRuntimeOpaque(rt, state);
|
sys::JS_SetRuntimeOpaque(rt, state);
|
||||||
sys::JS_SetModuleLoaderFunc(rt, None, Some(PrivateState::module_loader), state);
|
sys::JS_SetModuleLoaderFunc(rt, None, Some(PrivateState::module_loader), state);
|
||||||
|
sys::JS_SetHostPromiseRejectionTracker(
|
||||||
|
rt,
|
||||||
|
Some(PrivateState::promise_rejection_tracker),
|
||||||
|
state,
|
||||||
|
);
|
||||||
rt
|
rt
|
||||||
};
|
};
|
||||||
Runtime { rt }
|
Runtime { rt }
|
||||||
|
|
@ -141,6 +164,26 @@ impl Runtime {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the handler for loading modules. By default this is an instance
|
||||||
|
/// of `DefaultModuleLoader`.
|
||||||
|
pub fn set_module_loader<T>(&mut self, loader: T)
|
||||||
|
where
|
||||||
|
T: ModuleLoader + 'static,
|
||||||
|
{
|
||||||
|
let mut state = unsafe { PrivateState::from_rt_mut(self.rt) };
|
||||||
|
state.loader = Arc::new(Box::new(loader));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a tracker to be notified whenever a promise is rejected. By
|
||||||
|
/// default, this is an instance of `DefaultRejectedPromiseHandler`.
|
||||||
|
pub fn set_rejected_promise_tracker<T>(&mut self, tracker: T)
|
||||||
|
where
|
||||||
|
T: RejectedPromiseTracker + 'static,
|
||||||
|
{
|
||||||
|
let mut state = unsafe { PrivateState::from_rt_mut(self.rt) };
|
||||||
|
state.rejection_tracker = Arc::new(Box::new(tracker));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn run_gc(&mut self) {
|
pub fn run_gc(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
sys::JS_RunGC(self.rt);
|
sys::JS_RunGC(self.rt);
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,8 @@ pub struct ScriptContext {
|
||||||
|
|
||||||
impl ScriptContext {
|
impl ScriptContext {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let runtime = Runtime::with_loader(Loader::new());
|
let mut runtime = Runtime::new();
|
||||||
|
runtime.set_module_loader(Loader::new());
|
||||||
|
|
||||||
let mut context = Context::new(runtime);
|
let mut context = Context::new(runtime);
|
||||||
context.add_intrinsic_bigfloat();
|
context.add_intrinsic_bigfloat();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue