diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/core_isolate.rs | 22 | ||||
-rw-r--r-- | core/errors.rs | 126 | ||||
-rw-r--r-- | core/es_isolate.rs | 3 | ||||
-rw-r--r-- | core/lib.rs | 1 |
4 files changed, 114 insertions, 38 deletions
diff --git a/core/core_isolate.rs b/core/core_isolate.rs index 160e37f1d..f4acbd1ac 100644 --- a/core/core_isolate.rs +++ b/core/core_isolate.rs @@ -87,6 +87,17 @@ impl StartupData<'_> { type JSErrorCreateFn = dyn Fn(JSError) -> ErrBox; +pub trait RustErrToJsonFn: for<'e> Fn(&'e ErrBox) -> Box<[u8]> {} +impl<F> RustErrToJsonFn for F where for<'e> F: Fn(&'e ErrBox) -> Box<[u8]> {} + +fn rust_err_to_json(e: &ErrBox) -> Box<[u8]> { + let value = json!({ + "kind": "Error", + "message": e.to_string() + }); + serde_json::to_vec(&value).unwrap().into_boxed_slice() +} + /// Objects that need to live as long as the isolate #[derive(Default)] struct IsolateAllocations { @@ -123,6 +134,7 @@ pub struct CoreIsolateState { pub(crate) js_macrotask_cb: Option<v8::Global<v8::Function>>, pub(crate) pending_promise_exceptions: HashMap<i32, v8::Global<v8::Value>>, pub(crate) js_error_create_fn: Box<JSErrorCreateFn>, + pub rust_err_to_json_fn: &'static dyn RustErrToJsonFn, pub(crate) shared: SharedQueue, pending_ops: FuturesUnordered<PendingOpFuture>, pending_unref_ops: FuturesUnordered<PendingOpFuture>, @@ -307,6 +319,7 @@ impl CoreIsolate { js_recv_cb: None, js_macrotask_cb: None, js_error_create_fn: Box::new(JSError::create), + rust_err_to_json_fn: &rust_err_to_json, shared: SharedQueue::new(RECOMMENDED_SIZE), pending_ops: FuturesUnordered::new(), pending_unref_ops: FuturesUnordered::new(), @@ -539,8 +552,8 @@ fn serialize_result( Err(err) => json!({ "promiseId": promise_id , "err": { + "kind": "Error", "message": err.to_string(), - "kind": "Other", // TODO(ry) Figure out how to propagate errors. } }), }; @@ -668,6 +681,13 @@ impl CoreIsolateState { self.js_error_create_fn = Box::new(f); } + pub fn set_rust_err_to_json_fn( + &mut self, + f: &'static (impl for<'e> Fn(&'e ErrBox) -> Box<[u8]> + 'static), + ) { + self.rust_err_to_json_fn = f; + } + pub fn dispatch_op<'s>( &mut self, scope: &mut v8::HandleScope<'s>, diff --git a/core/errors.rs b/core/errors.rs index 218d7c619..3dfe5824c 100644 --- a/core/errors.rs +++ b/core/errors.rs @@ -3,11 +3,12 @@ use rusty_v8 as v8; use std::any::Any; use std::any::TypeId; +use std::borrow::Cow; use std::convert::TryFrom; use std::convert::TryInto; use std::error::Error; use std::fmt; -use std::ops::Deref; +use std::io; // The Send and Sync traits are required because deno is multithreaded and we // need to be able to handle errors across threads. @@ -15,60 +16,97 @@ pub trait AnyError: Any + Error + Send + Sync + 'static {} impl<T> AnyError for T where T: Any + Error + Send + Sync + Sized + 'static {} #[derive(Debug)] -pub struct ErrBox(Box<dyn AnyError>); +pub enum ErrBox { + Simple { + class: &'static str, + message: Cow<'static, str>, + }, + Boxed(Box<dyn AnyError>), +} -impl dyn AnyError { - pub fn downcast_ref<T: AnyError>(&self) -> Option<&T> { - if Any::type_id(self) == TypeId::of::<T>() { - let target = self as *const Self as *const T; - let target = unsafe { &*target }; - Some(target) - } else { - None +impl ErrBox { + pub fn new( + class: &'static str, + message: impl Into<Cow<'static, str>>, + ) -> Self { + Self::Simple { + class, + message: message.into(), } } -} -impl ErrBox { + pub fn bad_resource(message: impl Into<Cow<'static, str>>) -> Self { + Self::new("BadResource", message) + } + + pub fn bad_resource_id() -> Self { + Self::new("BadResource", "Bad resource ID") + } + + pub fn error(message: impl Into<Cow<'static, str>>) -> Self { + Self::new("Error", message) + } + + pub fn not_supported() -> Self { + Self::new("NotSupported", "The operation is supported") + } + + pub fn resource_unavailable() -> Self { + Self::new( + "Busy", + "Resource is unavailable because it is in use by a promise", + ) + } + + pub fn type_error(message: impl Into<Cow<'static, str>>) -> Self { + Self::new("TypeError", message) + } + + pub fn last_os_error() -> Self { + Self::from(io::Error::last_os_error()) + } + pub fn downcast<T: AnyError>(self) -> Result<T, Self> { - if Any::type_id(&*self.0) == TypeId::of::<T>() { - let target = Box::into_raw(self.0) as *mut T; - let target = unsafe { Box::from_raw(target) }; - Ok(*target) - } else { - Err(self) + match self { + Self::Boxed(error) if Any::type_id(&*error) == TypeId::of::<T>() => { + let error = Box::into_raw(error) as *mut T; + let error = unsafe { Box::from_raw(error) }; + Ok(*error) + } + other => Err(other), } } -} -impl AsRef<dyn AnyError> for ErrBox { - fn as_ref(&self) -> &dyn AnyError { - self.0.as_ref() + pub fn downcast_ref<T: AnyError>(&self) -> Option<&T> { + match self { + Self::Boxed(error) if Any::type_id(&**error) == TypeId::of::<T>() => { + let error = &**error as *const dyn AnyError as *const T; + let error = unsafe { &*error }; + Some(error) + } + _ => None, + } } } -impl Deref for ErrBox { - type Target = Box<dyn AnyError>; - fn deref(&self) -> &Self::Target { - &self.0 +impl fmt::Display for ErrBox { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Simple { message, .. } => f.write_str(message), + Self::Boxed(error) => error.fmt(f), + } } } impl<T: AnyError> From<T> for ErrBox { fn from(error: T) -> Self { - Self(Box::new(error)) + Self::Boxed(Box::new(error)) } } impl From<Box<dyn AnyError>> for ErrBox { fn from(boxed: Box<dyn AnyError>) -> Self { - Self(boxed) - } -} - -impl fmt::Display for ErrBox { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.0.fmt(f) + Self::Boxed(boxed) } } @@ -116,7 +154,7 @@ fn get_property<'a>( impl JSError { pub(crate) fn create(js_error: Self) -> ErrBox { - ErrBox::from(js_error) + js_error.into() } pub fn from_v8_exception( @@ -362,6 +400,7 @@ pub(crate) fn attach_handle_to_error( err: ErrBox, handle: v8::Local<v8::Value>, ) -> ErrBox { + // TODO(bartomieju): this is a special case... ErrWithV8Handle::new(scope, err, handle).into() } @@ -406,3 +445,20 @@ impl fmt::Debug for ErrWithV8Handle { self.err.fmt(f) } } + +#[cfg(tests)] +mod tests { + #[test] + fn test_bad_resource() { + let err = ErrBox::bad_resource("Resource has been closed".to_string()); + assert_eq!(err.1, "BadResource"); + assert_eq!(err.to_string(), "Resource has been closed"); + } + + #[test] + fn test_bad_resource_id() { + let err = ErrBox::bad_resource_id(); + assert_eq!(err.1, "BadResource"); + assert_eq!(err.to_string(), "Bad resource ID"); + } +} diff --git a/core/es_isolate.rs b/core/es_isolate.rs index 3ede1a5e4..a829a3a3b 100644 --- a/core/es_isolate.rs +++ b/core/es_isolate.rs @@ -784,8 +784,7 @@ pub mod tests { _maybe_referrer: Option<ModuleSpecifier>, _is_dyn_import: bool, ) -> Pin<Box<ModuleSourceFuture>> { - async { Err(ErrBox::from(io::Error::from(io::ErrorKind::NotFound))) } - .boxed() + async { Err(io::Error::from(io::ErrorKind::NotFound).into()) }.boxed() } } diff --git a/core/lib.rs b/core/lib.rs index 940e0c026..78a2fc244 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -28,6 +28,7 @@ pub use crate::core_isolate::js_check; pub use crate::core_isolate::CoreIsolate; pub use crate::core_isolate::CoreIsolateState; pub use crate::core_isolate::HeapLimits; +pub use crate::core_isolate::RustErrToJsonFn; pub use crate::core_isolate::Script; pub use crate::core_isolate::Snapshot; pub use crate::core_isolate::StartupData; |