summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/any_error.rs68
-rw-r--r--core/examples/http_bench.rs2
-rw-r--r--core/isolate.rs148
-rw-r--r--core/js_errors.rs173
-rw-r--r--core/lib.rs2
-rw-r--r--core/modules.rs109
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]