1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-28 16:20:57 -05:00

Refactor error to use dynamic dispatch and traits

This is in preperation for dynamic import (#1789), which is more easily
implemented when errors are dynamic.
This commit is contained in:
Bert Belder 2019-07-11 00:53:48 +02:00 committed by Ryan Dahl
parent db5c66a638
commit abe8a113ad
28 changed files with 926 additions and 1082 deletions

View file

@ -1,6 +1,4 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
use crate::deno_error::err_check;
use crate::deno_error::DenoError;
use crate::diagnostics::Diagnostic; use crate::diagnostics::Diagnostic;
use crate::msg; use crate::msg;
use crate::resources; use crate::resources;
@ -9,6 +7,7 @@ use crate::state::*;
use crate::tokio_util; use crate::tokio_util;
use crate::worker::Worker; use crate::worker::Worker;
use deno::Buf; use deno::Buf;
use deno::ErrBox;
use deno::ModuleSpecifier; use deno::ModuleSpecifier;
use futures::Future; use futures::Future;
use futures::Stream; use futures::Stream;
@ -96,7 +95,7 @@ pub fn bundle_async(
state: ThreadSafeState, state: ThreadSafeState,
module_name: String, module_name: String,
out_file: String, out_file: String,
) -> impl Future<Item = (), Error = DenoError> { ) -> impl Future<Item = (), Error = ErrBox> {
debug!( debug!(
"Invoking the compiler to bundle. module_name: {}", "Invoking the compiler to bundle. module_name: {}",
module_name module_name
@ -116,9 +115,9 @@ pub fn bundle_async(
// as was done previously. // as was done previously.
state.clone(), state.clone(),
); );
err_check(worker.execute("denoMain()")); worker.execute("denoMain()").unwrap();
err_check(worker.execute("workerMain()")); worker.execute("workerMain()").unwrap();
err_check(worker.execute("compilerMain()")); worker.execute("compilerMain()").unwrap();
let resource = worker.state.resource.clone(); let resource = worker.state.resource.clone();
let compiler_rid = resource.rid; let compiler_rid = resource.rid;
@ -144,7 +143,7 @@ pub fn bundle_async(
let json_str = std::str::from_utf8(&msg).unwrap(); let json_str = std::str::from_utf8(&msg).unwrap();
debug!("Message: {}", json_str); debug!("Message: {}", json_str);
if let Some(diagnostics) = Diagnostic::from_emit_result(json_str) { if let Some(diagnostics) = Diagnostic::from_emit_result(json_str) {
return Err(DenoError::from(diagnostics)); return Err(ErrBox::from(diagnostics));
} }
} }
@ -156,7 +155,7 @@ pub fn bundle_async(
pub fn compile_async( pub fn compile_async(
state: ThreadSafeState, state: ThreadSafeState,
module_meta_data: &ModuleMetaData, module_meta_data: &ModuleMetaData,
) -> impl Future<Item = ModuleMetaData, Error = DenoError> { ) -> impl Future<Item = ModuleMetaData, Error = ErrBox> {
let module_name = module_meta_data.module_name.clone(); let module_name = module_meta_data.module_name.clone();
debug!( debug!(
@ -178,9 +177,9 @@ pub fn compile_async(
// as was done previously. // as was done previously.
state.clone(), state.clone(),
); );
err_check(worker.execute("denoMain()")); worker.execute("denoMain()").unwrap();
err_check(worker.execute("workerMain()")); worker.execute("workerMain()").unwrap();
err_check(worker.execute("compilerMain()")); worker.execute("compilerMain()").unwrap();
let compiling_job = state.progress.add("Compile", &module_name); let compiling_job = state.progress.add("Compile", &module_name);
@ -211,7 +210,7 @@ pub fn compile_async(
let json_str = std::str::from_utf8(&msg).unwrap(); let json_str = std::str::from_utf8(&msg).unwrap();
debug!("Message: {}", json_str); debug!("Message: {}", json_str);
if let Some(diagnostics) = Diagnostic::from_emit_result(json_str) { if let Some(diagnostics) = Diagnostic::from_emit_result(json_str) {
return Err(DenoError::from(diagnostics)); return Err(ErrBox::from(diagnostics));
} }
} }
@ -242,7 +241,7 @@ pub fn compile_async(
pub fn compile_sync( pub fn compile_sync(
state: ThreadSafeState, state: ThreadSafeState,
module_meta_data: &ModuleMetaData, module_meta_data: &ModuleMetaData,
) -> Result<ModuleMetaData, DenoError> { ) -> Result<ModuleMetaData, ErrBox> {
tokio_util::block_on(compile_async(state, module_meta_data)) tokio_util::block_on(compile_async(state, module_meta_data))
} }

View file

@ -1,9 +1,8 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
use crate::compiler::ModuleMetaData; use crate::compiler::ModuleMetaData;
use crate::deno_error;
use crate::deno_error::DenoError; use crate::deno_error::DenoError;
use crate::deno_error::DenoResult;
use crate::deno_error::ErrorKind; use crate::deno_error::ErrorKind;
use crate::deno_error::GetErrorKind;
use crate::fs as deno_fs; use crate::fs as deno_fs;
use crate::http_util; use crate::http_util;
use crate::msg; use crate::msg;
@ -11,6 +10,7 @@ use crate::progress::Progress;
use crate::source_maps::SourceMapGetter; use crate::source_maps::SourceMapGetter;
use crate::tokio_util; use crate::tokio_util;
use crate::version; use crate::version;
use deno::ErrBox;
use deno::ModuleSpecifier; use deno::ModuleSpecifier;
use dirs; use dirs;
use futures::future::{loop_fn, Either, Loop}; use futures::future::{loop_fn, Either, Loop};
@ -20,8 +20,6 @@ use ring;
use serde_json; use serde_json;
use std; use std;
use std::collections::HashSet; use std::collections::HashSet;
use std::fmt;
use std::fmt::Display;
use std::fmt::Write; use std::fmt::Write;
use std::fs; use std::fs;
use std::path::Path; use std::path::Path;
@ -34,36 +32,6 @@ use std::sync::Mutex;
use url; use url;
use url::Url; use url::Url;
// TODO: DenoDirError is temporary solution, should be upgraded during rewrite
#[derive(Debug, PartialEq)]
pub enum DenoDirErrorKind {
UnsupportedFetchScheme,
}
#[derive(Debug)]
pub struct DenoDirError {
pub message: String,
pub kind: DenoDirErrorKind,
}
impl DenoDirError {
pub fn new(message: String, kind: DenoDirErrorKind) -> Self {
DenoDirError { message, kind }
}
}
impl std::error::Error for DenoDirError {
fn description(&self) -> &str {
&*self.message
}
}
impl Display for DenoDirError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "{}", self.message)
}
}
const SUPPORTED_URL_SCHEMES: [&str; 3] = ["http", "https", "file"]; const SUPPORTED_URL_SCHEMES: [&str; 3] = ["http", "https", "file"];
fn normalize_path(path: &Path) -> PathBuf { fn normalize_path(path: &Path) -> PathBuf {
@ -198,13 +166,13 @@ impl DenoDir {
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
use_cache: bool, use_cache: bool,
no_fetch: bool, no_fetch: bool,
) -> impl Future<Item = ModuleMetaData, Error = deno_error::DenoError> { ) -> impl Future<Item = ModuleMetaData, Error = ErrBox> {
let module_url = specifier.as_url().to_owned(); let module_url = specifier.as_url().to_owned();
debug!("fetch_module_meta_data. specifier {} ", &module_url); debug!("fetch_module_meta_data. specifier {} ", &module_url);
let result = self.url_to_deps_path(&module_url); let result = self.url_to_deps_path(&module_url);
if let Err(err) = result { if let Err(err) = result {
return Either::A(futures::future::err(DenoError::from(err))); return Either::A(futures::future::err(err));
} }
let deps_filepath = result.unwrap(); let deps_filepath = result.unwrap();
@ -223,20 +191,17 @@ impl DenoDir {
use_cache, use_cache,
no_fetch, no_fetch,
).then(move |result| { ).then(move |result| {
let mut out = match result { let mut out = result.map_err(|err| {
Ok(out) => out,
Err(err) => {
if err.kind() == ErrorKind::NotFound { if err.kind() == ErrorKind::NotFound {
// For NotFound, change the message to something better. // For NotFound, change the message to something better.
return Err(deno_error::new( DenoError::new(
ErrorKind::NotFound, ErrorKind::NotFound,
format!("Cannot resolve module \"{}\"", module_url.to_string()), format!("Cannot resolve module \"{}\"", module_url.to_string()),
)); ).into()
} else { } else {
return Err(err); err
} }
} })?;
};
if out.source_code.starts_with(b"#!") { if out.source_code.starts_with(b"#!") {
out.source_code = filter_shebang(out.source_code); out.source_code = filter_shebang(out.source_code);
@ -290,7 +255,7 @@ impl DenoDir {
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
use_cache: bool, use_cache: bool,
no_fetch: bool, no_fetch: bool,
) -> Result<ModuleMetaData, deno_error::DenoError> { ) -> Result<ModuleMetaData, ErrBox> {
tokio_util::block_on( tokio_util::block_on(
self.fetch_module_meta_data_async(specifier, use_cache, no_fetch), self.fetch_module_meta_data_async(specifier, use_cache, no_fetch),
) )
@ -303,20 +268,17 @@ impl DenoDir {
/// ///
/// For specifier starting with `http://` and `https://` it returns /// For specifier starting with `http://` and `https://` it returns
/// path to DenoDir dependency directory. /// path to DenoDir dependency directory.
pub fn url_to_deps_path( pub fn url_to_deps_path(self: &Self, url: &Url) -> Result<PathBuf, ErrBox> {
self: &Self,
url: &Url,
) -> Result<PathBuf, DenoDirError> {
let filename = match url.scheme() { let filename = match url.scheme() {
"file" => url.to_file_path().unwrap(), "file" => url.to_file_path().unwrap(),
"https" => get_cache_filename(self.deps_https.as_path(), &url), "https" => get_cache_filename(self.deps_https.as_path(), &url),
"http" => get_cache_filename(self.deps_http.as_path(), &url), "http" => get_cache_filename(self.deps_http.as_path(), &url),
scheme => { scheme => {
return Err( return Err(
DenoDirError::new( DenoError::new(
ErrorKind::UnsupportedFetchScheme,
format!("Unsupported scheme \"{}\" for module \"{}\". Supported schemes: {:#?}", scheme, url, SUPPORTED_URL_SCHEMES), format!("Unsupported scheme \"{}\" for module \"{}\". Supported schemes: {:#?}", scheme, url, SUPPORTED_URL_SCHEMES),
DenoDirErrorKind::UnsupportedFetchScheme ).into()
)
); );
} }
}; };
@ -389,7 +351,7 @@ fn get_source_code_async(
filepath: PathBuf, filepath: PathBuf,
use_cache: bool, use_cache: bool,
no_fetch: bool, no_fetch: bool,
) -> impl Future<Item = ModuleMetaData, Error = DenoError> { ) -> impl Future<Item = ModuleMetaData, Error = ErrBox> {
let filename = filepath.to_str().unwrap().to_string(); let filename = filepath.to_str().unwrap().to_string();
let module_name = module_url.to_string(); let module_name = module_url.to_string();
let url_scheme = module_url.scheme(); let url_scheme = module_url.scheme();
@ -425,23 +387,23 @@ fn get_source_code_async(
// If not remote file stop here! // If not remote file stop here!
if !is_module_remote { if !is_module_remote {
debug!("not remote file stop here"); debug!("not remote file stop here");
return Either::A(futures::future::err(DenoError::from( return Either::A(futures::future::err(
std::io::Error::new( std::io::Error::new(
std::io::ErrorKind::NotFound, std::io::ErrorKind::NotFound,
format!("cannot find local file '{}'", filename), format!("cannot find local file '{}'", filename),
), ).into(),
))); ));
} }
// If remote downloads are not allowed stop here! // If remote downloads are not allowed stop here!
if no_fetch { if no_fetch {
debug!("remote file with no_fetch stop here"); debug!("remote file with no_fetch stop here");
return Either::A(futures::future::err(DenoError::from( return Either::A(futures::future::err(
std::io::Error::new( std::io::Error::new(
std::io::ErrorKind::NotFound, std::io::ErrorKind::NotFound,
format!("cannot find remote file '{}' in cache", filename), format!("cannot find remote file '{}' in cache", filename),
), ).into(),
))); ));
} }
debug!("is remote but didn't find module"); debug!("is remote but didn't find module");
@ -456,10 +418,12 @@ fn get_source_code_async(
download_cache.mark(&module_name); download_cache.mark(&module_name);
Ok(output) Ok(output)
} }
None => Err(DenoError::from(std::io::Error::new( None => Err(
std::io::Error::new(
std::io::ErrorKind::NotFound, std::io::ErrorKind::NotFound,
format!("cannot find remote file '{}'", filename), format!("cannot find remote file '{}'", filename),
))), ).into(),
),
}, },
), ),
) )
@ -474,7 +438,7 @@ fn get_source_code(
filepath: PathBuf, filepath: PathBuf,
use_cache: bool, use_cache: bool,
no_fetch: bool, no_fetch: bool,
) -> DenoResult<ModuleMetaData> { ) -> Result<ModuleMetaData, ErrBox> {
tokio_util::block_on(get_source_code_async( tokio_util::block_on(get_source_code_async(
deno_dir, module_url, filepath, use_cache, no_fetch, deno_dir, module_url, filepath, use_cache, no_fetch,
)) ))
@ -595,7 +559,7 @@ fn save_module_code_and_headers(
source: &str, source: &str,
maybe_content_type: Option<String>, maybe_content_type: Option<String>,
maybe_initial_filepath: Option<PathBuf>, maybe_initial_filepath: Option<PathBuf>,
) -> DenoResult<()> { ) -> Result<(), ErrBox> {
match filepath.parent() { match filepath.parent() {
Some(ref parent) => fs::create_dir_all(parent), Some(ref parent) => fs::create_dir_all(parent),
None => Ok(()), None => Ok(()),
@ -636,7 +600,7 @@ fn fetch_remote_source_async(
deno_dir: &DenoDir, deno_dir: &DenoDir,
module_url: &Url, module_url: &Url,
filepath: &Path, filepath: &Path,
) -> impl Future<Item = Option<ModuleMetaData>, Error = DenoError> { ) -> impl Future<Item = Option<ModuleMetaData>, Error = ErrBox> {
use crate::http_util::FetchOnceResult; use crate::http_util::FetchOnceResult;
let download_job = deno_dir.progress.add("Download", &module_url.to_string()); let download_job = deno_dir.progress.add("Download", &module_url.to_string());
@ -664,11 +628,13 @@ fn fetch_remote_source_async(
)| { )| {
let module_uri = url_into_uri(&module_url); let module_uri = url_into_uri(&module_url);
// Single pass fetch, either yields code or yields redirect. // Single pass fetch, either yields code or yields redirect.
http_util::fetch_string_once(module_uri).and_then(move |fetch_once_result| { http_util::fetch_string_once(module_uri).and_then(
move |fetch_once_result| {
match fetch_once_result { match fetch_once_result {
FetchOnceResult::Redirect(uri) => { FetchOnceResult::Redirect(uri) => {
// If redirects, update module_name and filename for next looped call. // If redirects, update module_name and filename for next looped call.
let new_module_url = Url::parse(&uri.to_string()).expect("http::uri::Uri should be parseable as Url"); let new_module_url = Url::parse(&uri.to_string())
.expect("http::uri::Uri should be parseable as Url");
let new_filepath = dir.url_to_deps_path(&new_module_url)?; let new_filepath = dir.url_to_deps_path(&new_module_url)?;
if maybe_initial_module_name.is_none() { if maybe_initial_module_name.is_none() {
@ -716,9 +682,11 @@ fn fetch_remote_source_async(
Ok(Loop::Break(Some(module_meta_data))) Ok(Loop::Break(Some(module_meta_data)))
} }
} }
})
}, },
).then(move |r| { )
},
)
.then(move |r| {
// Explicit drop to keep reference alive until future completes. // Explicit drop to keep reference alive until future completes.
drop(download_job); drop(download_job);
r r
@ -731,7 +699,7 @@ fn fetch_remote_source(
deno_dir: &DenoDir, deno_dir: &DenoDir,
module_url: &Url, module_url: &Url,
filepath: &Path, filepath: &Path,
) -> DenoResult<Option<ModuleMetaData>> { ) -> Result<Option<ModuleMetaData>, ErrBox> {
tokio_util::block_on(fetch_remote_source_async( tokio_util::block_on(fetch_remote_source_async(
deno_dir, module_url, filepath, deno_dir, module_url, filepath,
)) ))
@ -751,7 +719,7 @@ fn fetch_local_source(
module_url: &Url, module_url: &Url,
filepath: &Path, filepath: &Path,
module_initial_source_name: Option<String>, module_initial_source_name: Option<String>,
) -> DenoResult<Option<ModuleMetaData>> { ) -> Result<Option<ModuleMetaData>, ErrBox> {
let source_code_headers = get_source_code_headers(&filepath); let source_code_headers = get_source_code_headers(&filepath);
// If source code headers says that it would redirect elsewhere, // If source code headers says that it would redirect elsewhere,
// (meaning that the source file might not exist; only .headers.json is present) // (meaning that the source file might not exist; only .headers.json is present)
@ -907,7 +875,7 @@ fn save_source_code_headers(
// TODO(bartlomieju): this method should be moved, it doesn't belong to deno_dir.rs // TODO(bartlomieju): this method should be moved, it doesn't belong to deno_dir.rs
// it's a general utility // it's a general utility
pub fn resolve_from_cwd(path: &str) -> Result<(PathBuf, String), DenoError> { pub fn resolve_from_cwd(path: &str) -> Result<(PathBuf, String), ErrBox> {
let candidate_path = Path::new(path); let candidate_path = Path::new(path);
let resolved_path = if candidate_path.is_absolute() { let resolved_path = if candidate_path.is_absolute() {
@ -1669,8 +1637,8 @@ mod tests {
for &test in test_cases.iter() { for &test in test_cases.iter() {
let url = Url::parse(test).unwrap(); let url = Url::parse(test).unwrap();
assert_eq!( assert_eq!(
deno_dir.url_to_deps_path(&url).unwrap_err().kind, deno_dir.url_to_deps_path(&url).unwrap_err().kind(),
DenoDirErrorKind::UnsupportedFetchScheme ErrorKind::UnsupportedFetchScheme
); );
} }
} }

View file

@ -1,76 +1,128 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
use crate::deno_dir; use crate::diagnostics::Diagnostic;
use crate::diagnostics; use crate::fmt_errors::JSError;
use crate::fmt_errors::JSErrorColor; use crate::import_map::ImportMapError;
use crate::import_map;
pub use crate::msg::ErrorKind; pub use crate::msg::ErrorKind;
use crate::resolve_addr::ResolveAddrError; use deno::AnyError;
use crate::source_maps::apply_source_map; use deno::ErrBox;
use crate::source_maps::SourceMapGetter;
use deno::JSError;
use deno::ModuleResolutionError; use deno::ModuleResolutionError;
use http::uri;
use hyper; use hyper;
#[cfg(unix)]
use nix::{errno::Errno, Error as UnixError};
use std; use std;
use std::error::Error;
use std::fmt; use std::fmt;
use std::io; use std::io;
use std::str;
use url; use url;
pub type DenoResult<T> = std::result::Result<T, DenoError>;
#[derive(Debug)] #[derive(Debug)]
pub struct DenoError { pub struct DenoError {
repr: Repr, kind: ErrorKind,
} msg: String,
#[derive(Debug)]
enum Repr {
Simple(ErrorKind, String),
IoErr(io::Error),
UrlErr(url::ParseError),
HyperErr(hyper::Error),
ImportMapErr(import_map::ImportMapError),
ModuleResolutionErr(ModuleResolutionError),
Diagnostic(diagnostics::Diagnostic),
JSError(JSError),
DenoDirErr(deno_dir::DenoDirError),
}
/// Create a new simple DenoError.
pub fn new(kind: ErrorKind, msg: String) -> DenoError {
DenoError {
repr: Repr::Simple(kind, msg),
}
} }
impl DenoError { impl DenoError {
fn url_error_kind(err: url::ParseError) -> ErrorKind { pub fn new(kind: ErrorKind, msg: String) -> Self {
use url::ParseError::*; Self { kind, msg }
match err {
EmptyHost => ErrorKind::EmptyHost,
IdnaError => ErrorKind::IdnaError,
InvalidPort => ErrorKind::InvalidPort,
InvalidIpv4Address => ErrorKind::InvalidIpv4Address,
InvalidIpv6Address => ErrorKind::InvalidIpv6Address,
InvalidDomainCharacter => ErrorKind::InvalidDomainCharacter,
RelativeUrlWithoutBase => ErrorKind::RelativeUrlWithoutBase,
RelativeUrlWithCannotBeABaseBase => {
ErrorKind::RelativeUrlWithCannotBeABaseBase
}
SetHostOnCannotBeABaseUrl => ErrorKind::SetHostOnCannotBeABaseUrl,
Overflow => ErrorKind::Overflow,
}
} }
}
pub fn kind(&self) -> ErrorKind { impl Error for DenoError {}
match self.repr {
Repr::Simple(kind, ref _msg) => kind, impl fmt::Display for DenoError {
// Repr::Simple(kind) => kind, fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Repr::IoErr(ref err) => { f.pad(self.msg.as_str())
use std::io::ErrorKind::*; }
match err.kind() { }
#[derive(Debug)]
struct StaticError(ErrorKind, &'static str);
impl Error for StaticError {}
impl fmt::Display for StaticError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad(self.1)
}
}
pub fn bad_resource() -> ErrBox {
StaticError(ErrorKind::BadResource, "bad resource id").into()
}
pub fn permission_denied() -> ErrBox {
StaticError(ErrorKind::PermissionDenied, "permission denied").into()
}
pub fn op_not_implemented() -> ErrBox {
StaticError(ErrorKind::OpNotAvailable, "op not implemented").into()
}
pub fn no_buffer_specified() -> ErrBox {
StaticError(ErrorKind::InvalidInput, "no buffer specified").into()
}
pub fn no_async_support() -> ErrBox {
StaticError(ErrorKind::NoAsyncSupport, "op doesn't support async calls")
.into()
}
pub fn no_sync_support() -> ErrBox {
StaticError(ErrorKind::NoSyncSupport, "op doesn't support sync calls").into()
}
pub fn invalid_address_syntax() -> ErrBox {
StaticError(ErrorKind::InvalidInput, "invalid address syntax").into()
}
pub trait GetErrorKind {
fn kind(&self) -> ErrorKind;
}
impl GetErrorKind for DenoError {
fn kind(&self) -> ErrorKind {
self.kind
}
}
impl GetErrorKind for StaticError {
fn kind(&self) -> ErrorKind {
self.0
}
}
impl GetErrorKind for JSError {
fn kind(&self) -> ErrorKind {
ErrorKind::JSError
}
}
impl GetErrorKind for Diagnostic {
fn kind(&self) -> ErrorKind {
ErrorKind::Diagnostic
}
}
impl GetErrorKind for ImportMapError {
fn kind(&self) -> ErrorKind {
ErrorKind::ImportMapError
}
}
impl GetErrorKind for ModuleResolutionError {
fn kind(&self) -> ErrorKind {
use ModuleResolutionError::*;
match self {
InvalidUrl(ref err) | InvalidBaseUrl(ref err) => err.kind(),
InvalidPath => ErrorKind::InvalidPath,
ImportPrefixMissing => ErrorKind::ImportPrefixMissing,
}
}
}
impl GetErrorKind for io::Error {
fn kind(&self) -> ErrorKind {
use io::ErrorKind::*;
match self.kind() {
NotFound => ErrorKind::NotFound, NotFound => ErrorKind::NotFound,
PermissionDenied => ErrorKind::PermissionDenied, PermissionDenied => ErrorKind::PermissionDenied,
ConnectionRefused => ErrorKind::ConnectionRefused, ConnectionRefused => ErrorKind::ConnectionRefused,
@ -87,260 +139,101 @@ impl DenoError {
TimedOut => ErrorKind::TimedOut, TimedOut => ErrorKind::TimedOut,
Interrupted => ErrorKind::Interrupted, Interrupted => ErrorKind::Interrupted,
WriteZero => ErrorKind::WriteZero, WriteZero => ErrorKind::WriteZero,
Other => ErrorKind::Other,
UnexpectedEof => ErrorKind::UnexpectedEof, UnexpectedEof => ErrorKind::UnexpectedEof,
_ => unreachable!(), _ => ErrorKind::Other,
}
}
Repr::UrlErr(err) => Self::url_error_kind(err),
Repr::HyperErr(ref err) => {
// For some reason hyper::errors::Kind is private.
if err.is_parse() {
ErrorKind::HttpParse
} else if err.is_user() {
ErrorKind::HttpUser
} else if err.is_canceled() {
ErrorKind::HttpCanceled
} else if err.is_closed() {
ErrorKind::HttpClosed
} else {
ErrorKind::HttpOther
}
}
Repr::ImportMapErr(ref _err) => ErrorKind::ImportMapError,
Repr::ModuleResolutionErr(err) => {
use ModuleResolutionError::*;
match err {
InvalidUrl(err) | InvalidBaseUrl(err) => Self::url_error_kind(err),
InvalidPath => ErrorKind::InvalidPath,
ImportPrefixMissing => ErrorKind::ImportPrefixMissing,
}
}
Repr::Diagnostic(ref _err) => ErrorKind::Diagnostic,
Repr::JSError(ref _err) => ErrorKind::JSError,
Repr::DenoDirErr(ref _err) => ErrorKind::DenoDirError,
}
}
pub fn apply_source_map<G: SourceMapGetter>(self, getter: &G) -> Self {
if let Repr::JSError(js_error) = self.repr {
return DenoError {
repr: Repr::JSError(apply_source_map(&js_error, getter)),
};
} else {
panic!("attempt to apply source map an unremappable error")
} }
} }
} }
impl fmt::Display for DenoError { impl GetErrorKind for uri::InvalidUri {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn kind(&self) -> ErrorKind {
match self.repr { // The http::uri::ErrorKind exists and is similar to url::ParseError.
Repr::Simple(_kind, ref err_str) => f.pad(err_str), // However it is also private, so we can't get any details out.
Repr::IoErr(ref err) => err.fmt(f), ErrorKind::InvalidUri
Repr::UrlErr(ref err) => err.fmt(f), }
Repr::HyperErr(ref err) => err.fmt(f), }
Repr::ImportMapErr(ref err) => f.pad(&err.msg),
Repr::ModuleResolutionErr(ref err) => err.fmt(f), impl GetErrorKind for url::ParseError {
Repr::Diagnostic(ref err) => err.fmt(f), fn kind(&self) -> ErrorKind {
Repr::JSError(ref err) => JSErrorColor(err).fmt(f), use url::ParseError::*;
Repr::DenoDirErr(ref err) => err.fmt(f), match self {
EmptyHost => ErrorKind::EmptyHost,
IdnaError => ErrorKind::IdnaError,
InvalidDomainCharacter => ErrorKind::InvalidDomainCharacter,
InvalidIpv4Address => ErrorKind::InvalidIpv4Address,
InvalidIpv6Address => ErrorKind::InvalidIpv6Address,
InvalidPort => ErrorKind::InvalidPort,
Overflow => ErrorKind::Overflow,
RelativeUrlWithCannotBeABaseBase => {
ErrorKind::RelativeUrlWithCannotBeABaseBase
}
RelativeUrlWithoutBase => ErrorKind::RelativeUrlWithoutBase,
SetHostOnCannotBeABaseUrl => ErrorKind::SetHostOnCannotBeABaseUrl,
} }
} }
} }
impl std::error::Error for DenoError { impl GetErrorKind for hyper::Error {
fn description(&self) -> &str { fn kind(&self) -> ErrorKind {
match self.repr { match self {
Repr::Simple(_kind, ref msg) => msg.as_str(), e if e.is_canceled() => ErrorKind::HttpCanceled,
Repr::IoErr(ref err) => err.description(), e if e.is_closed() => ErrorKind::HttpClosed,
Repr::UrlErr(ref err) => err.description(), e if e.is_parse() => ErrorKind::HttpParse,
Repr::HyperErr(ref err) => err.description(), e if e.is_user() => ErrorKind::HttpUser,
Repr::ImportMapErr(ref err) => &err.msg, _ => ErrorKind::HttpOther,
Repr::ModuleResolutionErr(ref err) => err.description(),
Repr::Diagnostic(ref err) => &err.items[0].message,
Repr::JSError(ref err) => &err.description(),
Repr::DenoDirErr(ref err) => err.description(),
}
}
fn cause(&self) -> Option<&dyn std::error::Error> {
match self.repr {
Repr::Simple(_kind, ref _msg) => None,
Repr::IoErr(ref err) => Some(err),
Repr::UrlErr(ref err) => Some(err),
Repr::HyperErr(ref err) => Some(err),
Repr::ImportMapErr(ref _err) => None,
Repr::ModuleResolutionErr(ref err) => err.source(),
Repr::Diagnostic(ref _err) => None,
Repr::JSError(ref err) => Some(err),
Repr::DenoDirErr(ref err) => Some(err),
}
}
}
impl From<io::Error> for DenoError {
#[inline]
fn from(err: io::Error) -> Self {
Self {
repr: Repr::IoErr(err),
}
}
}
impl From<url::ParseError> for DenoError {
#[inline]
fn from(err: url::ParseError) -> Self {
Self {
repr: Repr::UrlErr(err),
}
}
}
impl From<hyper::Error> for DenoError {
#[inline]
fn from(err: hyper::Error) -> Self {
Self {
repr: Repr::HyperErr(err),
}
}
}
impl From<ResolveAddrError> for DenoError {
fn from(e: ResolveAddrError) -> Self {
match e {
ResolveAddrError::Syntax => Self {
repr: Repr::Simple(
ErrorKind::InvalidInput,
"invalid address syntax".to_string(),
),
},
ResolveAddrError::Resolution(io_err) => Self {
repr: Repr::IoErr(io_err),
},
} }
} }
} }
#[cfg(unix)] #[cfg(unix)]
impl From<UnixError> for DenoError { mod unix {
fn from(e: UnixError) -> Self { use super::{ErrorKind, GetErrorKind};
match e { use nix::errno::Errno::*;
UnixError::Sys(Errno::EPERM) => Self { pub use nix::Error;
repr: Repr::Simple( use nix::Error::Sys;
ErrorKind::PermissionDenied,
Errno::EPERM.desc().to_owned(), impl GetErrorKind for Error {
), fn kind(&self) -> ErrorKind {
}, match self {
UnixError::Sys(Errno::EINVAL) => Self { Sys(EPERM) => ErrorKind::PermissionDenied,
repr: Repr::Simple( Sys(EINVAL) => ErrorKind::InvalidInput,
ErrorKind::InvalidInput, Sys(ENOENT) => ErrorKind::NotFound,
Errno::EINVAL.desc().to_owned(), Sys(_) => ErrorKind::UnixError,
), _ => ErrorKind::Other,
}, }
UnixError::Sys(Errno::ENOENT) => Self {
repr: Repr::Simple(
ErrorKind::NotFound,
Errno::ENOENT.desc().to_owned(),
),
},
UnixError::Sys(err) => Self {
repr: Repr::Simple(ErrorKind::UnixError, err.desc().to_owned()),
},
_ => Self {
repr: Repr::Simple(ErrorKind::Other, format!("{}", e)),
},
} }
} }
} }
impl From<import_map::ImportMapError> for DenoError { impl GetErrorKind for dyn AnyError {
fn from(err: import_map::ImportMapError) -> Self { fn kind(&self) -> ErrorKind {
Self { use self::GetErrorKind as Get;
repr: Repr::ImportMapErr(err),
#[cfg(unix)]
fn unix_error_kind(err: &dyn AnyError) -> Option<ErrorKind> {
err.downcast_ref::<unix::Error>().map(Get::kind)
} }
#[cfg(not(unix))]
fn unix_error_kind(_: &dyn AnyError) -> Option<ErrorKind> {
None
} }
}
impl From<deno_dir::DenoDirError> for DenoError { None
fn from(err: deno_dir::DenoDirError) -> Self { .or_else(|| self.downcast_ref::<DenoError>().map(Get::kind))
Self { .or_else(|| self.downcast_ref::<Diagnostic>().map(Get::kind))
repr: Repr::DenoDirErr(err), .or_else(|| self.downcast_ref::<hyper::Error>().map(Get::kind))
} .or_else(|| self.downcast_ref::<ImportMapError>().map(Get::kind))
} .or_else(|| self.downcast_ref::<io::Error>().map(Get::kind))
} .or_else(|| self.downcast_ref::<JSError>().map(Get::kind))
.or_else(|| self.downcast_ref::<ModuleResolutionError>().map(Get::kind))
impl From<ModuleResolutionError> for DenoError { .or_else(|| self.downcast_ref::<StaticError>().map(Get::kind))
fn from(err: ModuleResolutionError) -> Self { .or_else(|| self.downcast_ref::<uri::InvalidUri>().map(Get::kind))
Self { .or_else(|| self.downcast_ref::<url::ParseError>().map(Get::kind))
repr: Repr::ModuleResolutionErr(err), .or_else(|| unix_error_kind(self))
} .unwrap_or_else(|| {
} panic!("Can't get ErrorKind for {:?}", self);
} })
impl From<diagnostics::Diagnostic> for DenoError {
fn from(diagnostic: diagnostics::Diagnostic) -> Self {
Self {
repr: Repr::Diagnostic(diagnostic),
}
}
}
impl From<JSError> for DenoError {
fn from(err: JSError) -> Self {
Self {
repr: Repr::JSError(err),
}
}
}
pub fn bad_resource() -> DenoError {
new(ErrorKind::BadResource, String::from("bad resource id"))
}
pub fn permission_denied() -> DenoError {
new(
ErrorKind::PermissionDenied,
String::from("permission denied"),
)
}
pub fn op_not_implemented() -> DenoError {
new(
ErrorKind::OpNotAvailable,
String::from("op not implemented"),
)
}
pub fn worker_init_failed() -> DenoError {
// TODO(afinch7) pass worker error data through here
new(
ErrorKind::WorkerInitFailed,
String::from("worker init failed"),
)
}
pub fn no_buffer_specified() -> DenoError {
new(ErrorKind::InvalidInput, String::from("no buffer specified"))
}
pub fn no_async_support() -> DenoError {
new(
ErrorKind::NoAsyncSupport,
String::from("op doesn't support async calls"),
)
}
pub fn no_sync_support() -> DenoError {
new(
ErrorKind::NoSyncSupport,
String::from("op doesn't support sync calls"),
)
}
pub fn err_check<R>(r: Result<R, DenoError>) {
if let Err(e) = r {
panic!(e.to_string());
} }
} }
@ -348,16 +241,15 @@ pub fn err_check<R>(r: Result<R, DenoError>) {
mod tests { mod tests {
use super::*; use super::*;
use crate::ansi::strip_ansi_codes; use crate::ansi::strip_ansi_codes;
use crate::deno_dir::DenoDirError;
use crate::deno_dir::DenoDirErrorKind;
use crate::diagnostics::Diagnostic; use crate::diagnostics::Diagnostic;
use crate::diagnostics::DiagnosticCategory; use crate::diagnostics::DiagnosticCategory;
use crate::diagnostics::DiagnosticItem; use crate::diagnostics::DiagnosticItem;
use crate::import_map::ImportMapError; use deno::ErrBox;
use deno::StackFrame; use deno::StackFrame;
use deno::V8Exception;
fn js_error() -> JSError { fn js_error() -> JSError {
JSError { JSError::new(V8Exception {
message: "Error: foo bar".to_string(), message: "Error: foo bar".to_string(),
source_line: None, source_line: None,
script_resource_name: None, script_resource_name: None,
@ -396,7 +288,7 @@ mod tests {
is_wasm: false, is_wasm: false,
}, },
], ],
} })
} }
fn diagnostic() -> Diagnostic { fn diagnostic() -> Diagnostic {
@ -436,22 +328,6 @@ mod tests {
} }
} }
struct MockSourceMapGetter {}
impl SourceMapGetter for MockSourceMapGetter {
fn get_source_map(&self, _script_name: &str) -> Option<Vec<u8>> {
Some(vec![])
}
fn get_source_line(
&self,
_script_name: &str,
_line: usize,
) -> Option<String> {
None
}
}
fn io_error() -> io::Error { fn io_error() -> io::Error {
io::Error::from(io::ErrorKind::NotFound) io::Error::from(io::ErrorKind::NotFound)
} }
@ -466,30 +342,24 @@ mod tests {
} }
} }
fn deno_dir_error() -> DenoDirError {
DenoDirError::new(
"a deno dir error".to_string(),
DenoDirErrorKind::UnsupportedFetchScheme,
)
}
#[test] #[test]
fn test_simple_error() { fn test_simple_error() {
let err = new(ErrorKind::NoError, "foo".to_string()); let err =
ErrBox::from(DenoError::new(ErrorKind::NoError, "foo".to_string()));
assert_eq!(err.kind(), ErrorKind::NoError); assert_eq!(err.kind(), ErrorKind::NoError);
assert_eq!(err.to_string(), "foo"); assert_eq!(err.to_string(), "foo");
} }
#[test] #[test]
fn test_io_error() { fn test_io_error() {
let err = DenoError::from(io_error()); let err = ErrBox::from(io_error());
assert_eq!(err.kind(), ErrorKind::NotFound); assert_eq!(err.kind(), ErrorKind::NotFound);
assert_eq!(err.to_string(), "entity not found"); assert_eq!(err.to_string(), "entity not found");
} }
#[test] #[test]
fn test_url_error() { fn test_url_error() {
let err = DenoError::from(url_error()); let err = ErrBox::from(url_error());
assert_eq!(err.kind(), ErrorKind::EmptyHost); assert_eq!(err.kind(), ErrorKind::EmptyHost);
assert_eq!(err.to_string(), "empty host"); assert_eq!(err.to_string(), "empty host");
} }
@ -498,32 +368,25 @@ mod tests {
#[test] #[test]
fn test_diagnostic() { fn test_diagnostic() {
let err = DenoError::from(diagnostic()); let err = ErrBox::from(diagnostic());
assert_eq!(err.kind(), ErrorKind::Diagnostic); assert_eq!(err.kind(), ErrorKind::Diagnostic);
assert_eq!(strip_ansi_codes(&err.to_string()), "error TS2322: Example 1\n\n► deno/tests/complex_diagnostics.ts:19:3\n\n19 values: o => [\n ~~~~~~\n\nerror TS2000: Example 2\n\n► /foo/bar.ts:129:3\n\n129 values: undefined,\n ~~~~~~\n\n\nFound 2 errors.\n"); assert_eq!(strip_ansi_codes(&err.to_string()), "error TS2322: Example 1\n\n► deno/tests/complex_diagnostics.ts:19:3\n\n19 values: o => [\n ~~~~~~\n\nerror TS2000: Example 2\n\n► /foo/bar.ts:129:3\n\n129 values: undefined,\n ~~~~~~\n\n\nFound 2 errors.\n");
} }
#[test] #[test]
fn test_js_error() { fn test_js_error() {
let err = DenoError::from(js_error()); let err = ErrBox::from(js_error());
assert_eq!(err.kind(), ErrorKind::JSError); assert_eq!(err.kind(), ErrorKind::JSError);
assert_eq!(strip_ansi_codes(&err.to_string()), "error: 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!(strip_ansi_codes(&err.to_string()), "error: 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");
} }
#[test] #[test]
fn test_import_map_error() { fn test_import_map_error() {
let err = DenoError::from(import_map_error()); let err = ErrBox::from(import_map_error());
assert_eq!(err.kind(), ErrorKind::ImportMapError); assert_eq!(err.kind(), ErrorKind::ImportMapError);
assert_eq!(err.to_string(), "an import map error"); assert_eq!(err.to_string(), "an import map error");
} }
#[test]
fn test_deno_dir_error() {
let err = DenoError::from(deno_dir_error());
assert_eq!(err.kind(), ErrorKind::DenoDirError);
assert_eq!(err.to_string(), "a deno dir error\n");
}
#[test] #[test]
fn test_bad_resource() { fn test_bad_resource() {
let err = bad_resource(); let err = bad_resource();
@ -545,13 +408,6 @@ mod tests {
assert_eq!(err.to_string(), "op not implemented"); assert_eq!(err.to_string(), "op not implemented");
} }
#[test]
fn test_worker_init_failed() {
let err = worker_init_failed();
assert_eq!(err.kind(), ErrorKind::WorkerInitFailed);
assert_eq!(err.to_string(), "worker init failed");
}
#[test] #[test]
fn test_no_buffer_specified() { fn test_no_buffer_specified() {
let err = no_buffer_specified(); let err = no_buffer_specified();
@ -572,20 +428,4 @@ mod tests {
assert_eq!(err.kind(), ErrorKind::NoSyncSupport); assert_eq!(err.kind(), ErrorKind::NoSyncSupport);
assert_eq!(err.to_string(), "op doesn't support sync calls"); assert_eq!(err.to_string(), "op doesn't support sync calls");
} }
#[test]
#[should_panic]
fn test_apply_source_map_invalid() {
let getter = MockSourceMapGetter {};
let err = new(ErrorKind::NotFound, "not found".to_string());
err.apply_source_map(&getter);
}
#[test]
#[should_panic]
fn test_err_check() {
err_check(
Err(new(ErrorKind::NotFound, "foo".to_string())) as Result<(), DenoError>
);
}
} }

View file

@ -7,6 +7,7 @@ use crate::fmt_errors::format_maybe_source_name;
use crate::fmt_errors::DisplayFormatter; use crate::fmt_errors::DisplayFormatter;
use serde_json; use serde_json;
use serde_json::value::Value; use serde_json::value::Value;
use std::error::Error;
use std::fmt; use std::fmt;
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
@ -66,6 +67,12 @@ impl fmt::Display for Diagnostic {
} }
} }
impl Error for Diagnostic {
fn description(&self) -> &str {
&self.items[0].message
}
}
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub struct DiagnosticItem { pub struct DiagnosticItem {
/// The top level message relating to the diagnostic item. /// The top level message relating to the diagnostic item.

View file

@ -127,10 +127,11 @@ mod ops {
use crate::deno_error; use crate::deno_error;
use crate::resources; use crate::resources;
use crate::tokio_write; use crate::tokio_write;
use deno::ErrBox;
use deno::PinnedBuf; use deno::PinnedBuf;
use futures::Future; use futures::Future;
type MinimalOp = dyn Future<Item = i32, Error = deno_error::DenoError> + Send; type MinimalOp = dyn Future<Item = i32, Error = ErrBox> + Send;
pub fn read(rid: i32, zero_copy: Option<PinnedBuf>) -> Box<MinimalOp> { pub fn read(rid: i32, zero_copy: Option<PinnedBuf>) -> Box<MinimalOp> {
debug!("read rid={}", rid); debug!("read rid={}", rid);
@ -144,7 +145,7 @@ mod ops {
None => Box::new(futures::future::err(deno_error::bad_resource())), None => Box::new(futures::future::err(deno_error::bad_resource())),
Some(resource) => Box::new( Some(resource) => Box::new(
tokio::io::read(resource, zero_copy) tokio::io::read(resource, zero_copy)
.map_err(deno_error::DenoError::from) .map_err(ErrBox::from)
.and_then(move |(_resource, _buf, nread)| Ok(nread as i32)), .and_then(move |(_resource, _buf, nread)| Ok(nread as i32)),
), ),
} }
@ -162,7 +163,7 @@ mod ops {
None => Box::new(futures::future::err(deno_error::bad_resource())), None => Box::new(futures::future::err(deno_error::bad_resource())),
Some(resource) => Box::new( Some(resource) => Box::new(
tokio_write::write(resource, zero_copy) tokio_write::write(resource, zero_copy)
.map_err(deno_error::DenoError::from) .map_err(ErrBox::from)
.and_then(move |(_resource, _buf, nwritten)| Ok(nwritten as i32)), .and_then(move |(_resource, _buf, nwritten)| Ok(nwritten as i32)),
), ),
} }

View file

@ -1,8 +1,12 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
//! This mod provides DenoError to unify errors across Deno. //! This mod provides DenoError to unify errors across Deno.
use crate::ansi; use crate::ansi;
use deno::JSError; use crate::source_maps::apply_source_map;
use crate::source_maps::SourceMapGetter;
use deno::ErrBox;
use deno::StackFrame; use deno::StackFrame;
use deno::V8Exception;
use std::error::Error;
use std::fmt; use std::fmt;
/// A trait which specifies parts of a diagnostic like item needs to be able to /// A trait which specifies parts of a diagnostic like item needs to be able to
@ -104,10 +108,50 @@ pub fn format_error_message(msg: String) -> String {
format!("{} {}", preamble, msg) format!("{} {}", preamble, msg)
} }
/// Wrapper around JSError which provides color to_string. fn format_stack_frame(frame: &StackFrame) -> String {
pub struct JSErrorColor<'a>(pub &'a JSError); // Note when we print to string, we change from 0-indexed to 1-indexed.
let function_name = ansi::italic_bold(frame.function_name.clone());
let source_loc =
format_source_name(frame.script_name.clone(), frame.line, frame.column);
impl<'a> DisplayFormatter for JSErrorColor<'a> { if !frame.function_name.is_empty() {
format!(" at {} ({})", function_name, source_loc)
} else if frame.is_eval {
format!(" at eval ({})", source_loc)
} else {
format!(" at {}", source_loc)
}
}
/// Wrapper around V8Exception which provides color to_string.
#[derive(Debug)]
pub struct JSError(V8Exception);
impl JSError {
pub fn new(v8_exception: V8Exception) -> Self {
Self(v8_exception)
}
pub fn from_json(
json_str: &str,
source_map_getter: &impl SourceMapGetter,
) -> ErrBox {
let unmapped_exception = V8Exception::from_json(json_str).unwrap();
Self::from_v8_exception(unmapped_exception, source_map_getter)
}
pub fn from_v8_exception(
unmapped_exception: V8Exception,
source_map_getter: &impl SourceMapGetter,
) -> ErrBox {
let mapped_exception =
apply_source_map(&unmapped_exception, source_map_getter);
let js_error = Self(mapped_exception);
ErrBox::from(js_error)
}
}
impl DisplayFormatter for JSError {
fn format_category_and_code(&self) -> String { fn format_category_and_code(&self) -> String {
"".to_string() "".to_string()
} }
@ -136,7 +180,7 @@ impl<'a> DisplayFormatter for JSErrorColor<'a> {
} }
fn format_source_name(&self) -> String { fn format_source_name(&self) -> String {
let e = self.0; let e = &self.0;
if e.script_resource_name.is_none() { if e.script_resource_name.is_none() {
return "".to_string(); return "".to_string();
} }
@ -152,7 +196,7 @@ impl<'a> DisplayFormatter for JSErrorColor<'a> {
} }
} }
impl<'a> fmt::Display for JSErrorColor<'a> { impl fmt::Display for JSError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!( write!(
f, f,
@ -163,39 +207,21 @@ impl<'a> fmt::Display for JSErrorColor<'a> {
)?; )?;
for frame in &self.0.frames { for frame in &self.0.frames {
write!(f, "\n{}", StackFrameColor(&frame).to_string())?; write!(f, "\n{}", format_stack_frame(&frame))?;
} }
Ok(()) Ok(())
} }
} }
struct StackFrameColor<'a>(&'a StackFrame); impl Error for JSError {}
impl<'a> fmt::Display for StackFrameColor<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let frame = self.0;
// Note when we print to string, we change from 0-indexed to 1-indexed.
let function_name = ansi::italic_bold(frame.function_name.clone());
let script_line_column =
format_source_name(frame.script_name.clone(), frame.line, frame.column);
if !frame.function_name.is_empty() {
write!(f, " at {} ({})", function_name, script_line_column)
} else if frame.is_eval {
write!(f, " at eval ({})", script_line_column)
} else {
write!(f, " at {}", script_line_column)
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::ansi::strip_ansi_codes; use crate::ansi::strip_ansi_codes;
fn error1() -> JSError { fn error1() -> V8Exception {
JSError { V8Exception {
message: "Error: foo bar".to_string(), message: "Error: foo bar".to_string(),
source_line: None, source_line: None,
script_resource_name: None, script_resource_name: None,
@ -240,7 +266,7 @@ mod tests {
#[test] #[test]
fn js_error_to_string() { fn js_error_to_string() {
let e = error1(); let e = error1();
assert_eq!("error: 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", strip_ansi_codes(&JSErrorColor(&e).to_string())); assert_eq!("error: 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", strip_ansi_codes(&JSError(e).to_string()));
} }
#[test] #[test]

View file

@ -5,6 +5,7 @@ use std::io::ErrorKind;
use std::io::Write; use std::io::Write;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use deno::ErrBox;
use rand; use rand;
use rand::Rng; use rand::Rng;
@ -15,8 +16,6 @@ use std::os::unix::fs::DirBuilderExt;
#[cfg(any(unix))] #[cfg(any(unix))]
use std::os::unix::fs::PermissionsExt; use std::os::unix::fs::PermissionsExt;
use crate::deno_error::DenoResult;
pub fn write_file<T: AsRef<[u8]>>( pub fn write_file<T: AsRef<[u8]>>(
filename: &Path, filename: &Path,
data: T, data: T,
@ -114,18 +113,16 @@ pub fn normalize_path(path: &Path) -> String {
} }
#[cfg(unix)] #[cfg(unix)]
pub fn chown(path: &str, uid: u32, gid: u32) -> DenoResult<()> { pub fn chown(path: &str, uid: u32, gid: u32) -> Result<(), ErrBox> {
use crate::deno_error::DenoError;
let nix_uid = Uid::from_raw(uid); let nix_uid = Uid::from_raw(uid);
let nix_gid = Gid::from_raw(gid); let nix_gid = Gid::from_raw(gid);
unix_chown(path, Option::Some(nix_uid), Option::Some(nix_gid)) unix_chown(path, Option::Some(nix_uid), Option::Some(nix_gid))
.map_err(DenoError::from) .map_err(ErrBox::from)
} }
#[cfg(not(unix))] #[cfg(not(unix))]
pub fn chown(_path: &str, _uid: u32, _gid: u32) -> DenoResult<()> { pub fn chown(_path: &str, _uid: u32, _gid: u32) -> Result<(), ErrBox> {
// Noop // Noop
// TODO: implement chown for Windows // TODO: implement chown for Windows
use crate::deno_error; Err(crate::deno_error::op_not_implemented())
Err(deno_error::op_not_implemented())
} }

View file

@ -1,6 +1,7 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
use crate::deno_error; use crate::deno_error;
use crate::deno_error::DenoError; use crate::deno_error::DenoError;
use deno::ErrBox;
#[cfg(test)] #[cfg(test)]
use futures::future::{loop_fn, Loop}; use futures::future::{loop_fn, Loop};
use futures::{future, Future, Stream}; use futures::{future, Future, Stream};
@ -57,13 +58,13 @@ fn resolve_uri_from_location(base_uri: &Uri, location: &str) -> Uri {
} }
} }
#[cfg(test)]
use crate::deno_error::DenoResult;
#[cfg(test)] #[cfg(test)]
use crate::tokio_util; use crate::tokio_util;
#[cfg(test)] #[cfg(test)]
/// Synchronously fetchs the given HTTP URL. Returns (content, media_type). /// Synchronously fetchs the given HTTP URL. Returns (content, media_type).
pub fn fetch_sync_string(module_name: &str) -> DenoResult<(String, String)> { pub fn fetch_sync_string(
module_name: &str,
) -> Result<(String, String), ErrBox> {
tokio_util::block_on(fetch_string(module_name)) tokio_util::block_on(fetch_string(module_name))
} }
@ -80,15 +81,15 @@ pub enum FetchOnceResult {
/// yields Redirect(url). /// yields Redirect(url).
pub fn fetch_string_once( pub fn fetch_string_once(
url: http::uri::Uri, url: http::uri::Uri,
) -> impl Future<Item = FetchOnceResult, Error = DenoError> { ) -> impl Future<Item = FetchOnceResult, Error = ErrBox> {
type FetchAttempt = (Option<String>, Option<String>, Option<FetchOnceResult>); type FetchAttempt = (Option<String>, Option<String>, Option<FetchOnceResult>);
let client = get_client(); let client = get_client();
client client
.get(url.clone()) .get(url.clone())
.map_err(DenoError::from) .map_err(ErrBox::from)
.and_then( .and_then(
move |response| -> Box< move |response| -> Box<
dyn Future<Item = FetchAttempt, Error = DenoError> + Send, dyn Future<Item = FetchAttempt, Error = ErrBox> + Send,
> { > {
if response.status().is_redirection() { if response.status().is_redirection() {
let location_string = response let location_string = response
@ -108,10 +109,10 @@ pub fn fetch_string_once(
} else if response.status().is_client_error() } else if response.status().is_client_error()
|| response.status().is_server_error() || response.status().is_server_error()
{ {
return Box::new(future::err(deno_error::new( return Box::new(future::err(DenoError::new(
deno_error::ErrorKind::Other, deno_error::ErrorKind::Other,
format!("Import '{}' failed: {}", &url, response.status()), format!("Import '{}' failed: {}", &url, response.status()),
))); ).into()));
} }
let content_type = response let content_type = response
.headers() .headers()
@ -121,7 +122,7 @@ pub fn fetch_string_once(
.into_body() .into_body()
.concat2() .concat2()
.map(|body| String::from_utf8(body.to_vec()).ok()) .map(|body| String::from_utf8(body.to_vec()).ok())
.map_err(DenoError::from); .map_err(ErrBox::from);
Box::new(body.join3(future::ok(content_type), future::ok(None))) Box::new(body.join3(future::ok(content_type), future::ok(None)))
}, },
) )
@ -142,7 +143,7 @@ pub fn fetch_string_once(
/// Asynchronously fetchs the given HTTP URL. Returns (content, media_type). /// Asynchronously fetchs the given HTTP URL. Returns (content, media_type).
pub fn fetch_string( pub fn fetch_string(
module_name: &str, module_name: &str,
) -> impl Future<Item = (String, String), Error = DenoError> { ) -> impl Future<Item = (String, String), Error = ErrBox> {
let url = module_name.parse::<Uri>().unwrap(); let url = module_name.parse::<Uri>().unwrap();
let client = get_client(); let client = get_client();
// TODO(kevinkassimo): consider set a max redirection counter // TODO(kevinkassimo): consider set a max redirection counter
@ -150,7 +151,7 @@ pub fn fetch_string(
loop_fn((client, url), |(client, url)| { loop_fn((client, url), |(client, url)| {
client client
.get(url.clone()) .get(url.clone())
.map_err(DenoError::from) .map_err(ErrBox::from)
.and_then(move |response| { .and_then(move |response| {
if response.status().is_redirection() { if response.status().is_redirection() {
let location_string = response let location_string = response
@ -165,10 +166,12 @@ pub fn fetch_string(
return Ok(Loop::Continue((client, new_url))); return Ok(Loop::Continue((client, new_url)));
} }
if !response.status().is_success() { if !response.status().is_success() {
return Err(deno_error::new( return Err(
DenoError::new(
deno_error::ErrorKind::NotFound, deno_error::ErrorKind::NotFound,
"module not found".to_string(), "module not found".to_string(),
)); ).into(),
);
} }
Ok(Loop::Break(response)) Ok(Loop::Break(response))
}) })
@ -181,7 +184,7 @@ pub fn fetch_string(
.into_body() .into_body()
.concat2() .concat2()
.map(|body| String::from_utf8(body.to_vec()).unwrap()) .map(|body| String::from_utf8(body.to_vec()).unwrap())
.map_err(DenoError::from); .map_err(ErrBox::from);
body.join(future::ok(content_type)) body.join(future::ok(content_type))
}).and_then(|(body_string, maybe_content_type)| { }).and_then(|(body_string, maybe_content_type)| {
future::ok((body_string, maybe_content_type.unwrap())) future::ok((body_string, maybe_content_type.unwrap()))

View file

@ -3,6 +3,8 @@ use indexmap::IndexMap;
use serde_json::Map; use serde_json::Map;
use serde_json::Value; use serde_json::Value;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::error::Error;
use std::fmt;
use std::fs; use std::fs;
use url::Url; use url::Url;
@ -19,6 +21,14 @@ impl ImportMapError {
} }
} }
impl fmt::Display for ImportMapError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.pad(&self.msg)
}
}
impl Error for ImportMapError {}
// NOTE: here is difference between deno and reference implementation - deno currently // NOTE: here is difference between deno and reference implementation - deno currently
// can't resolve URL with other schemes (eg. data:, about:, blob:) // can't resolve URL with other schemes (eg. data:, about:, blob:)
const SUPPORTED_FETCH_SCHEMES: [&str; 3] = ["http", "https", "file"]; const SUPPORTED_FETCH_SCHEMES: [&str; 3] = ["http", "https", "file"];

View file

@ -46,11 +46,11 @@ pub mod version;
pub mod worker; pub mod worker;
use crate::compiler::bundle_async; use crate::compiler::bundle_async;
use crate::deno_error::DenoError;
use crate::progress::Progress; use crate::progress::Progress;
use crate::state::ThreadSafeState; use crate::state::ThreadSafeState;
use crate::worker::Worker; use crate::worker::Worker;
use deno::v8_set_flags; use deno::v8_set_flags;
use deno::ErrBox;
use deno::ModuleSpecifier; use deno::ModuleSpecifier;
use flags::DenoFlags; use flags::DenoFlags;
use flags::DenoSubcommand; use flags::DenoSubcommand;
@ -86,17 +86,14 @@ impl log::Log for Logger {
fn flush(&self) {} fn flush(&self) {}
} }
fn print_err_and_exit(err: DenoError) { fn print_err_and_exit(err: ErrBox) {
eprintln!("{}", err.to_string()); eprintln!("{}", err.to_string());
std::process::exit(1); std::process::exit(1);
} }
fn js_check<E>(r: Result<(), E>) fn js_check(r: Result<(), ErrBox>) {
where
E: Into<DenoError>,
{
if let Err(err) = r { if let Err(err) = r {
print_err_and_exit(err.into()); print_err_and_exit(err);
} }
} }
@ -264,7 +261,7 @@ fn xeval_command(flags: DenoFlags, argv: Vec<String>) {
.then(|result| { .then(|result| {
js_check(result); js_check(result);
Ok(()) Ok(())
}).map_err(|(err, _worker): (DenoError, Worker)| print_err_and_exit(err)) }).map_err(print_err_and_exit)
}); });
tokio_util::run(main_future); tokio_util::run(main_future);
} }
@ -277,10 +274,10 @@ fn bundle_command(flags: DenoFlags, argv: Vec<String>) {
let out_file = state.argv[2].clone(); let out_file = state.argv[2].clone();
debug!(">>>>> bundle_async START"); debug!(">>>>> bundle_async START");
let bundle_future = bundle_async(state, main_module.to_string(), out_file) let bundle_future = bundle_async(state, main_module.to_string(), out_file)
.map_err(|e| { .map_err(|err| {
debug!("diagnostics returned, exiting!"); debug!("diagnostics returned, exiting!");
eprintln!("\n{}", e.to_string()); eprintln!("");
std::process::exit(1); print_err_and_exit(err);
}).and_then(move |_| { }).and_then(move |_| {
debug!(">>>>> bundle_async END"); debug!(">>>>> bundle_async END");
Ok(()) Ok(())
@ -299,7 +296,7 @@ fn run_repl(flags: DenoFlags, argv: Vec<String>) {
.then(|result| { .then(|result| {
js_check(result); js_check(result);
Ok(()) Ok(())
}).map_err(|(err, _worker): (DenoError, Worker)| print_err_and_exit(err)) }).map_err(|(err, _worker): (ErrBox, Worker)| print_err_and_exit(err))
}); });
tokio_util::run(main_future); tokio_util::run(main_future);
} }

View file

@ -102,7 +102,6 @@ enum ErrorKind: byte {
WouldBlock, WouldBlock,
InvalidInput, InvalidInput,
InvalidData, InvalidData,
InvalidPath,
TimedOut, TimedOut,
Interrupted, Interrupted,
WriteZero, WriteZero,
@ -142,8 +141,9 @@ enum ErrorKind: byte {
NoAsyncSupport, NoAsyncSupport,
NoSyncSupport, NoSyncSupport,
ImportMapError, ImportMapError,
InvalidPath,
ImportPrefixMissing, ImportPrefixMissing,
DenoDirError, UnsupportedFetchScheme,
// other kinds // other kinds
Diagnostic, Diagnostic,

View file

@ -1,9 +1,7 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
// Helpers for serialization. // Helpers for serialization.
use crate::deno_error;
use crate::deno_error::DenoResult;
use crate::msg; use crate::msg;
use deno::ErrBox;
use flatbuffers; use flatbuffers;
use http::header::HeaderName; use http::header::HeaderName;
use http::uri::Uri; use http::uri::Uri;
@ -97,14 +95,13 @@ pub fn serialize_http_response<'bldr>(
pub fn deserialize_request( pub fn deserialize_request(
header_msg: msg::HttpHeader<'_>, header_msg: msg::HttpHeader<'_>,
body: Body, body: Body,
) -> DenoResult<Request<Body>> { ) -> Result<Request<Body>, ErrBox> {
let mut r = Request::new(body); let mut r = Request::new(body);
assert!(header_msg.is_request()); assert!(header_msg.is_request());
let u = header_msg.url().unwrap(); let u = header_msg.url().unwrap();
let u = Uri::from_str(u) let u = Uri::from_str(u).map_err(ErrBox::from)?;
.map_err(|e| deno_error::new(msg::ErrorKind::InvalidUri, e.to_string()))?;
*r.uri_mut() = u; *r.uri_mut() = u;
if let Some(method) = header_msg.method() { if let Some(method) = header_msg.method() {

View file

@ -3,12 +3,12 @@ use atty;
use crate::ansi; use crate::ansi;
use crate::deno_dir::resolve_from_cwd; use crate::deno_dir::resolve_from_cwd;
use crate::deno_error; use crate::deno_error;
use crate::deno_error::err_check;
use crate::deno_error::DenoError; use crate::deno_error::DenoError;
use crate::deno_error::DenoResult;
use crate::deno_error::ErrorKind; use crate::deno_error::ErrorKind;
use crate::deno_error::GetErrorKind;
use crate::dispatch_minimal::dispatch_minimal; use crate::dispatch_minimal::dispatch_minimal;
use crate::dispatch_minimal::parse_min_record; use crate::dispatch_minimal::parse_min_record;
use crate::fmt_errors::JSError;
use crate::fs as deno_fs; use crate::fs as deno_fs;
use crate::http_util; use crate::http_util;
use crate::msg; use crate::msg;
@ -28,7 +28,7 @@ use crate::version;
use crate::worker::Worker; use crate::worker::Worker;
use deno::Buf; use deno::Buf;
use deno::CoreOp; use deno::CoreOp;
use deno::JSError; use deno::ErrBox;
use deno::Loader; use deno::Loader;
use deno::ModuleSpecifier; use deno::ModuleSpecifier;
use deno::Op; use deno::Op;
@ -65,7 +65,7 @@ use std::os::unix::fs::PermissionsExt;
#[cfg(unix)] #[cfg(unix)]
use std::os::unix::process::ExitStatusExt; use std::os::unix::process::ExitStatusExt;
type CliOpResult = OpResult<DenoError>; type CliOpResult = OpResult<ErrBox>;
type CliDispatchFn = type CliDispatchFn =
fn(state: &ThreadSafeState, base: &msg::Base<'_>, data: Option<PinnedBuf>) fn(state: &ThreadSafeState, base: &msg::Base<'_>, data: Option<PinnedBuf>)
@ -132,7 +132,7 @@ pub fn dispatch_all_legacy(
} }
Ok(Op::Async(fut)) => { Ok(Op::Async(fut)) => {
let result_fut = Box::new( let result_fut = Box::new(
fut.or_else(move |err: DenoError| -> Result<Buf, ()> { fut.or_else(move |err: ErrBox| -> Result<Buf, ()> {
debug!("op err {}", err); debug!("op err {}", err);
// No matter whether we got an Err or Ok, we want a serialized message to // No matter whether we got an Err or Ok, we want a serialized message to
// send back. So transform the DenoError into a Buf. // send back. So transform the DenoError into a Buf.
@ -410,11 +410,9 @@ fn op_format_error(
) -> CliOpResult { ) -> CliOpResult {
assert!(data.is_none()); assert!(data.is_none());
let inner = base.inner_as_format_error().unwrap(); let inner = base.inner_as_format_error().unwrap();
let orig_error = String::from(inner.error().unwrap()); let json_str = inner.error().unwrap();
let error = JSError::from_json(json_str, &state.dir);
let js_error = JSError::from_v8_exception(&orig_error).unwrap(); let error_string = error.to_string();
let error_mapped = DenoError::from(js_error).apply_source_map(&state.dir);
let error_string = error_mapped.to_string();
let mut builder = FlatBufferBuilder::new(); let mut builder = FlatBufferBuilder::new();
let new_error = builder.create_string(&error_string); let new_error = builder.create_string(&error_string);
@ -495,10 +493,10 @@ fn op_cache(
if extension == ".map" { if extension == ".map" {
debug!("cache {:?}", source_map_path); debug!("cache {:?}", source_map_path);
fs::write(source_map_path, contents).map_err(DenoError::from)?; fs::write(source_map_path, contents).map_err(ErrBox::from)?;
} else if extension == ".js" { } else if extension == ".js" {
debug!("cache {:?}", js_cache_path); debug!("cache {:?}", js_cache_path);
fs::write(js_cache_path, contents).map_err(DenoError::from)?; fs::write(js_cache_path, contents).map_err(ErrBox::from)?;
} else { } else {
unreachable!(); unreachable!();
} }
@ -735,16 +733,15 @@ fn op_fetch(
let req = msg_util::deserialize_request(header, body)?; let req = msg_util::deserialize_request(header, body)?;
let url_ = url::Url::parse(url).map_err(DenoError::from)?; let url_ = url::Url::parse(url).map_err(ErrBox::from)?;
state.check_net_url(url_)?; state.check_net_url(url_)?;
let client = http_util::get_client(); let client = http_util::get_client();
debug!("Before fetch {}", url); debug!("Before fetch {}", url);
let future = let future = client
client
.request(req) .request(req)
.map_err(DenoError::from) .map_err(ErrBox::from)
.and_then(move |res| { .and_then(move |res| {
let builder = &mut FlatBufferBuilder::new(); let builder = &mut FlatBufferBuilder::new();
let header_off = msg_util::serialize_http_response(builder, &res); let header_off = msg_util::serialize_http_response(builder, &res);
@ -778,9 +775,9 @@ fn op_fetch(
// This is just type conversion. Implement From trait? // This is just type conversion. Implement From trait?
// See https://github.com/tokio-rs/tokio/blob/ffd73a64e7ec497622b7f939e38017afe7124dc4/tokio-fs/src/lib.rs#L76-L85 // See https://github.com/tokio-rs/tokio/blob/ffd73a64e7ec497622b7f939e38017afe7124dc4/tokio-fs/src/lib.rs#L76-L85
fn convert_blocking<F>(f: F) -> Poll<Buf, DenoError> fn convert_blocking<F>(f: F) -> Poll<Buf, ErrBox>
where where
F: FnOnce() -> DenoResult<Buf>, F: FnOnce() -> Result<Buf, ErrBox>,
{ {
use futures::Async::*; use futures::Async::*;
match tokio_threadpool::blocking(f) { match tokio_threadpool::blocking(f) {
@ -793,7 +790,7 @@ where
fn blocking<F>(is_sync: bool, f: F) -> CliOpResult fn blocking<F>(is_sync: bool, f: F) -> CliOpResult
where where
F: 'static + Send + FnOnce() -> DenoResult<Buf>, F: 'static + Send + FnOnce() -> Result<Buf, ErrBox>,
{ {
if is_sync { if is_sync {
let result_buf = f()?; let result_buf = f()?;
@ -981,10 +978,8 @@ fn op_open(
} }
} }
let op = open_options let op = open_options.open(filename).map_err(ErrBox::from).and_then(
.open(filename) move |fs_file| {
.map_err(DenoError::from)
.and_then(move |fs_file| {
let resource = resources::add_fs_file(fs_file); let resource = resources::add_fs_file(fs_file);
let builder = &mut FlatBufferBuilder::new(); let builder = &mut FlatBufferBuilder::new();
let inner = let inner =
@ -998,7 +993,8 @@ fn op_open(
..Default::default() ..Default::default()
}, },
)) ))
}); },
);
if base.sync() { if base.sync() {
let buf = op.wait()?; let buf = op.wait()?;
Ok(Op::Sync(buf)) Ok(Op::Sync(buf))
@ -1076,7 +1072,7 @@ fn op_read(
None => Err(deno_error::bad_resource()), None => Err(deno_error::bad_resource()),
Some(resource) => { Some(resource) => {
let op = tokio::io::read(resource, data.unwrap()) let op = tokio::io::read(resource, data.unwrap())
.map_err(DenoError::from) .map_err(ErrBox::from)
.and_then(move |(_resource, _buf, nread)| { .and_then(move |(_resource, _buf, nread)| {
let builder = &mut FlatBufferBuilder::new(); let builder = &mut FlatBufferBuilder::new();
let inner = msg::ReadRes::create( let inner = msg::ReadRes::create(
@ -1119,7 +1115,7 @@ fn op_write(
None => Err(deno_error::bad_resource()), None => Err(deno_error::bad_resource()),
Some(resource) => { Some(resource) => {
let op = tokio_write::write(resource, data.unwrap()) let op = tokio_write::write(resource, data.unwrap())
.map_err(DenoError::from) .map_err(ErrBox::from)
.and_then(move |(_resource, _buf, nwritten)| { .and_then(move |(_resource, _buf, nwritten)| {
let builder = &mut FlatBufferBuilder::new(); let builder = &mut FlatBufferBuilder::new();
let inner = msg::WriteRes::create( let inner = msg::WriteRes::create(
@ -1219,10 +1215,10 @@ fn op_copy_file(
// See https://github.com/rust-lang/rust/issues/54800 // See https://github.com/rust-lang/rust/issues/54800
// Once the issue is reolved, we should remove this workaround. // Once the issue is reolved, we should remove this workaround.
if cfg!(unix) && !from.is_file() { if cfg!(unix) && !from.is_file() {
return Err(deno_error::new( return Err(
ErrorKind::NotFound, DenoError::new(ErrorKind::NotFound, "File not found".to_string())
"File not found".to_string(), .into(),
)); );
} }
fs::copy(&from, &to)?; fs::copy(&from, &to)?;
@ -1431,10 +1427,9 @@ fn op_symlink(
state.check_write(&newname_)?; state.check_write(&newname_)?;
// TODO Use type for Windows. // TODO Use type for Windows.
if cfg!(windows) { if cfg!(windows) {
return Err(deno_error::new( return Err(
ErrorKind::Other, DenoError::new(ErrorKind::Other, "Not implemented".to_string()).into(),
"Not implemented".to_string(), );
));
} }
blocking(base.sync(), move || { blocking(base.sync(), move || {
debug!("op_symlink {} {}", oldname.display(), newname.display()); debug!("op_symlink {} {}", oldname.display(), newname.display());
@ -1621,7 +1616,7 @@ fn op_listen(
ok_buf(response_buf) ok_buf(response_buf)
} }
fn new_conn(cmd_id: u32, tcp_stream: TcpStream) -> DenoResult<Buf> { fn new_conn(cmd_id: u32, tcp_stream: TcpStream) -> Result<Buf, ErrBox> {
let tcp_stream_resource = resources::add_tcp_stream(tcp_stream); let tcp_stream_resource = resources::add_tcp_stream(tcp_stream);
// TODO forward socket_addr to client. // TODO forward socket_addr to client.
@ -1658,7 +1653,7 @@ fn op_accept(
None => Err(deno_error::bad_resource()), None => Err(deno_error::bad_resource()),
Some(server_resource) => { Some(server_resource) => {
let op = tokio_util::accept(server_resource) let op = tokio_util::accept(server_resource)
.map_err(DenoError::from) .map_err(ErrBox::from)
.and_then(move |(tcp_stream, _socket_addr)| { .and_then(move |(tcp_stream, _socket_addr)| {
new_conn(cmd_id, tcp_stream) new_conn(cmd_id, tcp_stream)
}); });
@ -1686,12 +1681,9 @@ fn op_dial(
state.check_net(&address)?; state.check_net(&address)?;
let op = let op = resolve_addr(address).and_then(move |addr| {
resolve_addr(address)
.map_err(DenoError::from)
.and_then(move |addr| {
TcpStream::connect(&addr) TcpStream::connect(&addr)
.map_err(DenoError::from) .map_err(ErrBox::from)
.and_then(move |tcp_stream| new_conn(cmd_id, tcp_stream)) .and_then(move |tcp_stream| new_conn(cmd_id, tcp_stream))
}); });
if base.sync() { if base.sync() {
@ -1858,7 +1850,7 @@ fn op_run(
} }
// Spawn the command. // Spawn the command.
let child = c.spawn_async().map_err(DenoError::from)?; let child = c.spawn_async().map_err(ErrBox::from)?;
let pid = child.id(); let pid = child.id();
let resources = resources::add_child(child); let resources = resources::add_child(child);
@ -1977,8 +1969,8 @@ fn op_worker_get_message(
let op = GetMessageFuture { let op = GetMessageFuture {
state: state.clone(), state: state.clone(),
}; };
let op = op.map_err(move |_| -> DenoError { unimplemented!() }); let op = op.map_err(move |_| -> ErrBox { unimplemented!() });
let op = op.and_then(move |maybe_buf| -> DenoResult<Buf> { let op = op.and_then(move |maybe_buf| -> Result<Buf, ErrBox> {
debug!("op_worker_get_message"); debug!("op_worker_get_message");
let builder = &mut FlatBufferBuilder::new(); let builder = &mut FlatBufferBuilder::new();
@ -2015,7 +2007,7 @@ fn op_worker_post_message(
}; };
tx.send(d) tx.send(d)
.wait() .wait()
.map_err(|e| deno_error::new(ErrorKind::Other, e.to_string()))?; .map_err(|e| DenoError::new(ErrorKind::Other, e.to_string()))?;
let builder = &mut FlatBufferBuilder::new(); let builder = &mut FlatBufferBuilder::new();
ok_buf(serialize_response( ok_buf(serialize_response(
@ -2051,8 +2043,8 @@ fn op_create_worker(
let mut worker = let mut worker =
Worker::new(name, startup_data::deno_isolate_init(), child_state); Worker::new(name, startup_data::deno_isolate_init(), child_state);
err_check(worker.execute("denoMain()")); worker.execute("denoMain()").unwrap();
err_check(worker.execute("workerMain()")); worker.execute("workerMain()").unwrap();
let module_specifier = ModuleSpecifier::resolve_url_or_path(specifier)?; let module_specifier = ModuleSpecifier::resolve_url_or_path(specifier)?;
@ -2132,8 +2124,8 @@ fn op_host_get_message(
let rid = inner.rid(); let rid = inner.rid();
let op = resources::get_message_from_worker(rid); let op = resources::get_message_from_worker(rid);
let op = op.map_err(move |_| -> DenoError { unimplemented!() }); let op = op.map_err(move |_| -> ErrBox { unimplemented!() });
let op = op.and_then(move |maybe_buf| -> DenoResult<Buf> { let op = op.and_then(move |maybe_buf| -> Result<Buf, ErrBox> {
let builder = &mut FlatBufferBuilder::new(); let builder = &mut FlatBufferBuilder::new();
let data = maybe_buf.as_ref().map(|buf| builder.create_vector(buf)); let data = maybe_buf.as_ref().map(|buf| builder.create_vector(buf));
@ -2168,7 +2160,7 @@ fn op_host_post_message(
resources::post_message_to_worker(rid, d) resources::post_message_to_worker(rid, d)
.wait() .wait()
.map_err(|e| deno_error::new(ErrorKind::Other, e.to_string()))?; .map_err(|e| DenoError::new(ErrorKind::Other, e.to_string()))?;
let builder = &mut FlatBufferBuilder::new(); let builder = &mut FlatBufferBuilder::new();
ok_buf(serialize_response( ok_buf(serialize_response(

View file

@ -1,11 +1,9 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
use atty;
use crate::flags::DenoFlags;
use ansi_term::Style; use ansi_term::Style;
use atty;
use crate::deno_error::permission_denied; use crate::deno_error::permission_denied;
use crate::deno_error::DenoResult; use crate::flags::DenoFlags;
use deno::ErrBox;
use log; use log;
use std::collections::HashSet; use std::collections::HashSet;
use std::fmt; use std::fmt;
@ -160,7 +158,7 @@ impl DenoPermissions {
} }
} }
pub fn check_run(&self) -> DenoResult<()> { pub fn check_run(&self) -> Result<(), ErrBox> {
let msg = "access to run a subprocess"; let msg = "access to run a subprocess";
match self.allow_run.get_state() { match self.allow_run.get_state() {
@ -181,7 +179,7 @@ impl DenoPermissions {
} }
} }
pub fn check_read(&self, filename: &str) -> DenoResult<()> { pub fn check_read(&self, filename: &str) -> Result<(), ErrBox> {
let msg = &format!("read access to \"{}\"", filename); let msg = &format!("read access to \"{}\"", filename);
match self.allow_read.get_state() { match self.allow_read.get_state() {
PermissionAccessorState::Allow => { PermissionAccessorState::Allow => {
@ -213,7 +211,7 @@ impl DenoPermissions {
} }
} }
pub fn check_write(&self, filename: &str) -> DenoResult<()> { pub fn check_write(&self, filename: &str) -> Result<(), ErrBox> {
let msg = &format!("write access to \"{}\"", filename); let msg = &format!("write access to \"{}\"", filename);
match self.allow_write.get_state() { match self.allow_write.get_state() {
PermissionAccessorState::Allow => { PermissionAccessorState::Allow => {
@ -245,7 +243,7 @@ impl DenoPermissions {
} }
} }
pub fn check_net(&self, host_and_port: &str) -> DenoResult<()> { pub fn check_net(&self, host_and_port: &str) -> Result<(), ErrBox> {
let msg = &format!("network access to \"{}\"", host_and_port); let msg = &format!("network access to \"{}\"", host_and_port);
match self.allow_net.get_state() { match self.allow_net.get_state() {
PermissionAccessorState::Allow => { PermissionAccessorState::Allow => {
@ -276,7 +274,7 @@ impl DenoPermissions {
} }
} }
pub fn check_net_url(&self, url: url::Url) -> DenoResult<()> { pub fn check_net_url(&self, url: url::Url) -> Result<(), ErrBox> {
let msg = &format!("network access to \"{}\"", url); let msg = &format!("network access to \"{}\"", url);
match self.allow_net.get_state() { match self.allow_net.get_state() {
PermissionAccessorState::Allow => { PermissionAccessorState::Allow => {
@ -311,7 +309,7 @@ impl DenoPermissions {
&self, &self,
state: PermissionAccessorState, state: PermissionAccessorState,
prompt_str: &str, prompt_str: &str,
) -> DenoResult<()> { ) -> Result<(), ErrBox> {
match state { match state {
PermissionAccessorState::Ask => { PermissionAccessorState::Ask => {
match self.try_permissions_prompt(prompt_str) { match self.try_permissions_prompt(prompt_str) {
@ -329,7 +327,7 @@ impl DenoPermissions {
} }
} }
pub fn check_env(&self) -> DenoResult<()> { pub fn check_env(&self) -> Result<(), ErrBox> {
let msg = "access to environment variables"; let msg = "access to environment variables";
match self.allow_env.get_state() { match self.allow_env.get_state() {
PermissionAccessorState::Allow => { PermissionAccessorState::Allow => {
@ -351,7 +349,10 @@ impl DenoPermissions {
/// Try to present the user with a permission prompt /// Try to present the user with a permission prompt
/// will error with permission_denied if no_prompts is enabled /// will error with permission_denied if no_prompts is enabled
fn try_permissions_prompt(&self, message: &str) -> DenoResult<PromptResult> { fn try_permissions_prompt(
&self,
message: &str,
) -> Result<PromptResult, ErrBox> {
if self.no_prompts.load(Ordering::SeqCst) { if self.no_prompts.load(Ordering::SeqCst) {
return Err(permission_denied()); return Err(permission_denied());
} }
@ -396,31 +397,31 @@ impl DenoPermissions {
self.allow_hrtime.is_allow() self.allow_hrtime.is_allow()
} }
pub fn revoke_run(&self) -> DenoResult<()> { pub fn revoke_run(&self) -> Result<(), ErrBox> {
self.allow_run.revoke(); self.allow_run.revoke();
Ok(()) Ok(())
} }
pub fn revoke_read(&self) -> DenoResult<()> { pub fn revoke_read(&self) -> Result<(), ErrBox> {
self.allow_read.revoke(); self.allow_read.revoke();
Ok(()) Ok(())
} }
pub fn revoke_write(&self) -> DenoResult<()> { pub fn revoke_write(&self) -> Result<(), ErrBox> {
self.allow_write.revoke(); self.allow_write.revoke();
Ok(()) Ok(())
} }
pub fn revoke_net(&self) -> DenoResult<()> { pub fn revoke_net(&self) -> Result<(), ErrBox> {
self.allow_net.revoke(); self.allow_net.revoke();
Ok(()) Ok(())
} }
pub fn revoke_env(&self) -> DenoResult<()> { pub fn revoke_env(&self) -> Result<(), ErrBox> {
self.allow_env.revoke(); self.allow_env.revoke();
Ok(()) Ok(())
} }
pub fn revoke_hrtime(&self) -> DenoResult<()> { pub fn revoke_hrtime(&self) -> Result<(), ErrBox> {
self.allow_hrtime.revoke(); self.allow_hrtime.revoke();
Ok(()) Ok(())
} }
@ -437,7 +438,7 @@ pub enum PromptResult {
impl PromptResult { impl PromptResult {
/// If value is any form of deny this will error with permission_denied /// If value is any form of deny this will error with permission_denied
pub fn check(&self) -> DenoResult<()> { pub fn check(&self) -> Result<(), ErrBox> {
match self { match self {
PromptResult::DenyOnce => Err(permission_denied()), PromptResult::DenyOnce => Err(permission_denied()),
PromptResult::DenyAlways => Err(permission_denied()), PromptResult::DenyAlways => Err(permission_denied()),
@ -457,7 +458,7 @@ impl fmt::Display for PromptResult {
} }
} }
fn permission_prompt(message: &str) -> DenoResult<PromptResult> { fn permission_prompt(message: &str) -> Result<PromptResult, ErrBox> {
let msg = format!("{} Deno requests {}. Grant? [a/y/n/d (a = allow always, y = allow once, n = deny once, d = deny always)] ", PERMISSION_EMOJI, message); let msg = format!("{} Deno requests {}. Grant? [a/y/n/d (a = allow always, y = allow once, n = deny once, d = deny always)] ", PERMISSION_EMOJI, message);
// print to stderr so that if deno is > to a file this is still displayed. // print to stderr so that if deno is > to a file this is still displayed.
eprint!("{}", Style::new().bold().paint(msg)); eprint!("{}", Style::new().bold().paint(msg));

View file

@ -1,12 +1,7 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
use rustyline;
use crate::msg::ErrorKind;
use std::error::Error;
use crate::deno_dir::DenoDir; use crate::deno_dir::DenoDir;
use crate::deno_error::new as deno_error; use deno::ErrBox;
use crate::deno_error::DenoResult; use rustyline;
use std::path::PathBuf; use std::path::PathBuf;
#[cfg(not(windows))] #[cfg(not(windows))]
@ -78,25 +73,25 @@ impl Repl {
.unwrap_or(()) .unwrap_or(())
} }
fn save_history(&mut self) -> DenoResult<()> { fn save_history(&mut self) -> Result<(), ErrBox> {
self self
.editor .editor
.save_history(&self.history_file.to_str().unwrap()) .save_history(&self.history_file.to_str().unwrap())
.map(|_| debug!("Saved REPL history to: {:?}", self.history_file)) .map(|_| debug!("Saved REPL history to: {:?}", self.history_file))
.map_err(|e| { .map_err(|e| {
eprintln!("Unable to save REPL history: {:?} {}", self.history_file, e); eprintln!("Unable to save REPL history: {:?} {}", self.history_file, e);
deno_error(ErrorKind::Other, e.description().to_string()) ErrBox::from(e)
}) })
} }
pub fn readline(&mut self, prompt: &str) -> DenoResult<String> { pub fn readline(&mut self, prompt: &str) -> Result<String, ErrBox> {
self self
.editor .editor
.readline(&prompt) .readline(&prompt)
.map(|line| { .map(|line| {
self.editor.add_history_entry(line.clone()); self.editor.add_history_entry(line.clone());
line line
}).map_err(|e| deno_error(ErrorKind::Other, e.description().to_string())) }).map_err(ErrBox::from)
// Forward error to TS side for processing // Forward error to TS side for processing
} }
} }

View file

@ -1,10 +1,9 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
use crate::deno_error;
use deno::ErrBox;
use futures::Async; use futures::Async;
use futures::Future; use futures::Future;
use futures::Poll; use futures::Poll;
use std::error::Error;
use std::fmt;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::net::ToSocketAddrs; use std::net::ToSocketAddrs;
@ -21,46 +20,23 @@ pub fn resolve_addr(address: &str) -> ResolveAddrFuture {
} }
} }
#[derive(Debug)]
pub enum ResolveAddrError {
Syntax,
Resolution(std::io::Error),
}
impl fmt::Display for ResolveAddrError {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.write_str(self.description())
}
}
impl Error for ResolveAddrError {
fn description(&self) -> &str {
match self {
ResolveAddrError::Syntax => "invalid address syntax",
ResolveAddrError::Resolution(e) => e.description(),
}
}
}
pub struct ResolveAddrFuture { pub struct ResolveAddrFuture {
address: String, address: String,
} }
impl Future for ResolveAddrFuture { impl Future for ResolveAddrFuture {
type Item = SocketAddr; type Item = SocketAddr;
type Error = ResolveAddrError; type Error = ErrBox;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
// The implementation of this is not actually async at the moment, // The implementation of this is not actually async at the moment,
// however we intend to use async DNS resolution in the future and // however we intend to use async DNS resolution in the future and
// so we expose this as a future instead of Result. // so we expose this as a future instead of Result.
match split(&self.address) { match split(&self.address) {
None => Err(ResolveAddrError::Syntax), None => Err(deno_error::invalid_address_syntax()),
Some(addr_port_pair) => { Some(addr_port_pair) => {
// I absolutely despise the .to_socket_addrs() API. // I absolutely despise the .to_socket_addrs() API.
let r = addr_port_pair let r = addr_port_pair.to_socket_addrs().map_err(ErrBox::from);
.to_socket_addrs()
.map_err(ResolveAddrError::Resolution);
r.and_then(|mut iter| match iter.next() { r.and_then(|mut iter| match iter.next() {
Some(a) => Ok(Async::Ready(a)), Some(a) => Ok(Async::Ready(a)),

View file

@ -10,13 +10,12 @@
use crate::deno_error; use crate::deno_error;
use crate::deno_error::bad_resource; use crate::deno_error::bad_resource;
use crate::deno_error::DenoError;
use crate::deno_error::DenoResult;
use crate::http_body::HttpBody; use crate::http_body::HttpBody;
use crate::repl::Repl; use crate::repl::Repl;
use crate::state::WorkerChannels; use crate::state::WorkerChannels;
use deno::Buf; use deno::Buf;
use deno::ErrBox;
use futures; use futures;
use futures::Future; use futures::Future;
@ -216,14 +215,14 @@ impl Resource {
} }
} }
pub fn shutdown(&mut self, how: Shutdown) -> Result<(), DenoError> { pub fn shutdown(&mut self, how: Shutdown) -> Result<(), ErrBox> {
let mut table = RESOURCE_TABLE.lock().unwrap(); let mut table = RESOURCE_TABLE.lock().unwrap();
let maybe_repr = table.get_mut(&self.rid); let maybe_repr = table.get_mut(&self.rid);
match maybe_repr { match maybe_repr {
None => panic!("bad rid"), None => panic!("bad rid"),
Some(repr) => match repr { Some(repr) => match repr {
Repr::TcpStream(ref mut f) => { Repr::TcpStream(ref mut f) => {
TcpStream::shutdown(f, how).map_err(DenoError::from) TcpStream::shutdown(f, how).map_err(ErrBox::from)
} }
_ => panic!("Cannot shutdown"), _ => panic!("Cannot shutdown"),
}, },
@ -366,15 +365,13 @@ pub struct WorkerReceiver {
// Invert the dumbness that tokio_process causes by making Child itself a future. // Invert the dumbness that tokio_process causes by making Child itself a future.
impl Future for WorkerReceiver { impl Future for WorkerReceiver {
type Item = Option<Buf>; type Item = Option<Buf>;
type Error = DenoError; type Error = ErrBox;
fn poll(&mut self) -> Poll<Option<Buf>, DenoError> { fn poll(&mut self) -> Poll<Option<Buf>, ErrBox> {
let mut table = RESOURCE_TABLE.lock().unwrap(); let mut table = RESOURCE_TABLE.lock().unwrap();
let maybe_repr = table.get_mut(&self.rid); let maybe_repr = table.get_mut(&self.rid);
match maybe_repr { match maybe_repr {
Some(Repr::Worker(ref mut wc)) => wc.1.poll().map_err(|err| { Some(Repr::Worker(ref mut wc)) => wc.1.poll().map_err(ErrBox::from),
deno_error::new(deno_error::ErrorKind::Other, err.to_string())
}),
_ => Err(bad_resource()), _ => Err(bad_resource()),
} }
} }
@ -391,15 +388,13 @@ pub struct WorkerReceiverStream {
// Invert the dumbness that tokio_process causes by making Child itself a future. // Invert the dumbness that tokio_process causes by making Child itself a future.
impl Stream for WorkerReceiverStream { impl Stream for WorkerReceiverStream {
type Item = Buf; type Item = Buf;
type Error = DenoError; type Error = ErrBox;
fn poll(&mut self) -> Poll<Option<Buf>, DenoError> { fn poll(&mut self) -> Poll<Option<Buf>, ErrBox> {
let mut table = RESOURCE_TABLE.lock().unwrap(); let mut table = RESOURCE_TABLE.lock().unwrap();
let maybe_repr = table.get_mut(&self.rid); let maybe_repr = table.get_mut(&self.rid);
match maybe_repr { match maybe_repr {
Some(Repr::Worker(ref mut wc)) => wc.1.poll().map_err(|err| { Some(Repr::Worker(ref mut wc)) => wc.1.poll().map_err(ErrBox::from),
deno_error::new(deno_error::ErrorKind::Other, err.to_string())
}),
_ => Err(bad_resource()), _ => Err(bad_resource()),
} }
} }
@ -462,19 +457,19 @@ pub struct ChildStatus {
// Invert the dumbness that tokio_process causes by making Child itself a future. // Invert the dumbness that tokio_process causes by making Child itself a future.
impl Future for ChildStatus { impl Future for ChildStatus {
type Item = ExitStatus; type Item = ExitStatus;
type Error = DenoError; type Error = ErrBox;
fn poll(&mut self) -> Poll<ExitStatus, DenoError> { fn poll(&mut self) -> Poll<ExitStatus, ErrBox> {
let mut table = RESOURCE_TABLE.lock().unwrap(); let mut table = RESOURCE_TABLE.lock().unwrap();
let maybe_repr = table.get_mut(&self.rid); let maybe_repr = table.get_mut(&self.rid);
match maybe_repr { match maybe_repr {
Some(Repr::Child(ref mut child)) => child.poll().map_err(DenoError::from), Some(Repr::Child(ref mut child)) => child.poll().map_err(ErrBox::from),
_ => Err(bad_resource()), _ => Err(bad_resource()),
} }
} }
} }
pub fn child_status(rid: ResourceId) -> DenoResult<ChildStatus> { pub fn child_status(rid: ResourceId) -> Result<ChildStatus, ErrBox> {
let mut table = RESOURCE_TABLE.lock().unwrap(); let mut table = RESOURCE_TABLE.lock().unwrap();
let maybe_repr = table.get_mut(&rid); let maybe_repr = table.get_mut(&rid);
match maybe_repr { match maybe_repr {
@ -483,7 +478,7 @@ pub fn child_status(rid: ResourceId) -> DenoResult<ChildStatus> {
} }
} }
pub fn get_repl(rid: ResourceId) -> DenoResult<Arc<Mutex<Repl>>> { pub fn get_repl(rid: ResourceId) -> Result<Arc<Mutex<Repl>>, ErrBox> {
let mut table = RESOURCE_TABLE.lock().unwrap(); let mut table = RESOURCE_TABLE.lock().unwrap();
let maybe_repr = table.get_mut(&rid); let maybe_repr = table.get_mut(&rid);
match maybe_repr { match maybe_repr {
@ -494,7 +489,7 @@ pub fn get_repl(rid: ResourceId) -> DenoResult<Arc<Mutex<Repl>>> {
// TODO: revamp this after the following lands: // TODO: revamp this after the following lands:
// https://github.com/tokio-rs/tokio/pull/785 // https://github.com/tokio-rs/tokio/pull/785
pub fn get_file(rid: ResourceId) -> DenoResult<std::fs::File> { pub fn get_file(rid: ResourceId) -> Result<std::fs::File, ErrBox> {
let mut table = RESOURCE_TABLE.lock().unwrap(); let mut table = RESOURCE_TABLE.lock().unwrap();
// We take ownership of File here. // We take ownership of File here.
// It is put back below while still holding the lock. // It is put back below while still holding the lock.
@ -516,7 +511,7 @@ pub fn get_file(rid: ResourceId) -> DenoResult<std::fs::File> {
table.insert(rid, Repr::FsFile(tokio_fs::File::from_std(std_file))); table.insert(rid, Repr::FsFile(tokio_fs::File::from_std(std_file)));
if maybe_std_file_copy.is_err() { if maybe_std_file_copy.is_err() {
return Err(DenoError::from(maybe_std_file_copy.unwrap_err())); return Err(ErrBox::from(maybe_std_file_copy.unwrap_err()));
} }
let std_file_copy = maybe_std_file_copy.unwrap(); let std_file_copy = maybe_std_file_copy.unwrap();
@ -537,23 +532,25 @@ pub fn seek(
resource: Resource, resource: Resource,
offset: i32, offset: i32,
whence: u32, whence: u32,
) -> Box<dyn Future<Item = (), Error = DenoError> + Send> { ) -> Box<dyn Future<Item = (), Error = ErrBox> + Send> {
// Translate seek mode to Rust repr. // Translate seek mode to Rust repr.
let seek_from = match whence { let seek_from = match whence {
0 => SeekFrom::Start(offset as u64), 0 => SeekFrom::Start(offset as u64),
1 => SeekFrom::Current(i64::from(offset)), 1 => SeekFrom::Current(i64::from(offset)),
2 => SeekFrom::End(i64::from(offset)), 2 => SeekFrom::End(i64::from(offset)),
_ => { _ => {
return Box::new(futures::future::err(deno_error::new( return Box::new(futures::future::err(
deno_error::DenoError::new(
deno_error::ErrorKind::InvalidSeekMode, deno_error::ErrorKind::InvalidSeekMode,
format!("Invalid seek mode: {}", whence), format!("Invalid seek mode: {}", whence),
))); ).into(),
));
} }
}; };
match get_file(resource.rid) { match get_file(resource.rid) {
Ok(mut file) => Box::new(futures::future::lazy(move || { Ok(mut file) => Box::new(futures::future::lazy(move || {
let result = file.seek(seek_from).map(|_| {}).map_err(DenoError::from); let result = file.seek(seek_from).map(|_| {}).map_err(ErrBox::from);
futures::future::result(result) futures::future::result(result)
})), })),
Err(err) => Box::new(futures::future::err(err)), Err(err) => Box::new(futures::future::err(err)),

View file

@ -10,11 +10,10 @@ use std::fmt;
use std::io::prelude::*; use std::io::prelude::*;
use atty; use atty;
use deno::ErrBox;
use termcolor::Color::{Cyan, Green, Red, Yellow}; use termcolor::Color::{Cyan, Green, Red, Yellow};
use termcolor::{self, Color, ColorSpec, StandardStream, WriteColor}; use termcolor::{self, Color, ColorSpec, StandardStream, WriteColor};
use crate::deno_error::DenoResult;
/// The requested verbosity of output. /// The requested verbosity of output.
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum Verbosity { pub enum Verbosity {
@ -116,7 +115,7 @@ impl Shell {
message: Option<&dyn fmt::Display>, message: Option<&dyn fmt::Display>,
color: Color, color: Color,
justified: bool, justified: bool,
) -> DenoResult<()> { ) -> Result<(), ErrBox> {
match self.verbosity { match self.verbosity {
Verbosity::Quiet => Ok(()), Verbosity::Quiet => Ok(()),
_ => { _ => {
@ -171,7 +170,7 @@ impl Shell {
} }
/// Shortcut to right-align and color green a status message. /// Shortcut to right-align and color green a status message.
pub fn status<T, U>(&mut self, status: T, message: U) -> DenoResult<()> pub fn status<T, U>(&mut self, status: T, message: U) -> Result<(), ErrBox>
where where
T: fmt::Display, T: fmt::Display,
U: fmt::Display, U: fmt::Display,
@ -179,7 +178,7 @@ impl Shell {
self.print(&status, Some(&message), Green, false) self.print(&status, Some(&message), Green, false)
} }
pub fn status_header<T>(&mut self, status: T) -> DenoResult<()> pub fn status_header<T>(&mut self, status: T) -> Result<(), ErrBox>
where where
T: fmt::Display, T: fmt::Display,
{ {
@ -192,7 +191,7 @@ impl Shell {
status: T, status: T,
message: U, message: U,
color: Color, color: Color,
) -> DenoResult<()> ) -> Result<(), ErrBox>
where where
T: fmt::Display, T: fmt::Display,
U: fmt::Display, U: fmt::Display,
@ -201,9 +200,9 @@ impl Shell {
} }
/// Runs the callback only if we are in verbose mode. /// Runs the callback only if we are in verbose mode.
pub fn verbose<F>(&mut self, mut callback: F) -> DenoResult<()> pub fn verbose<F>(&mut self, mut callback: F) -> Result<(), ErrBox>
where where
F: FnMut(&mut Shell) -> DenoResult<()>, F: FnMut(&mut Shell) -> Result<(), ErrBox>,
{ {
match self.verbosity { match self.verbosity {
Verbosity::Verbose => callback(self), Verbosity::Verbose => callback(self),
@ -212,9 +211,9 @@ impl Shell {
} }
/// Runs the callback if we are not in verbose mode. /// Runs the callback if we are not in verbose mode.
pub fn concise<F>(&mut self, mut callback: F) -> DenoResult<()> pub fn concise<F>(&mut self, mut callback: F) -> Result<(), ErrBox>
where where
F: FnMut(&mut Shell) -> DenoResult<()>, F: FnMut(&mut Shell) -> Result<(), ErrBox>,
{ {
match self.verbosity { match self.verbosity {
Verbosity::Verbose => Ok(()), Verbosity::Verbose => Ok(()),
@ -223,12 +222,12 @@ impl Shell {
} }
/// Prints a red 'error' message. /// Prints a red 'error' message.
pub fn error<T: fmt::Display>(&mut self, message: T) -> DenoResult<()> { pub fn error<T: fmt::Display>(&mut self, message: T) -> Result<(), ErrBox> {
self.print(&"error:", Some(&message), Red, false) self.print(&"error:", Some(&message), Red, false)
} }
/// Prints an amber 'warning' message. /// Prints an amber 'warning' message.
pub fn warn<T: fmt::Display>(&mut self, message: T) -> DenoResult<()> { pub fn warn<T: fmt::Display>(&mut self, message: T) -> Result<(), ErrBox> {
match self.verbosity { match self.verbosity {
Verbosity::Quiet => Ok(()), Verbosity::Quiet => Ok(()),
_ => self.print(&"warning:", Some(&message), Yellow, false), _ => self.print(&"warning:", Some(&message), Yellow, false),
@ -246,7 +245,10 @@ impl Shell {
} }
/// Updates the color choice (always, never, or auto) from a string.. /// Updates the color choice (always, never, or auto) from a string..
pub fn set_color_choice(&mut self, color: Option<&str>) -> DenoResult<()> { pub fn set_color_choice(
&mut self,
color: Option<&str>,
) -> Result<(), ErrBox> {
if let ShellOut::Stream { if let ShellOut::Stream {
ref mut stream, ref mut stream,
ref mut color_choice, ref mut color_choice,
@ -291,7 +293,7 @@ impl Shell {
} }
/// Prints a message and translates ANSI escape code into console colors. /// Prints a message and translates ANSI escape code into console colors.
pub fn print_ansi(&mut self, message: &[u8]) -> DenoResult<()> { pub fn print_ansi(&mut self, message: &[u8]) -> Result<(), ErrBox> {
if self.needs_clear { if self.needs_clear {
self.err_erase_line(); self.err_erase_line();
} }
@ -323,7 +325,7 @@ impl ShellOut {
message: Option<&dyn fmt::Display>, message: Option<&dyn fmt::Display>,
color: Color, color: Color,
justified: bool, justified: bool,
) -> DenoResult<()> { ) -> Result<(), ErrBox> {
match *self { match *self {
ShellOut::Stream { ref mut stream, .. } => { ShellOut::Stream { ref mut stream, .. } => {
stream.reset()?; stream.reset()?;

View file

@ -1,19 +1,15 @@
#[cfg(unix)] use deno::ErrBox;
use nix::sys::signal::{kill as unix_kill, Signal};
#[cfg(unix)]
use nix::unistd::Pid;
use crate::deno_error::DenoResult;
#[cfg(unix)] #[cfg(unix)]
pub fn kill(pid: i32, signo: i32) -> DenoResult<()> { pub fn kill(pid: i32, signo: i32) -> Result<(), ErrBox> {
use crate::deno_error::DenoError; use nix::sys::signal::{kill as unix_kill, Signal};
use nix::unistd::Pid;
let sig = Signal::from_c_int(signo)?; let sig = Signal::from_c_int(signo)?;
unix_kill(Pid::from_raw(pid), Option::Some(sig)).map_err(DenoError::from) unix_kill(Pid::from_raw(pid), Option::Some(sig)).map_err(ErrBox::from)
} }
#[cfg(not(unix))] #[cfg(not(unix))]
pub fn kill(_pid: i32, _signal: i32) -> DenoResult<()> { pub fn kill(_pid: i32, _signal: i32) -> Result<(), ErrBox> {
// NOOP // NOOP
// TODO: implement this for windows // TODO: implement this for windows
Ok(()) Ok(())

View file

@ -1,7 +1,7 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
//! This mod provides functions to remap a deno::JSError based on a source map //! This mod provides functions to remap a deno::V8Exception based on a source map
use deno::JSError;
use deno::StackFrame; use deno::StackFrame;
use deno::V8Exception;
use serde_json; use serde_json;
use source_map_mappings::parse_mappings; use source_map_mappings::parse_mappings;
use source_map_mappings::Bias; use source_map_mappings::Bias;
@ -90,36 +90,36 @@ fn builtin_source_map(script_name: &str) -> Option<Vec<u8>> {
} }
} }
/// Apply a source map to a JSError, returning a JSError where the filenames, /// Apply a source map to a V8Exception, returning a V8Exception where the filenames,
/// the lines and the columns point to their original source location, not their /// the lines and the columns point to their original source location, not their
/// transpiled location if applicable. /// transpiled location if applicable.
pub fn apply_source_map<G: SourceMapGetter>( pub fn apply_source_map<G: SourceMapGetter>(
js_error: &JSError, v8_exception: &V8Exception,
getter: &G, getter: &G,
) -> JSError { ) -> V8Exception {
let mut mappings_map: CachedMaps = HashMap::new(); let mut mappings_map: CachedMaps = HashMap::new();
let mut frames = Vec::<StackFrame>::new(); let mut frames = Vec::<StackFrame>::new();
for frame in &js_error.frames { for frame in &v8_exception.frames {
let f = frame_apply_source_map(&frame, &mut mappings_map, getter); let f = frame_apply_source_map(&frame, &mut mappings_map, getter);
frames.push(f); frames.push(f);
} }
let (script_resource_name, line_number, start_column) = let (script_resource_name, line_number, start_column) =
get_maybe_orig_position( get_maybe_orig_position(
js_error.script_resource_name.clone(), v8_exception.script_resource_name.clone(),
js_error.line_number, v8_exception.line_number,
js_error.start_column, v8_exception.start_column,
&mut mappings_map, &mut mappings_map,
getter, getter,
); );
// It is better to just move end_column to be the same distance away from // It is better to just move end_column to be the same distance away from
// start column because sometimes the code point is not available in the // start column because sometimes the code point is not available in the
// source file map. // source file map.
let end_column = match js_error.end_column { let end_column = match v8_exception.end_column {
Some(ec) => { Some(ec) => {
if start_column.is_some() { if start_column.is_some() {
Some(ec - (js_error.start_column.unwrap() - start_column.unwrap())) Some(ec - (v8_exception.start_column.unwrap() - start_column.unwrap()))
} else { } else {
None None
} }
@ -128,22 +128,22 @@ pub fn apply_source_map<G: SourceMapGetter>(
}; };
// if there is a source line that we might be different in the source file, we // if there is a source line that we might be different in the source file, we
// will go fetch it from the getter // will go fetch it from the getter
let source_line = if js_error.source_line.is_some() let source_line = if v8_exception.source_line.is_some()
&& script_resource_name.is_some() && script_resource_name.is_some()
&& line_number.is_some() && line_number.is_some()
{ {
getter.get_source_line( getter.get_source_line(
&js_error.script_resource_name.clone().unwrap(), &v8_exception.script_resource_name.clone().unwrap(),
line_number.unwrap() as usize, line_number.unwrap() as usize,
) )
} else { } else {
js_error.source_line.clone() v8_exception.source_line.clone()
}; };
JSError { V8Exception {
message: js_error.message.clone(), message: v8_exception.message.clone(),
frames, frames,
error_level: js_error.error_level, error_level: v8_exception.error_level,
source_line, source_line,
script_resource_name, script_resource_name,
line_number, line_number,
@ -151,8 +151,8 @@ pub fn apply_source_map<G: SourceMapGetter>(
end_column, end_column,
// These are difficult to map to their original position and they are not // These are difficult to map to their original position and they are not
// currently used in any output, so we don't remap them. // currently used in any output, so we don't remap them.
start_position: js_error.start_position, start_position: v8_exception.start_position,
end_position: js_error.end_position, end_position: v8_exception.end_position,
} }
} }
@ -297,8 +297,8 @@ mod tests {
} }
} }
fn error1() -> JSError { fn error1() -> V8Exception {
JSError { V8Exception {
message: "Error: foo bar".to_string(), message: "Error: foo bar".to_string(),
source_line: None, source_line: None,
script_resource_name: None, script_resource_name: None,
@ -341,11 +341,11 @@ mod tests {
} }
#[test] #[test]
fn js_error_apply_source_map_1() { fn v8_exception_apply_source_map_1() {
let e = error1(); let e = error1();
let getter = MockSourceMapGetter {}; let getter = MockSourceMapGetter {};
let actual = apply_source_map(&e, &getter); let actual = apply_source_map(&e, &getter);
let expected = JSError { let expected = V8Exception {
message: "Error: foo bar".to_string(), message: "Error: foo bar".to_string(),
source_line: None, source_line: None,
script_resource_name: None, script_resource_name: None,
@ -389,8 +389,8 @@ mod tests {
} }
#[test] #[test]
fn js_error_apply_source_map_2() { fn v8_exception_apply_source_map_2() {
let e = JSError { let e = V8Exception {
message: "TypeError: baz".to_string(), message: "TypeError: baz".to_string(),
source_line: None, source_line: None,
script_resource_name: None, script_resource_name: None,
@ -419,8 +419,8 @@ mod tests {
} }
#[test] #[test]
fn js_error_apply_source_map_line() { fn v8_exception_apply_source_map_line() {
let e = JSError { let e = V8Exception {
message: "TypeError: baz".to_string(), message: "TypeError: baz".to_string(),
source_line: Some("foo".to_string()), source_line: Some("foo".to_string()),
script_resource_name: Some("foo_bar.ts".to_string()), script_resource_name: Some("foo_bar.ts".to_string()),

View file

@ -2,8 +2,6 @@
use crate::compiler::compile_async; use crate::compiler::compile_async;
use crate::compiler::ModuleMetaData; use crate::compiler::ModuleMetaData;
use crate::deno_dir; use crate::deno_dir;
use crate::deno_error::DenoError;
use crate::deno_error::DenoResult;
use crate::flags; use crate::flags;
use crate::global_timer::GlobalTimer; use crate::global_timer::GlobalTimer;
use crate::import_map::ImportMap; use crate::import_map::ImportMap;
@ -16,6 +14,7 @@ use crate::resources::ResourceId;
use crate::worker::Worker; use crate::worker::Worker;
use deno::Buf; use deno::Buf;
use deno::CoreOp; use deno::CoreOp;
use deno::ErrBox;
use deno::Loader; use deno::Loader;
use deno::ModuleSpecifier; use deno::ModuleSpecifier;
use deno::PinnedBuf; use deno::PinnedBuf;
@ -118,7 +117,7 @@ impl ThreadSafeState {
pub fn fetch_module_meta_data_and_maybe_compile_async( pub fn fetch_module_meta_data_and_maybe_compile_async(
state: &ThreadSafeState, state: &ThreadSafeState,
module_specifier: &ModuleSpecifier, module_specifier: &ModuleSpecifier,
) -> impl Future<Item = ModuleMetaData, Error = DenoError> { ) -> impl Future<Item = ModuleMetaData, Error = ErrBox> {
let state_ = state.clone(); let state_ = state.clone();
let use_cache = let use_cache =
!state_.flags.reload || state_.has_compiled(&module_specifier.to_string()); !state_.flags.reload || state_.has_compiled(&module_specifier.to_string());
@ -150,14 +149,12 @@ pub fn fetch_module_meta_data_and_maybe_compile_async(
} }
impl Loader for ThreadSafeState { impl Loader for ThreadSafeState {
type Error = DenoError;
fn resolve( fn resolve(
&self, &self,
specifier: &str, specifier: &str,
referrer: &str, referrer: &str,
is_root: bool, is_root: bool,
) -> Result<ModuleSpecifier, Self::Error> { ) -> Result<ModuleSpecifier, ErrBox> {
if !is_root { if !is_root {
if let Some(import_map) = &self.import_map { if let Some(import_map) = &self.import_map {
let result = import_map.resolve(specifier, referrer)?; let result = import_map.resolve(specifier, referrer)?;
@ -167,15 +164,14 @@ impl Loader for ThreadSafeState {
} }
} }
ModuleSpecifier::resolve_import(specifier, referrer) ModuleSpecifier::resolve_import(specifier, referrer).map_err(ErrBox::from)
.map_err(DenoError::from)
} }
/// Given an absolute url, load its source code. /// Given an absolute url, load its source code.
fn load( fn load(
&self, &self,
module_specifier: &ModuleSpecifier, module_specifier: &ModuleSpecifier,
) -> Box<deno::SourceCodeInfoFuture<Self::Error>> { ) -> Box<deno::SourceCodeInfoFuture> {
self.metrics.resolve_count.fetch_add(1, Ordering::SeqCst); self.metrics.resolve_count.fetch_add(1, Ordering::SeqCst);
Box::new( Box::new(
fetch_module_meta_data_and_maybe_compile_async(self, module_specifier) fetch_module_meta_data_and_maybe_compile_async(self, module_specifier)
@ -324,32 +320,32 @@ impl ThreadSafeState {
} }
#[inline] #[inline]
pub fn check_read(&self, filename: &str) -> DenoResult<()> { pub fn check_read(&self, filename: &str) -> Result<(), ErrBox> {
self.permissions.check_read(filename) self.permissions.check_read(filename)
} }
#[inline] #[inline]
pub fn check_write(&self, filename: &str) -> DenoResult<()> { pub fn check_write(&self, filename: &str) -> Result<(), ErrBox> {
self.permissions.check_write(filename) self.permissions.check_write(filename)
} }
#[inline] #[inline]
pub fn check_env(&self) -> DenoResult<()> { pub fn check_env(&self) -> Result<(), ErrBox> {
self.permissions.check_env() self.permissions.check_env()
} }
#[inline] #[inline]
pub fn check_net(&self, host_and_port: &str) -> DenoResult<()> { pub fn check_net(&self, host_and_port: &str) -> Result<(), ErrBox> {
self.permissions.check_net(host_and_port) self.permissions.check_net(host_and_port)
} }
#[inline] #[inline]
pub fn check_net_url(&self, url: url::Url) -> DenoResult<()> { pub fn check_net_url(&self, url: url::Url) -> Result<(), ErrBox> {
self.permissions.check_net_url(url) self.permissions.check_net_url(url)
} }
#[inline] #[inline]
pub fn check_run(&self) -> DenoResult<()> { pub fn check_run(&self) -> Result<(), ErrBox> {
self.permissions.check_run() self.permissions.check_run()
} }

View file

@ -1,8 +1,9 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
use crate::deno_error::DenoError; use crate::fmt_errors::JSError;
use crate::state::ThreadSafeState; use crate::state::ThreadSafeState;
use crate::tokio_util; use crate::tokio_util;
use deno; use deno;
use deno::ErrBox;
use deno::ModuleSpecifier; use deno::ModuleSpecifier;
use deno::StartupData; use deno::StartupData;
use futures::Async; use futures::Async;
@ -24,19 +25,23 @@ impl Worker {
startup_data: StartupData, startup_data: StartupData,
state: ThreadSafeState, state: ThreadSafeState,
) -> Worker { ) -> Worker {
let state_ = state.clone();
let isolate = Arc::new(Mutex::new(deno::Isolate::new(startup_data, false))); let isolate = Arc::new(Mutex::new(deno::Isolate::new(startup_data, false)));
{ {
let mut i = isolate.lock().unwrap(); let mut i = isolate.lock().unwrap();
let state_ = state.clone();
i.set_dispatch(move |control_buf, zero_copy_buf| { i.set_dispatch(move |control_buf, zero_copy_buf| {
state_.dispatch(control_buf, zero_copy_buf) state_.dispatch(control_buf, zero_copy_buf)
}); });
let state_ = state.clone();
i.set_js_error_create(move |v8_exception| {
JSError::from_v8_exception(v8_exception, &state_.dir)
})
} }
Self { isolate, state } Self { isolate, state }
} }
/// Same as execute2() but the filename defaults to "<anonymous>". /// Same as execute2() but the filename defaults to "<anonymous>".
pub fn execute(&mut self, js_source: &str) -> Result<(), DenoError> { pub fn execute(&mut self, js_source: &str) -> Result<(), ErrBox> {
self.execute2("<anonymous>", js_source) self.execute2("<anonymous>", js_source)
} }
@ -46,12 +51,9 @@ impl Worker {
&mut self, &mut self,
js_filename: &str, js_filename: &str,
js_source: &str, js_source: &str,
) -> Result<(), DenoError> { ) -> Result<(), ErrBox> {
let mut isolate = self.isolate.lock().unwrap(); let mut isolate = self.isolate.lock().unwrap();
match isolate.execute(js_filename, js_source) { isolate.execute(js_filename, js_source)
Ok(_) => Ok(()),
Err(err) => Err(DenoError::from(err)),
}
} }
/// Executes the provided JavaScript module. /// Executes the provided JavaScript module.
@ -59,9 +61,8 @@ impl Worker {
&mut self, &mut self,
module_specifier: &ModuleSpecifier, module_specifier: &ModuleSpecifier,
is_prefetch: bool, is_prefetch: bool,
) -> impl Future<Item = (), Error = DenoError> { ) -> impl Future<Item = (), Error = ErrBox> {
let worker = self.clone(); let worker = self.clone();
let worker_ = worker.clone();
let loader = self.state.clone(); let loader = self.state.clone();
let isolate = self.isolate.clone(); let isolate = self.isolate.clone();
let modules = self.state.modules.clone(); let modules = self.state.modules.clone();
@ -71,28 +72,13 @@ impl Worker {
isolate, isolate,
modules, modules,
); );
recursive_load recursive_load.and_then(move |id| -> Result<(), ErrBox> {
.and_then(move |id| -> Result<(), deno::JSErrorOr<DenoError>> {
worker.state.progress.done(); worker.state.progress.done();
if is_prefetch { if is_prefetch {
Ok(()) Ok(())
} else { } else {
let mut isolate = worker.isolate.lock().unwrap(); let mut isolate = worker.isolate.lock().unwrap();
let result = isolate.mod_evaluate(id); isolate.mod_evaluate(id)
if let Err(err) = result {
Err(deno::JSErrorOr::JSError(err))
} else {
Ok(())
}
}
}).map_err(move |err| {
worker_.state.progress.done();
// Convert to DenoError AND apply_source_map.
match err {
deno::JSErrorOr::JSError(err) => {
worker_.apply_source_map(DenoError::from(err))
}
deno::JSErrorOr::Other(err) => err,
} }
}) })
} }
@ -102,32 +88,24 @@ impl Worker {
&mut self, &mut self,
module_specifier: &ModuleSpecifier, module_specifier: &ModuleSpecifier,
is_prefetch: bool, is_prefetch: bool,
) -> Result<(), DenoError> { ) -> Result<(), ErrBox> {
tokio_util::block_on(self.execute_mod_async(module_specifier, is_prefetch)) tokio_util::block_on(self.execute_mod_async(module_specifier, is_prefetch))
} }
/// Applies source map to the error.
fn apply_source_map(&self, err: DenoError) -> DenoError {
err.apply_source_map(&self.state.dir)
}
} }
impl Future for Worker { impl Future for Worker {
type Item = (); type Item = ();
type Error = DenoError; type Error = ErrBox;
fn poll(&mut self) -> Result<Async<()>, Self::Error> { fn poll(&mut self) -> Result<Async<()>, ErrBox> {
let mut isolate = self.isolate.lock().unwrap(); let mut isolate = self.isolate.lock().unwrap();
isolate isolate.poll()
.poll()
.map_err(|err| self.apply_source_map(DenoError::from(err)))
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::deno_error::err_check;
use crate::flags; use crate::flags;
use crate::ops::op_selector_std; use crate::ops::op_selector_std;
use crate::progress::Progress; use crate::progress::Progress;
@ -210,7 +188,7 @@ mod tests {
startup_data::deno_isolate_init(), startup_data::deno_isolate_init(),
state, state,
); );
err_check(worker.execute("denoMain()")); worker.execute("denoMain()").unwrap();
let result = worker.execute_mod(&module_specifier, false); let result = worker.execute_mod(&module_specifier, false);
if let Err(err) = result { if let Err(err) = result {
eprintln!("execute_mod err {:?}", err); eprintln!("execute_mod err {:?}", err);
@ -231,8 +209,8 @@ mod tests {
]); ]);
let mut worker = let mut worker =
Worker::new("TEST".to_string(), startup_data::deno_isolate_init(), state); Worker::new("TEST".to_string(), startup_data::deno_isolate_init(), state);
err_check(worker.execute("denoMain()")); worker.execute("denoMain()").unwrap();
err_check(worker.execute("workerMain()")); worker.execute("workerMain()").unwrap();
worker worker
} }
@ -253,7 +231,7 @@ mod tests {
console.log("after postMessage"); console.log("after postMessage");
} }
"#; "#;
err_check(worker.execute(source)); worker.execute(source).unwrap();
let resource = worker.state.resource.clone(); let resource = worker.state.resource.clone();
let resource_ = resource.clone(); let resource_ = resource.clone();
@ -261,7 +239,7 @@ mod tests {
tokio::spawn(lazy(move || { tokio::spawn(lazy(move || {
worker.then(move |r| -> Result<(), ()> { worker.then(move |r| -> Result<(), ()> {
resource_.close(); resource_.close();
err_check(r); r.unwrap();
Ok(()) Ok(())
}) })
})); }));
@ -291,9 +269,9 @@ mod tests {
fn removed_from_resource_table_on_close() { fn removed_from_resource_table_on_close() {
tokio_util::init(|| { tokio_util::init(|| {
let mut worker = create_test_worker(); let mut worker = create_test_worker();
err_check( worker
worker.execute("onmessage = () => { delete window.onmessage; }"), .execute("onmessage = () => { delete window.onmessage; }")
); .unwrap();
let resource = worker.state.resource.clone(); let resource = worker.state.resource.clone();
let rid = resource.rid; let rid = resource.rid;
@ -302,7 +280,7 @@ mod tests {
.then(move |r| -> Result<(), ()> { .then(move |r| -> Result<(), ()> {
resource.close(); resource.close();
println!("workers.rs after resource close"); println!("workers.rs after resource close");
err_check(r); r.unwrap();
Ok(()) Ok(())
}).shared(); }).shared();

68
core/any_error.rs Normal file
View file

@ -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)
}
}

View file

@ -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 { if let Err(e) = r {
panic!(e.to_string()); panic!(e.to_string());
} }

View file

@ -4,7 +4,9 @@
// isolate to keep the Isolate struct from becoming too bloating for users who // isolate to keep the Isolate struct from becoming too bloating for users who
// do not need asynchronous module loading. // 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;
use crate::libdeno::deno_buf; use crate::libdeno::deno_buf;
use crate::libdeno::deno_dyn_import_id; 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>; pub type DynImportFuture = Box<dyn Future<Item = deno_mod, Error = ()> + Send>;
type DynImportFn = dyn Fn(&str, &str) -> DynImportFuture; 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 /// Wraps DynImportFuture to include the deno_dyn_import_id, so that it doesn't
/// need to be exposed. /// need to be exposed.
struct DynImport { struct DynImport {
@ -114,6 +118,7 @@ pub struct Isolate {
shared_libdeno_isolate: Arc<Mutex<Option<*const libdeno::isolate>>>, shared_libdeno_isolate: Arc<Mutex<Option<*const libdeno::isolate>>>,
dispatch: Option<Arc<CoreDispatchFn>>, dispatch: Option<Arc<CoreDispatchFn>>,
dyn_import: Option<Arc<DynImportFn>>, dyn_import: Option<Arc<DynImportFn>>,
js_error_create: Arc<JSErrorCreateFn>,
needs_init: bool, needs_init: bool,
shared: SharedQueue, shared: SharedQueue,
pending_ops: FuturesUnordered<CoreOpAsyncFuture>, pending_ops: FuturesUnordered<CoreOpAsyncFuture>,
@ -178,6 +183,7 @@ impl Isolate {
shared_libdeno_isolate: Arc::new(Mutex::new(Some(libdeno_isolate))), shared_libdeno_isolate: Arc::new(Mutex::new(Some(libdeno_isolate))),
dispatch: None, dispatch: None,
dyn_import: None, dyn_import: None,
js_error_create: Arc::new(CoreJSError::from_v8_exception),
shared, shared,
needs_init, needs_init,
pending_ops: FuturesUnordered::new(), pending_ops: FuturesUnordered::new(),
@ -204,6 +210,16 @@ impl Isolate {
self.dyn_import = Some(Arc::new(f)); 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. /// Get a thread safe handle on the isolate.
pub fn shared_isolate_handle(&mut self) -> IsolateHandle { pub fn shared_isolate_handle(&mut self) -> IsolateHandle {
IsolateHandle { IsolateHandle {
@ -307,11 +323,16 @@ impl Isolate {
self as *const _ as *const c_void 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( pub fn execute(
&mut self, &mut self,
js_filename: &str, js_filename: &str,
js_source: &str, js_source: &str,
) -> Result<(), JSError> { ) -> Result<(), ErrBox> {
self.shared_init(); self.shared_init();
let filename = CString::new(js_filename).unwrap(); let filename = CString::new(js_filename).unwrap();
let source = CString::new(js_source).unwrap(); let source = CString::new(js_source).unwrap();
@ -323,22 +344,20 @@ impl Isolate {
source.as_ptr(), source.as_ptr(),
) )
}; };
if let Some(err) = self.last_exception() { self.check_last_exception()
return Err(err);
}
Ok(())
} }
fn last_exception(&self) -> Option<JSError> { fn check_last_exception(&self) -> Result<(), ErrBox> {
let ptr = unsafe { libdeno::deno_last_exception(self.libdeno_isolate) }; let ptr = unsafe { libdeno::deno_last_exception(self.libdeno_isolate) };
if ptr.is_null() { if ptr.is_null() {
None Ok(())
} else { } else {
let js_error_create = &*self.js_error_create;
let cstr = unsafe { CStr::from_ptr(ptr) }; let cstr = unsafe { CStr::from_ptr(ptr) };
let v8_exception = cstr.to_str().unwrap(); let json_str = cstr.to_str().unwrap();
debug!("v8_exception\n{}\n", v8_exception); let v8_exception = V8Exception::from_json(json_str).unwrap();
let js_error = JSError::from_v8_exception(v8_exception).unwrap(); let js_error = js_error_create(v8_exception);
Some(js_error) 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 { let buf = match maybe_buf {
None => deno_buf::empty(), None => deno_buf::empty(),
Some(r) => deno_buf::from(r), Some(r) => deno_buf::from(r),
@ -356,11 +375,7 @@ impl Isolate {
unsafe { unsafe {
libdeno::deno_respond(self.libdeno_isolate, self.as_raw_ptr(), buf) libdeno::deno_respond(self.libdeno_isolate, self.as_raw_ptr(), buf)
} }
if let Some(err) = self.last_exception() { self.check_last_exception()
Err(err)
} else {
Ok(())
}
} }
/// Low-level module creation. /// Low-level module creation.
@ -369,7 +384,7 @@ impl Isolate {
main: bool, main: bool,
name: &str, name: &str,
source: &str, source: &str,
) -> Result<deno_mod, JSError> { ) -> Result<deno_mod, ErrBox> {
let name_ = CString::new(name.to_string()).unwrap(); let name_ = CString::new(name.to_string()).unwrap();
let name_ptr = name_.as_ptr() as *const libc::c_char; let name_ptr = name_.as_ptr() as *const libc::c_char;
@ -379,12 +394,11 @@ impl Isolate {
let id = unsafe { let id = unsafe {
libdeno::deno_mod_new(self.libdeno_isolate, main, name_ptr, source_ptr) 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> { pub fn mod_get_imports(&self, id: deno_mod) -> Vec<String> {
@ -402,23 +416,29 @@ impl Isolate {
out 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) }; let snapshot = unsafe { libdeno::deno_snapshot_new(self.libdeno_isolate) };
if let Some(js_error) = self.last_exception() { match self.check_last_exception() {
Ok(..) => Ok(snapshot),
Err(err) => {
assert_eq!(snapshot.data_ptr, null()); assert_eq!(snapshot.data_ptr, null());
assert_eq!(snapshot.data_len, 0); assert_eq!(snapshot.data_len, 0);
return Err(js_error); Err(err)
}
} }
assert_ne!(snapshot.data_ptr, null());
assert_ne!(snapshot.data_len, 0);
Ok(snapshot)
} }
fn dyn_import_done( fn dyn_import_done(
&self, &self,
id: libdeno::deno_dyn_import_id, id: libdeno::deno_dyn_import_id,
mod_id: deno_mod, mod_id: deno_mod,
) -> Result<(), JSError> { ) -> Result<(), ErrBox> {
debug!("dyn_import_done {} {}", id, mod_id); debug!("dyn_import_done {} {}", id, mod_id);
unsafe { unsafe {
libdeno::deno_dyn_import( libdeno::deno_dyn_import(
@ -428,11 +448,10 @@ impl Isolate {
mod_id, mod_id,
) )
}; };
if let Some(js_error) = self.last_exception() { self.check_last_exception().map_err(|err| {
assert_eq!(id, 0); assert_eq!(id, 0);
return Err(js_error); err
} })
Ok(())
} }
} }
@ -458,11 +477,16 @@ impl<'a> ResolveContext<'a> {
} }
impl Isolate { 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( pub fn mod_instantiate(
&mut self, &mut self,
id: deno_mod, id: deno_mod,
resolve_fn: &mut ResolveFn, resolve_fn: &mut ResolveFn,
) -> Result<(), JSError> { ) -> Result<(), ErrBox> {
let libdeno_isolate = self.libdeno_isolate; let libdeno_isolate = self.libdeno_isolate;
let mut ctx = ResolveContext { resolve_fn }; let mut ctx = ResolveContext { resolve_fn };
unsafe { unsafe {
@ -473,11 +497,7 @@ impl Isolate {
Self::resolve_cb, Self::resolve_cb,
) )
}; };
self.check_last_exception()
if let Some(js_error) = self.last_exception() {
return Err(js_error);
}
Ok(())
} }
/// Called during mod_instantiate() only. /// Called during mod_instantiate() only.
@ -494,15 +514,17 @@ impl Isolate {
resolve_fn(specifier, referrer) 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(); self.shared_init();
unsafe { unsafe {
libdeno::deno_mod_evaluate(self.libdeno_isolate, self.as_raw_ptr(), id) libdeno::deno_mod_evaluate(self.libdeno_isolate, self.as_raw_ptr(), id)
}; };
if let Some(js_error) = self.last_exception() { self.check_last_exception()
return Err(js_error);
}
Ok(())
} }
} }
@ -525,9 +547,9 @@ impl Drop for LockerScope {
impl Future for Isolate { impl Future for Isolate {
type Item = (); 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. // Lock the current thread for V8.
let _locker = LockerScope::new(self.libdeno_isolate); let _locker = LockerScope::new(self.libdeno_isolate);
@ -579,9 +601,7 @@ impl Future for Isolate {
} }
self.check_promise_errors(); self.check_promise_errors();
if let Some(err) = self.last_exception() { self.check_last_exception()?;
return Err(err);
}
// We're idle if pending_ops is empty. // We're idle if pending_ops is empty.
if self.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 { if let Err(e) = r {
panic!(e.to_string()); panic!(e.to_string());
} }
@ -814,7 +834,7 @@ pub mod tests {
"#, "#,
)); ));
assert_eq!(dispatch_count.load(Ordering::Relaxed), 1); 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); assert_eq!(dispatch_count.load(Ordering::Relaxed), 1);
js_check(isolate.execute( js_check(isolate.execute(
"check2.js", "check2.js",
@ -825,11 +845,11 @@ pub mod tests {
"#, "#,
)); ));
assert_eq!(dispatch_count.load(Ordering::Relaxed), 2); 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)")); js_check(isolate.execute("check3.js", "assert(nrecv == 2)"));
assert_eq!(dispatch_count.load(Ordering::Relaxed), 2); assert_eq!(dispatch_count.load(Ordering::Relaxed), 2);
// We are idle, so the next poll should be the last. // 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!(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);")); 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!(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!(count.load(Ordering::Relaxed), 2);
assert_eq!(Ok(Ready(())), isolate.poll()); assert_eq!(Ready(()), isolate.poll().unwrap());
assert_eq!(count.load(Ordering::Relaxed), 2); assert_eq!(count.load(Ordering::Relaxed), 2);
}) })
} }
@ -1090,7 +1110,7 @@ pub mod tests {
"#, "#,
)); ));
assert_eq!(dispatch_count.load(Ordering::Relaxed), 1); 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);")); 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!(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);")); 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!(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);")); js_check(isolate.execute("check.js", "assert(asyncRecv == 2);"));
}); });
} }
@ -1164,7 +1184,7 @@ pub mod tests {
include_str!("shared_queue_test.js"), include_str!("shared_queue_test.js"),
), ),
); );
assert_eq!(Ok(Async::Ready(())), isolate.poll()); assert_eq!(Async::Ready(()), isolate.poll().unwrap());
}); });
} }

View file

@ -9,8 +9,10 @@
// console.log(err.stack); // console.log(err.stack);
// It would require calling into Rust from Error.prototype.prepareStackTrace. // It would require calling into Rust from Error.prototype.prepareStackTrace.
use crate::any_error::ErrBox;
use serde_json; use serde_json;
use serde_json::value::Value; use serde_json::value::Value;
use std::error::Error;
use std::fmt; use std::fmt;
use std::str; use std::str;
@ -26,7 +28,7 @@ pub struct StackFrame {
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub struct JSError { pub struct V8Exception {
pub message: String, pub message: String,
pub source_line: Option<String>, pub source_line: Option<String>,
@ -41,77 +43,8 @@ pub struct JSError {
pub frames: Vec<StackFrame>, pub frames: Vec<StackFrame>,
} }
impl std::error::Error for JSError { #[derive(Debug, PartialEq, Clone)]
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { pub struct CoreJSError(V8Exception);
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(())
}
}
impl StackFrame { impl StackFrame {
// TODO Maybe use serde_derive? // TODO Maybe use serde_derive?
@ -186,9 +119,9 @@ impl StackFrame {
} }
} }
impl JSError { impl V8Exception {
/// Creates a new JSError by parsing the raw exception JSON string from V8. /// Creates a new V8Exception by parsing the raw exception JSON string from V8.
pub fn from_v8_exception(json_str: &str) -> Option<Self> { pub fn from_json(json_str: &str) -> Option<Self> {
let v = serde_json::from_str::<serde_json::Value>(json_str); let v = serde_json::from_str::<serde_json::Value>(json_str);
if v.is_err() { if v.is_err() {
return None; return None;
@ -236,7 +169,7 @@ impl JSError {
} }
} }
Some(JSError { Some(V8Exception {
message, message,
source_line, source_line,
script_resource_name, 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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
fn error1() -> JSError { fn error1() -> V8Exception {
JSError { V8Exception {
message: "Error: foo bar".to_string(), message: "Error: foo bar".to_string(),
source_line: None, source_line: None,
script_resource_name: None, script_resource_name: None,
@ -344,8 +344,8 @@ mod tests {
} }
#[test] #[test]
fn js_error_from_v8_exception() { fn v8_exception_from_json() {
let r = JSError::from_v8_exception( let r = V8Exception::from_json(
r#"{ r#"{
"message":"Uncaught Error: bad", "message":"Uncaught Error: bad",
"frames":[ "frames":[
@ -387,8 +387,8 @@ mod tests {
} }
#[test] #[test]
fn js_error_from_v8_exception2() { fn v8_exception_from_json_2() {
let r = JSError::from_v8_exception( 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}]}" "{\"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()); assert!(r.is_some());
@ -405,16 +405,9 @@ mod tests {
assert_eq!(e.frames.len(), 1); assert_eq!(e.frames.len(), 1);
} }
#[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] #[test]
fn js_error_to_string() { 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"; 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()); assert_eq!(expected, &e.to_string());
} }

View file

@ -4,6 +4,7 @@ extern crate log;
extern crate futures; extern crate futures;
extern crate libc; extern crate libc;
mod any_error;
mod flags; mod flags;
mod isolate; mod isolate;
mod js_errors; mod js_errors;
@ -12,6 +13,7 @@ mod module_specifier;
mod modules; mod modules;
mod shared_queue; mod shared_queue;
pub use crate::any_error::*;
pub use crate::flags::v8_set_flags; pub use crate::flags::v8_set_flags;
pub use crate::isolate::*; pub use crate::isolate::*;
pub use crate::js_errors::*; pub use crate::js_errors::*;

View file

@ -6,8 +6,8 @@
// small and simple for users who do not use modules or if they do can load them // 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. // synchronously. The isolate.rs module should never depend on this module.
use crate::any_error::ErrBox;
use crate::isolate::Isolate; use crate::isolate::Isolate;
use crate::js_errors::JSError;
use crate::libdeno::deno_mod; use crate::libdeno::deno_mod;
use crate::module_specifier::ModuleSpecifier; use crate::module_specifier::ModuleSpecifier;
use futures::Async; use futures::Async;
@ -15,7 +15,6 @@ use futures::Future;
use futures::Poll; use futures::Poll;
use std::collections::HashMap; use std::collections::HashMap;
use std::collections::HashSet; use std::collections::HashSet;
use std::error::Error;
use std::fmt; use std::fmt;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::sync::Arc; use std::sync::Arc;
@ -34,12 +33,10 @@ pub struct SourceCodeInfo {
pub code: String, pub code: String,
} }
pub type SourceCodeInfoFuture<E> = pub type SourceCodeInfoFuture =
dyn Future<Item = SourceCodeInfo, Error = E> + Send; dyn Future<Item = SourceCodeInfo, Error = ErrBox> + Send;
pub trait Loader: Send + Sync { pub trait Loader: Send + Sync {
type Error: std::error::Error + 'static;
/// Returns an absolute URL. /// Returns an absolute URL.
/// When implementing an spec-complaint VM, this should be exactly the /// When implementing an spec-complaint VM, this should be exactly the
/// algorithm described here: /// algorithm described here:
@ -49,19 +46,19 @@ pub trait Loader: Send + Sync {
specifier: &str, specifier: &str,
referrer: &str, referrer: &str,
is_root: bool, is_root: bool,
) -> Result<ModuleSpecifier, Self::Error>; ) -> Result<ModuleSpecifier, ErrBox>;
/// Given ModuleSpecifier, load its source code. /// Given ModuleSpecifier, load its source code.
fn load( fn load(
&self, &self,
module_specifier: &ModuleSpecifier, module_specifier: &ModuleSpecifier,
) -> Box<SourceCodeInfoFuture<Self::Error>>; ) -> Box<SourceCodeInfoFuture>;
} }
struct PendingLoad<E: Error> { struct PendingLoad {
url: String, url: String,
is_root: bool, 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 /// This future is used to implement parallel async module loading without
@ -71,7 +68,7 @@ pub struct RecursiveLoad<L: Loader> {
loader: L, loader: L,
isolate: Arc<Mutex<Isolate>>, isolate: Arc<Mutex<Isolate>>,
modules: Arc<Mutex<Modules>>, modules: Arc<Mutex<Modules>>,
pending: Vec<PendingLoad<L::Error>>, pending: Vec<PendingLoad>,
is_pending: HashSet<String>, is_pending: HashSet<String>,
phantom: PhantomData<L>, phantom: PhantomData<L>,
// TODO(ry) The following can all be combined into a single enum State type. // 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, specifier: &str,
referrer: &str, referrer: &str,
parent_id: Option<deno_mod>, parent_id: Option<deno_mod>,
) -> Result<String, L::Error> { ) -> Result<String, ErrBox> {
let is_root = parent_id.is_none(); let is_root = parent_id.is_none();
let module_specifier = self.loader.resolve(specifier, referrer, is_root)?; let module_specifier = self.loader.resolve(specifier, referrer, is_root)?;
let module_name = module_specifier.to_string(); 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> { impl<L: Loader> Future for RecursiveLoad<L> {
type Item = deno_mod; type Item = deno_mod;
type Error = JSErrorOr<L::Error>; type Error = ErrBox;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
if self.root.is_none() && self.root_specifier.is_some() { if self.root.is_none() && self.root_specifier.is_some() {
let s = self.root_specifier.take().unwrap(); let s = self.root_specifier.take().unwrap();
match self.add(&s, ".", None) { match self.add(&s, ".", None) {
Err(err) => { Err(err) => {
return Err(JSErrorOr::Other(err)); return Err(err);
} }
Ok(root) => { Ok(root) => {
self.root = Some(root); self.root = Some(root);
@ -172,7 +163,7 @@ impl<L: Loader> Future for RecursiveLoad<L> {
let pending = &mut self.pending[i]; let pending = &mut self.pending[i];
match pending.source_code_info_future.poll() { match pending.source_code_info_future.poll() {
Err(err) => { Err(err) => {
return Err(JSErrorOr::Other(err)); return Err(err);
} }
Ok(Async::NotReady) => { Ok(Async::NotReady) => {
i += 1; i += 1;
@ -200,18 +191,15 @@ impl<L: Loader> Future for RecursiveLoad<L> {
if !is_module_registered { if !is_module_registered {
let module_name = &source_code_info.module_name; let module_name = &source_code_info.module_name;
let result = { let mod_id = {
let isolate = self.isolate.lock().unwrap(); let isolate = self.isolate.lock().unwrap();
isolate.mod_new( isolate.mod_new(
completed.is_root, completed.is_root,
module_name, module_name,
&source_code_info.code, &source_code_info.code,
) )
}; }?;
if let Err(err) = result {
return Err(JSErrorOr::JSError(err));
}
let mod_id = result.unwrap();
if completed.is_root { if completed.is_root {
assert!(self.root_id.is_none()); assert!(self.root_id.is_none());
self.root_id = Some(mod_id); self.root_id = Some(mod_id);
@ -235,9 +223,7 @@ impl<L: Loader> Future for RecursiveLoad<L> {
}; };
let referrer = module_name; let referrer = module_name;
for specifier in imports { for specifier in imports {
self self.add(&specifier, referrer, Some(mod_id))?;
.add(&specifier, referrer, Some(mod_id))
.map_err(JSErrorOr::Other)?;
} }
} else if need_alias { } else if need_alias {
let mut modules = self.modules.lock().unwrap(); let mut modules = self.modules.lock().unwrap();
@ -252,9 +238,8 @@ impl<L: Loader> Future for RecursiveLoad<L> {
} }
let root_id = self.root_id.unwrap(); let root_id = self.root_id.unwrap();
let result = {
let mut resolve_cb = let mut resolve_cb = |specifier: &str, referrer_id: deno_mod| -> deno_mod {
|specifier: &str, referrer_id: deno_mod| -> deno_mod {
let modules = self.modules.lock().unwrap(); let modules = self.modules.lock().unwrap();
let referrer = modules.get_name(referrer_id).unwrap(); let referrer = modules.get_name(referrer_id).unwrap();
// this callback is only called for non-root modules // this callback is only called for non-root modules
@ -270,13 +255,9 @@ impl<L: Loader> Future for RecursiveLoad<L> {
}; };
let mut isolate = self.isolate.lock().unwrap(); let mut isolate = self.isolate.lock().unwrap();
isolate.mod_instantiate(root_id, &mut resolve_cb) isolate
}; .mod_instantiate(root_id, &mut resolve_cb)
.map(|_| Async::Ready(root_id))
match result {
Err(err) => Err(JSErrorOr::JSError(err)),
Ok(()) => Ok(Async::Ready(root_id)),
}
} }
} }
@ -537,6 +518,7 @@ mod tests {
use super::*; use super::*;
use crate::isolate::js_check; use crate::isolate::js_check;
use crate::isolate::tests::*; use crate::isolate::tests::*;
use std::error::Error;
use std::fmt; use std::fmt;
struct MockLoader { struct MockLoader {
@ -607,9 +589,9 @@ mod tests {
impl Future for DelayedSourceCodeFuture { impl Future for DelayedSourceCodeFuture {
type Item = SourceCodeInfo; 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; self.counter += 1;
if self.url == "file:///never_ready.js" if self.url == "file:///never_ready.js"
|| (self.url == "file:///slow.js" && self.counter < 2) || (self.url == "file:///slow.js" && self.counter < 2)
@ -621,20 +603,18 @@ mod tests {
code: src.0.to_owned(), code: src.0.to_owned(),
module_name: src.1.to_owned(), module_name: src.1.to_owned(),
})), })),
None => Err(MockError::LoadErr), None => Err(MockError::LoadErr.into()),
} }
} }
} }
impl Loader for MockLoader { impl Loader for MockLoader {
type Error = MockError;
fn resolve( fn resolve(
&self, &self,
specifier: &str, specifier: &str,
referrer: &str, referrer: &str,
_is_root: bool, _is_root: bool,
) -> Result<ModuleSpecifier, Self::Error> { ) -> Result<ModuleSpecifier, ErrBox> {
let referrer = if referrer == "." { let referrer = if referrer == "." {
"file:///" "file:///"
} else { } else {
@ -646,20 +626,20 @@ mod tests {
let output_specifier = let output_specifier =
match ModuleSpecifier::resolve_import(specifier, referrer) { match ModuleSpecifier::resolve_import(specifier, referrer) {
Ok(specifier) => specifier, Ok(specifier) => specifier,
Err(..) => return Err(MockError::ResolveErr), Err(..) => return Err(MockError::ResolveErr.into()),
}; };
if mock_source_code(&output_specifier.to_string()).is_some() { if mock_source_code(&output_specifier.to_string()).is_some() {
Ok(output_specifier) Ok(output_specifier)
} else { } else {
Err(MockError::ResolveErr) Err(MockError::ResolveErr.into())
} }
} }
fn load( fn load(
&self, &self,
module_specifier: &ModuleSpecifier, module_specifier: &ModuleSpecifier,
) -> Box<SourceCodeInfoFuture<Self::Error>> { ) -> Box<SourceCodeInfoFuture> {
let mut loads = self.loads.lock().unwrap(); let mut loads = self.loads.lock().unwrap();
loads.push(module_specifier.to_string()); loads.push(module_specifier.to_string());
let url = module_specifier.to_string(); let url = module_specifier.to_string();
@ -962,8 +942,11 @@ mod tests {
RecursiveLoad::new("/bad_import.js", loader, isolate, modules); RecursiveLoad::new("/bad_import.js", loader, isolate, modules);
let result = recursive_load.poll(); let result = recursive_load.poll();
assert!(result.is_err()); assert!(result.is_err());
let either_err = result.err().unwrap(); let err = result.err().unwrap();
assert_eq!(either_err, JSErrorOr::Other(MockError::ResolveErr)); assert_eq!(
err.downcast_ref::<MockError>().unwrap(),
&MockError::ResolveErr
);
} }
#[test] #[test]