diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/any_error.rs | 68 | ||||
-rw-r--r-- | core/examples/http_bench.rs | 2 | ||||
-rw-r--r-- | core/isolate.rs | 148 | ||||
-rw-r--r-- | core/js_errors.rs | 173 | ||||
-rw-r--r-- | core/lib.rs | 2 | ||||
-rw-r--r-- | core/modules.rs | 109 |
6 files changed, 284 insertions, 218 deletions
diff --git a/core/any_error.rs b/core/any_error.rs new file mode 100644 index 000000000..001ca54eb --- /dev/null +++ b/core/any_error.rs @@ -0,0 +1,68 @@ +use std::any::{Any, TypeId}; +use std::convert::From; +use std::error::Error; +use std::fmt; +use std::ops::Deref; + +// The Send and Sync traits are required because deno is multithreaded and we +// need to beable to handle errors across threads. +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>); + +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 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) + } + } +} + +impl AsRef<dyn AnyError> for ErrBox { + fn as_ref(&self) -> &dyn AnyError { + self.0.as_ref() + } +} + +impl Deref for ErrBox { + type Target = Box<AnyError>; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<T: AnyError> From<T> for ErrBox { + fn from(error: T) -> Self { + Self(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) + } +} diff --git a/core/examples/http_bench.rs b/core/examples/http_bench.rs index ee8de5086..98f11bc4d 100644 --- a/core/examples/http_bench.rs +++ b/core/examples/http_bench.rs @@ -310,7 +310,7 @@ fn op_write(rid: i32, zero_copy_buf: Option<PinnedBuf>) -> Box<HttpBenchOp> { ) } -fn js_check(r: Result<(), JSError>) { +fn js_check(r: Result<(), ErrBox>) { if let Err(e) = r { panic!(e.to_string()); } diff --git a/core/isolate.rs b/core/isolate.rs index 2e6406e56..38f7b7987 100644 --- a/core/isolate.rs +++ b/core/isolate.rs @@ -4,7 +4,9 @@ // isolate to keep the Isolate struct from becoming too bloating for users who // do not need asynchronous module loading. -use crate::js_errors::JSError; +use crate::any_error::ErrBox; +use crate::js_errors::CoreJSError; +use crate::js_errors::V8Exception; use crate::libdeno; use crate::libdeno::deno_buf; use crate::libdeno::deno_dyn_import_id; @@ -81,6 +83,8 @@ type CoreDispatchFn = dyn Fn(&[u8], Option<PinnedBuf>) -> CoreOp; pub type DynImportFuture = Box<dyn Future<Item = deno_mod, Error = ()> + Send>; type DynImportFn = dyn Fn(&str, &str) -> DynImportFuture; +type JSErrorCreateFn = dyn Fn(V8Exception) -> ErrBox; + /// Wraps DynImportFuture to include the deno_dyn_import_id, so that it doesn't /// need to be exposed. struct DynImport { @@ -114,6 +118,7 @@ pub struct Isolate { shared_libdeno_isolate: Arc<Mutex<Option<*const libdeno::isolate>>>, dispatch: Option<Arc<CoreDispatchFn>>, dyn_import: Option<Arc<DynImportFn>>, + js_error_create: Arc<JSErrorCreateFn>, needs_init: bool, shared: SharedQueue, pending_ops: FuturesUnordered<CoreOpAsyncFuture>, @@ -178,6 +183,7 @@ impl Isolate { shared_libdeno_isolate: Arc::new(Mutex::new(Some(libdeno_isolate))), dispatch: None, dyn_import: None, + js_error_create: Arc::new(CoreJSError::from_v8_exception), shared, needs_init, pending_ops: FuturesUnordered::new(), @@ -204,6 +210,16 @@ impl Isolate { self.dyn_import = Some(Arc::new(f)); } + /// Allows a callback to be set whenever a V8 exception is made. This allows + /// the caller to wrap the V8Exception into an error. By default this callback + /// is set to CoreJSError::from_v8_exception. + pub fn set_js_error_create<F>(&mut self, f: F) + where + F: Fn(V8Exception) -> ErrBox + 'static, + { + self.js_error_create = Arc::new(f); + } + /// Get a thread safe handle on the isolate. pub fn shared_isolate_handle(&mut self) -> IsolateHandle { IsolateHandle { @@ -307,11 +323,16 @@ impl Isolate { self as *const _ as *const c_void } + /// Executes traditional JavaScript code (traditional = not ES modules) + /// + /// ErrBox can be downcast to a type that exposes additional information about + /// the V8 exception. By default this type is CoreJSError, however it may be a + /// different type if Isolate::set_js_error_create() has been used. pub fn execute( &mut self, js_filename: &str, js_source: &str, - ) -> Result<(), JSError> { + ) -> Result<(), ErrBox> { self.shared_init(); let filename = CString::new(js_filename).unwrap(); let source = CString::new(js_source).unwrap(); @@ -323,22 +344,20 @@ impl Isolate { source.as_ptr(), ) }; - if let Some(err) = self.last_exception() { - return Err(err); - } - Ok(()) + self.check_last_exception() } - fn last_exception(&self) -> Option<JSError> { + fn check_last_exception(&self) -> Result<(), ErrBox> { let ptr = unsafe { libdeno::deno_last_exception(self.libdeno_isolate) }; if ptr.is_null() { - None + Ok(()) } else { + let js_error_create = &*self.js_error_create; let cstr = unsafe { CStr::from_ptr(ptr) }; - let v8_exception = cstr.to_str().unwrap(); - debug!("v8_exception\n{}\n", v8_exception); - let js_error = JSError::from_v8_exception(v8_exception).unwrap(); - Some(js_error) + let json_str = cstr.to_str().unwrap(); + let v8_exception = V8Exception::from_json(json_str).unwrap(); + let js_error = js_error_create(v8_exception); + Err(js_error) } } @@ -348,7 +367,7 @@ impl Isolate { } } - fn respond(&mut self, maybe_buf: Option<&[u8]>) -> Result<(), JSError> { + fn respond(&mut self, maybe_buf: Option<&[u8]>) -> Result<(), ErrBox> { let buf = match maybe_buf { None => deno_buf::empty(), Some(r) => deno_buf::from(r), @@ -356,11 +375,7 @@ impl Isolate { unsafe { libdeno::deno_respond(self.libdeno_isolate, self.as_raw_ptr(), buf) } - if let Some(err) = self.last_exception() { - Err(err) - } else { - Ok(()) - } + self.check_last_exception() } /// Low-level module creation. @@ -369,7 +384,7 @@ impl Isolate { main: bool, name: &str, source: &str, - ) -> Result<deno_mod, JSError> { + ) -> Result<deno_mod, ErrBox> { let name_ = CString::new(name.to_string()).unwrap(); let name_ptr = name_.as_ptr() as *const libc::c_char; @@ -379,12 +394,11 @@ impl Isolate { let id = unsafe { libdeno::deno_mod_new(self.libdeno_isolate, main, name_ptr, source_ptr) }; - if let Some(js_error) = self.last_exception() { - assert_eq!(id, 0); - return Err(js_error); - } - Ok(id) + self.check_last_exception().map(|_| id).map_err(|err| { + assert_eq!(id, 0); + err + }) } pub fn mod_get_imports(&self, id: deno_mod) -> Vec<String> { @@ -402,23 +416,29 @@ impl Isolate { out } - pub fn snapshot(&self) -> Result<Snapshot1<'static>, JSError> { + /// Takes a snapshot. The isolate should have been created with will_snapshot + /// set to true. + /// + /// ErrBox can be downcast to a type that exposes additional information about + /// the V8 exception. By default this type is CoreJSError, however it may be a + /// different type if Isolate::set_js_error_create() has been used. + pub fn snapshot(&self) -> Result<Snapshot1<'static>, ErrBox> { let snapshot = unsafe { libdeno::deno_snapshot_new(self.libdeno_isolate) }; - if let Some(js_error) = self.last_exception() { - assert_eq!(snapshot.data_ptr, null()); - assert_eq!(snapshot.data_len, 0); - return Err(js_error); + match self.check_last_exception() { + Ok(..) => Ok(snapshot), + Err(err) => { + assert_eq!(snapshot.data_ptr, null()); + assert_eq!(snapshot.data_len, 0); + Err(err) + } } - assert_ne!(snapshot.data_ptr, null()); - assert_ne!(snapshot.data_len, 0); - Ok(snapshot) } fn dyn_import_done( &self, id: libdeno::deno_dyn_import_id, mod_id: deno_mod, - ) -> Result<(), JSError> { + ) -> Result<(), ErrBox> { debug!("dyn_import_done {} {}", id, mod_id); unsafe { libdeno::deno_dyn_import( @@ -428,11 +448,10 @@ impl Isolate { mod_id, ) }; - if let Some(js_error) = self.last_exception() { + self.check_last_exception().map_err(|err| { assert_eq!(id, 0); - return Err(js_error); - } - Ok(()) + err + }) } } @@ -458,11 +477,16 @@ impl<'a> ResolveContext<'a> { } impl Isolate { + /// Instanciates a ES module + /// + /// ErrBox can be downcast to a type that exposes additional information about + /// the V8 exception. By default this type is CoreJSError, however it may be a + /// different type if Isolate::set_js_error_create() has been used. pub fn mod_instantiate( &mut self, id: deno_mod, resolve_fn: &mut ResolveFn, - ) -> Result<(), JSError> { + ) -> Result<(), ErrBox> { let libdeno_isolate = self.libdeno_isolate; let mut ctx = ResolveContext { resolve_fn }; unsafe { @@ -473,11 +497,7 @@ impl Isolate { Self::resolve_cb, ) }; - - if let Some(js_error) = self.last_exception() { - return Err(js_error); - } - Ok(()) + self.check_last_exception() } /// Called during mod_instantiate() only. @@ -494,15 +514,17 @@ impl Isolate { resolve_fn(specifier, referrer) } - pub fn mod_evaluate(&mut self, id: deno_mod) -> Result<(), JSError> { + /// Evaluates an already instantiated ES module. + /// + /// ErrBox can be downcast to a type that exposes additional information about + /// the V8 exception. By default this type is CoreJSError, however it may be a + /// different type if Isolate::set_js_error_create() has been used. + pub fn mod_evaluate(&mut self, id: deno_mod) -> Result<(), ErrBox> { self.shared_init(); unsafe { libdeno::deno_mod_evaluate(self.libdeno_isolate, self.as_raw_ptr(), id) }; - if let Some(js_error) = self.last_exception() { - return Err(js_error); - } - Ok(()) + self.check_last_exception() } } @@ -525,9 +547,9 @@ impl Drop for LockerScope { impl Future for Isolate { type Item = (); - type Error = JSError; + type Error = ErrBox; - fn poll(&mut self) -> Poll<(), JSError> { + fn poll(&mut self) -> Poll<(), ErrBox> { // Lock the current thread for V8. let _locker = LockerScope::new(self.libdeno_isolate); @@ -579,9 +601,7 @@ impl Future for Isolate { } self.check_promise_errors(); - if let Some(err) = self.last_exception() { - return Err(err); - } + self.check_last_exception()?; // We're idle if pending_ops is empty. if self.pending_ops.is_empty() { @@ -616,7 +636,7 @@ impl IsolateHandle { } } -pub fn js_check(r: Result<(), JSError>) { +pub fn js_check(r: Result<(), ErrBox>) { if let Err(e) = r { panic!(e.to_string()); } @@ -814,7 +834,7 @@ pub mod tests { "#, )); assert_eq!(dispatch_count.load(Ordering::Relaxed), 1); - assert_eq!(Ok(Async::Ready(())), isolate.poll()); + assert_eq!(Async::Ready(()), isolate.poll().unwrap()); assert_eq!(dispatch_count.load(Ordering::Relaxed), 1); js_check(isolate.execute( "check2.js", @@ -825,11 +845,11 @@ pub mod tests { "#, )); assert_eq!(dispatch_count.load(Ordering::Relaxed), 2); - assert_eq!(Ok(Async::Ready(())), isolate.poll()); + assert_eq!(Async::Ready(()), isolate.poll().unwrap()); js_check(isolate.execute("check3.js", "assert(nrecv == 2)")); assert_eq!(dispatch_count.load(Ordering::Relaxed), 2); // We are idle, so the next poll should be the last. - assert_eq!(Ok(Async::Ready(())), isolate.poll()); + assert_eq!(Async::Ready(()), isolate.poll().unwrap()); }); } @@ -865,7 +885,7 @@ pub mod tests { "#, )); assert_eq!(dispatch_count.load(Ordering::Relaxed), 2); - assert_eq!(Ok(Async::Ready(())), isolate.poll()); + assert_eq!(Async::Ready(()), isolate.poll().unwrap()); js_check(isolate.execute("send1.js", "assert(nrecv === 2);")); }); } @@ -949,9 +969,9 @@ pub mod tests { )); assert_eq!(count.load(Ordering::Relaxed), 1); - assert_eq!(Ok(Ready(())), isolate.poll()); + assert_eq!(Ready(()), isolate.poll().unwrap()); assert_eq!(count.load(Ordering::Relaxed), 2); - assert_eq!(Ok(Ready(())), isolate.poll()); + assert_eq!(Ready(()), isolate.poll().unwrap()); assert_eq!(count.load(Ordering::Relaxed), 2); }) } @@ -1090,7 +1110,7 @@ pub mod tests { "#, )); assert_eq!(dispatch_count.load(Ordering::Relaxed), 1); - assert_eq!(Ok(Async::Ready(())), isolate.poll()); + assert_eq!(Async::Ready(()), isolate.poll().unwrap()); js_check(isolate.execute("check.js", "assert(asyncRecv == 1);")); }); } @@ -1118,7 +1138,7 @@ pub mod tests { "#, )); assert_eq!(dispatch_count.load(Ordering::Relaxed), 1); - assert_eq!(Ok(()), poll_until_ready(&mut isolate, 3)); + poll_until_ready(&mut isolate, 3).unwrap(); js_check(isolate.execute("check.js", "assert(asyncRecv == 1);")); }); } @@ -1149,7 +1169,7 @@ pub mod tests { "#, )); assert_eq!(dispatch_count.load(Ordering::Relaxed), 2); - assert_eq!(Ok(()), poll_until_ready(&mut isolate, 3)); + poll_until_ready(&mut isolate, 3).unwrap(); js_check(isolate.execute("check.js", "assert(asyncRecv == 2);")); }); } @@ -1164,7 +1184,7 @@ pub mod tests { include_str!("shared_queue_test.js"), ), ); - assert_eq!(Ok(Async::Ready(())), isolate.poll()); + assert_eq!(Async::Ready(()), isolate.poll().unwrap()); }); } diff --git a/core/js_errors.rs b/core/js_errors.rs index ca5cf8085..08cb00a72 100644 --- a/core/js_errors.rs +++ b/core/js_errors.rs @@ -9,8 +9,10 @@ // console.log(err.stack); // It would require calling into Rust from Error.prototype.prepareStackTrace. +use crate::any_error::ErrBox; use serde_json; use serde_json::value::Value; +use std::error::Error; use std::fmt; use std::str; @@ -26,7 +28,7 @@ pub struct StackFrame { } #[derive(Debug, PartialEq, Clone)] -pub struct JSError { +pub struct V8Exception { pub message: String, pub source_line: Option<String>, @@ -41,77 +43,8 @@ pub struct JSError { pub frames: Vec<StackFrame>, } -impl std::error::Error for JSError { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - None - } -} - -impl fmt::Display for StackFrame { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // Note when we print to string, we change from 0-indexed to 1-indexed. - let function_name = self.function_name.clone(); - let script_line_column = - format_script_line_column(&self.script_name, self.line, self.column); - - if !self.function_name.is_empty() { - write!(f, " at {} ({})", function_name, script_line_column) - } else if self.is_eval { - write!(f, " at eval ({})", script_line_column) - } else { - write!(f, " at {}", script_line_column) - } - } -} - -fn format_script_line_column( - script_name: &str, - line: i64, - column: i64, -) -> String { - // TODO match this style with how typescript displays errors. - let line = (1 + line).to_string(); - let column = (1 + column).to_string(); - let script_name = script_name.to_string(); - format!("{}:{}:{}", script_name, line, column) -} - -impl fmt::Display for JSError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if self.script_resource_name.is_some() { - let script_resource_name = self.script_resource_name.as_ref().unwrap(); - if self.line_number.is_some() && self.start_column.is_some() { - assert!(self.line_number.is_some()); - assert!(self.start_column.is_some()); - let script_line_column = format_script_line_column( - script_resource_name, - self.line_number.unwrap() - 1, - self.start_column.unwrap() - 1, - ); - write!(f, "{}", script_line_column)?; - } - if self.source_line.is_some() { - write!(f, "\n{}\n", self.source_line.as_ref().unwrap())?; - let mut s = String::new(); - for i in 0..self.end_column.unwrap() { - if i >= self.start_column.unwrap() { - s.push('^'); - } else { - s.push(' '); - } - } - writeln!(f, "{}", s)?; - } - } - - write!(f, "{}", self.message.clone())?; - - for frame in &self.frames { - write!(f, "\n{}", &frame.to_string())?; - } - Ok(()) - } -} +#[derive(Debug, PartialEq, Clone)] +pub struct CoreJSError(V8Exception); impl StackFrame { // TODO Maybe use serde_derive? @@ -186,9 +119,9 @@ impl StackFrame { } } -impl JSError { - /// Creates a new JSError by parsing the raw exception JSON string from V8. - pub fn from_v8_exception(json_str: &str) -> Option<Self> { +impl V8Exception { + /// Creates a new V8Exception by parsing the raw exception JSON string from V8. + pub fn from_json(json_str: &str) -> Option<Self> { let v = serde_json::from_str::<serde_json::Value>(json_str); if v.is_err() { return None; @@ -236,7 +169,7 @@ impl JSError { } } - Some(JSError { + Some(V8Exception { message, source_line, script_resource_name, @@ -251,12 +184,79 @@ impl JSError { } } +impl CoreJSError { + pub fn from_v8_exception(v8_exception: V8Exception) -> ErrBox { + let error = Self(v8_exception); + ErrBox::from(error) + } +} + +fn format_source_loc(script_name: &str, line: i64, column: i64) -> String { + // TODO match this style with how typescript displays errors. + let line = line + 1; + let column = column + 1; + format!("{}:{}:{}", script_name, line, column) +} + +fn format_stack_frame(frame: &StackFrame) -> String { + // Note when we print to string, we change from 0-indexed to 1-indexed. + let source_loc = + format_source_loc(&frame.script_name, frame.line, frame.column); + + if !frame.function_name.is_empty() { + format!(" at {} ({})", frame.function_name, source_loc) + } else if frame.is_eval { + format!(" at eval ({})", source_loc) + } else { + format!(" at {}", source_loc) + } +} + +impl fmt::Display for CoreJSError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self.0.script_resource_name.is_some() { + let script_resource_name = self.0.script_resource_name.as_ref().unwrap(); + if self.0.line_number.is_some() && self.0.start_column.is_some() { + assert!(self.0.line_number.is_some()); + assert!(self.0.start_column.is_some()); + let source_loc = format_source_loc( + script_resource_name, + self.0.line_number.unwrap() - 1, + self.0.start_column.unwrap() - 1, + ); + write!(f, "{}", source_loc)?; + } + if self.0.source_line.is_some() { + write!(f, "\n{}\n", self.0.source_line.as_ref().unwrap())?; + let mut s = String::new(); + for i in 0..self.0.end_column.unwrap() { + if i >= self.0.start_column.unwrap() { + s.push('^'); + } else { + s.push(' '); + } + } + writeln!(f, "{}", s)?; + } + } + + write!(f, "{}", self.0.message)?; + + for frame in &self.0.frames { + write!(f, "\n{}", format_stack_frame(frame))?; + } + Ok(()) + } +} + +impl Error for CoreJSError {} + #[cfg(test)] mod tests { use super::*; - fn error1() -> JSError { - JSError { + fn error1() -> V8Exception { + V8Exception { message: "Error: foo bar".to_string(), source_line: None, script_resource_name: None, @@ -344,8 +344,8 @@ mod tests { } #[test] - fn js_error_from_v8_exception() { - let r = JSError::from_v8_exception( + fn v8_exception_from_json() { + let r = V8Exception::from_json( r#"{ "message":"Uncaught Error: bad", "frames":[ @@ -387,8 +387,8 @@ mod tests { } #[test] - fn js_error_from_v8_exception2() { - let r = JSError::from_v8_exception( + fn v8_exception_from_json_2() { + let r = V8Exception::from_json( "{\"message\":\"Error: boo\",\"sourceLine\":\"throw Error('boo');\",\"scriptResourceName\":\"a.js\",\"lineNumber\":3,\"startPosition\":8,\"endPosition\":9,\"errorLevel\":8,\"startColumn\":6,\"endColumn\":7,\"isSharedCrossOrigin\":false,\"isOpaque\":false,\"frames\":[{\"line\":3,\"column\":7,\"functionName\":\"\",\"scriptName\":\"a.js\",\"isEval\":false,\"isConstructor\":false,\"isWasm\":false}]}" ); assert!(r.is_some()); @@ -406,15 +406,8 @@ mod tests { } #[test] - fn stack_frame_to_string() { - let e = error1(); - assert_eq!(" at foo (foo_bar.ts:5:17)", &e.frames[0].to_string()); - assert_eq!(" at qat (bar_baz.ts:6:21)", &e.frames[1].to_string()); - } - - #[test] fn js_error_to_string() { - let e = error1(); + let e = CoreJSError(error1()); let expected = "Error: foo bar\n at foo (foo_bar.ts:5:17)\n at qat (bar_baz.ts:6:21)\n at deno_main.js:2:2"; assert_eq!(expected, &e.to_string()); } diff --git a/core/lib.rs b/core/lib.rs index 5bbe2fb86..61521aecb 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -4,6 +4,7 @@ extern crate log; extern crate futures; extern crate libc; +mod any_error; mod flags; mod isolate; mod js_errors; @@ -12,6 +13,7 @@ mod module_specifier; mod modules; mod shared_queue; +pub use crate::any_error::*; pub use crate::flags::v8_set_flags; pub use crate::isolate::*; pub use crate::js_errors::*; diff --git a/core/modules.rs b/core/modules.rs index c1fa5d733..4b0d128f2 100644 --- a/core/modules.rs +++ b/core/modules.rs @@ -6,8 +6,8 @@ // small and simple for users who do not use modules or if they do can load them // synchronously. The isolate.rs module should never depend on this module. +use crate::any_error::ErrBox; use crate::isolate::Isolate; -use crate::js_errors::JSError; use crate::libdeno::deno_mod; use crate::module_specifier::ModuleSpecifier; use futures::Async; @@ -15,7 +15,6 @@ use futures::Future; use futures::Poll; use std::collections::HashMap; use std::collections::HashSet; -use std::error::Error; use std::fmt; use std::marker::PhantomData; use std::sync::Arc; @@ -34,12 +33,10 @@ pub struct SourceCodeInfo { pub code: String, } -pub type SourceCodeInfoFuture<E> = - dyn Future<Item = SourceCodeInfo, Error = E> + Send; +pub type SourceCodeInfoFuture = + dyn Future<Item = SourceCodeInfo, Error = ErrBox> + Send; pub trait Loader: Send + Sync { - type Error: std::error::Error + 'static; - /// Returns an absolute URL. /// When implementing an spec-complaint VM, this should be exactly the /// algorithm described here: @@ -49,19 +46,19 @@ pub trait Loader: Send + Sync { specifier: &str, referrer: &str, is_root: bool, - ) -> Result<ModuleSpecifier, Self::Error>; + ) -> Result<ModuleSpecifier, ErrBox>; /// Given ModuleSpecifier, load its source code. fn load( &self, module_specifier: &ModuleSpecifier, - ) -> Box<SourceCodeInfoFuture<Self::Error>>; + ) -> Box<SourceCodeInfoFuture>; } -struct PendingLoad<E: Error> { +struct PendingLoad { url: String, is_root: bool, - source_code_info_future: Box<SourceCodeInfoFuture<E>>, + source_code_info_future: Box<SourceCodeInfoFuture>, } /// This future is used to implement parallel async module loading without @@ -71,7 +68,7 @@ pub struct RecursiveLoad<L: Loader> { loader: L, isolate: Arc<Mutex<Isolate>>, modules: Arc<Mutex<Modules>>, - pending: Vec<PendingLoad<L::Error>>, + pending: Vec<PendingLoad>, is_pending: HashSet<String>, phantom: PhantomData<L>, // TODO(ry) The following can all be combined into a single enum State type. @@ -106,7 +103,7 @@ impl<L: Loader> RecursiveLoad<L> { specifier: &str, referrer: &str, parent_id: Option<deno_mod>, - ) -> Result<String, L::Error> { + ) -> Result<String, ErrBox> { let is_root = parent_id.is_none(); let module_specifier = self.loader.resolve(specifier, referrer, is_root)?; let module_name = module_specifier.to_string(); @@ -142,22 +139,16 @@ impl<L: Loader> RecursiveLoad<L> { } } -#[derive(Debug, PartialEq)] -pub enum JSErrorOr<E> { - JSError(JSError), - Other(E), -} - impl<L: Loader> Future for RecursiveLoad<L> { type Item = deno_mod; - type Error = JSErrorOr<L::Error>; + type Error = ErrBox; fn poll(&mut self) -> Poll<Self::Item, Self::Error> { if self.root.is_none() && self.root_specifier.is_some() { let s = self.root_specifier.take().unwrap(); match self.add(&s, ".", None) { Err(err) => { - return Err(JSErrorOr::Other(err)); + return Err(err); } Ok(root) => { self.root = Some(root); @@ -172,7 +163,7 @@ impl<L: Loader> Future for RecursiveLoad<L> { let pending = &mut self.pending[i]; match pending.source_code_info_future.poll() { Err(err) => { - return Err(JSErrorOr::Other(err)); + return Err(err); } Ok(Async::NotReady) => { i += 1; @@ -200,18 +191,15 @@ impl<L: Loader> Future for RecursiveLoad<L> { if !is_module_registered { let module_name = &source_code_info.module_name; - let result = { + let mod_id = { let isolate = self.isolate.lock().unwrap(); isolate.mod_new( completed.is_root, module_name, &source_code_info.code, ) - }; - if let Err(err) = result { - return Err(JSErrorOr::JSError(err)); - } - let mod_id = result.unwrap(); + }?; + if completed.is_root { assert!(self.root_id.is_none()); self.root_id = Some(mod_id); @@ -235,9 +223,7 @@ impl<L: Loader> Future for RecursiveLoad<L> { }; let referrer = module_name; for specifier in imports { - self - .add(&specifier, referrer, Some(mod_id)) - .map_err(JSErrorOr::Other)?; + self.add(&specifier, referrer, Some(mod_id))?; } } else if need_alias { let mut modules = self.modules.lock().unwrap(); @@ -252,31 +238,26 @@ impl<L: Loader> Future for RecursiveLoad<L> { } let root_id = self.root_id.unwrap(); - let result = { - let mut resolve_cb = - |specifier: &str, referrer_id: deno_mod| -> deno_mod { - let modules = self.modules.lock().unwrap(); - let referrer = modules.get_name(referrer_id).unwrap(); - // this callback is only called for non-root modules - match self.loader.resolve(specifier, &referrer, false) { - Ok(specifier) => match modules.get_id(&specifier.to_string()) { - Some(id) => id, - None => 0, - }, - // We should have already resolved and loaded this module, so - // resolve() will not fail this time. - Err(_err) => unreachable!(), - } - }; - let mut isolate = self.isolate.lock().unwrap(); - isolate.mod_instantiate(root_id, &mut resolve_cb) + let mut resolve_cb = |specifier: &str, referrer_id: deno_mod| -> deno_mod { + let modules = self.modules.lock().unwrap(); + let referrer = modules.get_name(referrer_id).unwrap(); + // this callback is only called for non-root modules + match self.loader.resolve(specifier, &referrer, false) { + Ok(specifier) => match modules.get_id(&specifier.to_string()) { + Some(id) => id, + None => 0, + }, + // We should have already resolved and loaded this module, so + // resolve() will not fail this time. + Err(_err) => unreachable!(), + } }; - match result { - Err(err) => Err(JSErrorOr::JSError(err)), - Ok(()) => Ok(Async::Ready(root_id)), - } + let mut isolate = self.isolate.lock().unwrap(); + isolate + .mod_instantiate(root_id, &mut resolve_cb) + .map(|_| Async::Ready(root_id)) } } @@ -537,6 +518,7 @@ mod tests { use super::*; use crate::isolate::js_check; use crate::isolate::tests::*; + use std::error::Error; use std::fmt; struct MockLoader { @@ -607,9 +589,9 @@ mod tests { impl Future for DelayedSourceCodeFuture { type Item = SourceCodeInfo; - type Error = MockError; + type Error = ErrBox; - fn poll(&mut self) -> Poll<Self::Item, Self::Error> { + fn poll(&mut self) -> Poll<Self::Item, ErrBox> { self.counter += 1; if self.url == "file:///never_ready.js" || (self.url == "file:///slow.js" && self.counter < 2) @@ -621,20 +603,18 @@ mod tests { code: src.0.to_owned(), module_name: src.1.to_owned(), })), - None => Err(MockError::LoadErr), + None => Err(MockError::LoadErr.into()), } } } impl Loader for MockLoader { - type Error = MockError; - fn resolve( &self, specifier: &str, referrer: &str, _is_root: bool, - ) -> Result<ModuleSpecifier, Self::Error> { + ) -> Result<ModuleSpecifier, ErrBox> { let referrer = if referrer == "." { "file:///" } else { @@ -646,20 +626,20 @@ mod tests { let output_specifier = match ModuleSpecifier::resolve_import(specifier, referrer) { Ok(specifier) => specifier, - Err(..) => return Err(MockError::ResolveErr), + Err(..) => return Err(MockError::ResolveErr.into()), }; if mock_source_code(&output_specifier.to_string()).is_some() { Ok(output_specifier) } else { - Err(MockError::ResolveErr) + Err(MockError::ResolveErr.into()) } } fn load( &self, module_specifier: &ModuleSpecifier, - ) -> Box<SourceCodeInfoFuture<Self::Error>> { + ) -> Box<SourceCodeInfoFuture> { let mut loads = self.loads.lock().unwrap(); loads.push(module_specifier.to_string()); let url = module_specifier.to_string(); @@ -962,8 +942,11 @@ mod tests { RecursiveLoad::new("/bad_import.js", loader, isolate, modules); let result = recursive_load.poll(); assert!(result.is_err()); - let either_err = result.err().unwrap(); - assert_eq!(either_err, JSErrorOr::Other(MockError::ResolveErr)); + let err = result.err().unwrap(); + assert_eq!( + err.downcast_ref::<MockError>().unwrap(), + &MockError::ResolveErr + ); } #[test] |