diff --git a/cli/errors.rs b/cli/errors.rs new file mode 100644 index 0000000000..6b53ac8d07 --- /dev/null +++ b/cli/errors.rs @@ -0,0 +1,236 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. + +//! There are many types of errors in Deno: +//! - ErrBox: a generic boxed object. This is the super type of all +//! errors handled in Rust. +//! - JSError: a container for the error message and stack trace for exceptions +//! thrown in JavaScript code. We use this to pretty-print stack traces. +//! - Diagnostic: these are errors that originate in TypeScript's compiler. +//! They're similar to JSError, in that they have line numbers. +//! But Diagnostics are compile-time type errors, whereas JSErrors are runtime +//! exceptions. + +use crate::import_map::ImportMapError; +use crate::swc_util::SwcDiagnosticBuffer; +use deno_core::ErrBox; +use deno_core::ModuleResolutionError; +use rustyline::error::ReadlineError; +use std::env; +use std::error::Error; +use std::io; + +fn get_dlopen_error_class(error: &dlopen::Error) -> &'static str { + use dlopen::Error::*; + match error { + NullCharacter(_) => "InvalidData", + OpeningLibraryError(ref e) => get_io_error_class(e), + SymbolGettingError(ref e) => get_io_error_class(e), + AddrNotMatchingDll(ref e) => get_io_error_class(e), + NullSymbol => "NotFound", + } +} + +fn get_env_var_error_class(error: &env::VarError) -> &'static str { + use env::VarError::*; + match error { + NotPresent => "NotFound", + NotUnicode(..) => "InvalidData", + } +} + +fn get_import_map_error_class(_: &ImportMapError) -> &'static str { + "URIError" +} + +fn get_io_error_class(error: &io::Error) -> &'static str { + use io::ErrorKind::*; + match error.kind() { + NotFound => "NotFound", + PermissionDenied => "PermissionDenied", + ConnectionRefused => "ConnectionRefused", + ConnectionReset => "ConnectionReset", + ConnectionAborted => "ConnectionAborted", + NotConnected => "NotConnected", + AddrInUse => "AddrInUse", + AddrNotAvailable => "AddrNotAvailable", + BrokenPipe => "BrokenPipe", + AlreadyExists => "AlreadyExists", + InvalidInput => "TypeError", + InvalidData => "InvalidData", + TimedOut => "TimedOut", + Interrupted => "Interrupted", + WriteZero => "WriteZero", + UnexpectedEof => "UnexpectedEof", + Other => "Error", + WouldBlock => unreachable!(), + // Non-exhaustive enum - might add new variants + // in the future + _ => unreachable!(), + } +} + +fn get_module_resolution_error_class( + _: &ModuleResolutionError, +) -> &'static str { + "URIError" +} + +fn get_notify_error_class(error: ¬ify::Error) -> &'static str { + use notify::ErrorKind::*; + match error.kind { + Generic(_) => "Error", + Io(ref e) => get_io_error_class(e), + PathNotFound => "NotFound", + WatchNotFound => "NotFound", + InvalidConfig(_) => "InvalidData", + } +} + +fn get_readline_error_class(error: &ReadlineError) -> &'static str { + use ReadlineError::*; + match error { + Io(err) => get_io_error_class(err), + Eof => "UnexpectedEof", + Interrupted => "Interrupted", + #[cfg(unix)] + Errno(err) => get_nix_error_class(err), + _ => unimplemented!(), + } +} + +fn get_regex_error_class(error: ®ex::Error) -> &'static str { + use regex::Error::*; + match error { + Syntax(_) => "SyntaxError", + CompiledTooBig(_) => "RangeError", + _ => "Error", + } +} + +fn get_request_error_class(error: &reqwest::Error) -> &'static str { + error + .source() + .and_then(|inner_err| { + (inner_err + .downcast_ref::() + .map(get_io_error_class)) + .or_else(|| { + inner_err + .downcast_ref::() + .map(get_serde_json_error_class) + }) + .or_else(|| { + inner_err + .downcast_ref::() + .map(get_url_parse_error_class) + }) + }) + .unwrap_or("Http") +} + +fn get_serde_json_error_class( + error: &serde_json::error::Error, +) -> &'static str { + use serde_json::error::*; + match error.classify() { + Category::Io => error + .source() + .and_then(|e| e.downcast_ref::()) + .map(get_io_error_class) + .unwrap(), + Category::Syntax => "SyntaxError", + Category::Data => "InvalidData", + Category::Eof => "UnexpectedEof", + } +} + +fn get_swc_diagnostic_class(_: &SwcDiagnosticBuffer) -> &'static str { + "SyntaxError" +} + +fn get_url_parse_error_class(_error: &url::ParseError) -> &'static str { + "URIError" +} + +#[cfg(unix)] +fn get_nix_error_class(error: &nix::Error) -> &'static str { + use nix::errno::Errno::*; + match error { + nix::Error::Sys(EPERM) => "PermissionDenied", + nix::Error::Sys(EINVAL) => "TypeError", + nix::Error::Sys(ENOENT) => "NotFound", + nix::Error::Sys(ENOTTY) => "BadResource", + nix::Error::Sys(UnknownErrno) => unreachable!(), + nix::Error::Sys(_) => unreachable!(), + nix::Error::InvalidPath => "TypeError", + nix::Error::InvalidUtf8 => "InvalidData", + nix::Error::UnsupportedOperation => unreachable!(), + } +} + +pub fn get_error_class(e: &ErrBox) -> &'static str { + use ErrBox::*; + match e { + Simple { class, .. } => Some(*class), + _ => None, + } + .or_else(|| { + e.downcast_ref::() + .map(get_dlopen_error_class) + }) + .or_else(|| { + e.downcast_ref::() + .map(get_env_var_error_class) + }) + .or_else(|| { + e.downcast_ref::() + .map(get_import_map_error_class) + }) + .or_else(|| e.downcast_ref::().map(get_io_error_class)) + .or_else(|| { + e.downcast_ref::() + .map(get_module_resolution_error_class) + }) + .or_else(|| { + e.downcast_ref::() + .map(get_notify_error_class) + }) + .or_else(|| { + e.downcast_ref::() + .map(get_readline_error_class) + }) + .or_else(|| { + e.downcast_ref::() + .map(get_request_error_class) + }) + .or_else(|| e.downcast_ref::().map(get_regex_error_class)) + .or_else(|| { + e.downcast_ref::() + .map(get_serde_json_error_class) + }) + .or_else(|| { + e.downcast_ref::() + .map(get_swc_diagnostic_class) + }) + .or_else(|| { + e.downcast_ref::() + .map(get_url_parse_error_class) + }) + .or_else(|| { + #[cfg(unix)] + let maybe_get_nix_error_class = + || e.downcast_ref::().map(get_nix_error_class); + #[cfg(not(unix))] + let maybe_get_nix_error_class = || Option::<&'static str>::None; + (maybe_get_nix_error_class)() + }) + .unwrap_or_else(|| { + panic!("ErrBox '{}' contains boxed error of unknown type", e); + }) +} + +pub fn rust_err_to_json(error: &ErrBox) -> Box<[u8]> { + let error_value = + json!({ "kind": get_error_class(error), "message": error.to_string()}); + serde_json::to_vec(&error_value).unwrap().into_boxed_slice() +} diff --git a/cli/file_fetcher.rs b/cli/file_fetcher.rs index c703bfc272..c5fd788972 100644 --- a/cli/file_fetcher.rs +++ b/cli/file_fetcher.rs @@ -5,7 +5,6 @@ use crate::http_util; use crate::http_util::create_http_client; use crate::http_util::FetchOnceResult; use crate::msg; -use crate::op_error::OpError; use crate::permissions::Permissions; use crate::text_encoding; use deno_core::ErrBox; @@ -138,11 +137,12 @@ impl SourceFileFetcher { pub fn check_if_supported_scheme(url: &Url) -> Result<(), ErrBox> { if !SUPPORTED_URL_SCHEMES.contains(&url.scheme()) { - return Err( - OpError::other( - format!("Unsupported scheme \"{}\" for module \"{}\". Supported schemes: {:#?}", url.scheme(), url, SUPPORTED_URL_SCHEMES), - ).into() - ); + return Err(ErrBox::error(format!( + "Unsupported scheme \"{}\" for module \"{}\". Supported schemes: {:#?}", + url.scheme(), + url, + SUPPORTED_URL_SCHEMES + ))); } Ok(()) @@ -255,13 +255,13 @@ impl SourceFileFetcher { r#"Cannot find module "{}"{} in cache, --cached-only is specified"#, module_url, referrer_suffix ); - OpError::not_found(msg).into() + ErrBox::new("NotFound", msg) } else if is_not_found { let msg = format!( r#"Cannot resolve module "{}"{}"#, module_url, referrer_suffix ); - OpError::not_found(msg).into() + ErrBox::new("NotFound", msg) } else { err }; @@ -346,9 +346,7 @@ impl SourceFileFetcher { permissions: &Permissions, ) -> Result { let filepath = module_url.to_file_path().map_err(|()| { - ErrBox::from(OpError::uri_error( - "File URL contains invalid path".to_owned(), - )) + ErrBox::new("URIError", "File URL contains invalid path") })?; permissions.check_read(&filepath)?; @@ -385,8 +383,7 @@ impl SourceFileFetcher { redirect_limit: i64, ) -> Result, ErrBox> { if redirect_limit < 0 { - let e = OpError::http("too many redirects".to_string()); - return Err(e.into()); + return Err(ErrBox::new("Http", "too many redirects")); } let result = self.http_cache.get(&module_url); @@ -451,12 +448,12 @@ impl SourceFileFetcher { permissions: &Permissions, ) -> Pin>>> { if redirect_limit < 0 { - let e = OpError::http("too many redirects".to_string()); - return futures::future::err(e.into()).boxed_local(); + let e = ErrBox::new("Http", "too many redirects"); + return futures::future::err(e).boxed_local(); } if let Err(e) = permissions.check_net_url(&module_url) { - return futures::future::err(e.into()).boxed_local(); + return futures::future::err(e).boxed_local(); } let is_blocked = @@ -479,17 +476,12 @@ impl SourceFileFetcher { // If file wasn't found in cache check if we can fetch it if cached_only { // We can't fetch remote file - bail out - return futures::future::err( - std::io::Error::new( - std::io::ErrorKind::NotFound, - format!( - "Cannot find remote file '{}' in cache, --cached-only is specified", - module_url.to_string() - ), - ) - .into(), - ) - .boxed_local(); + let message = format!( + "Cannot find remote file '{}' in cache, --cached-only is specified", + module_url + ); + return futures::future::err(ErrBox::new("NotFound", message)) + .boxed_local(); } info!("{} {}", colors::green("Download"), module_url.to_string()); diff --git a/cli/fmt.rs b/cli/fmt.rs index 319f7fece7..b815732949 100644 --- a/cli/fmt.rs +++ b/cli/fmt.rs @@ -10,7 +10,6 @@ use crate::colors; use crate::diff::diff; use crate::fs::files_in_subtree; -use crate::op_error::OpError; use crate::text_encoding; use deno_core::ErrBox; use dprint_plugin_typescript as dprint; @@ -110,14 +109,11 @@ async fn check_source_files( if not_formatted_files_count == 0 { Ok(()) } else { - Err( - OpError::other(format!( - "Found {} not formatted {}", - not_formatted_files_count, - files_str(not_formatted_files_count), - )) - .into(), - ) + Err(ErrBox::error(format!( + "Found {} not formatted {}", + not_formatted_files_count, + files_str(not_formatted_files_count), + ))) } } @@ -175,7 +171,7 @@ async fn format_source_files( fn format_stdin(check: bool) -> Result<(), ErrBox> { let mut source = String::new(); if stdin().read_to_string(&mut source).is_err() { - return Err(OpError::other("Failed to read from stdin".to_string()).into()); + return Err(ErrBox::error("Failed to read from stdin")); } let formatter = dprint::Formatter::new(get_config()); @@ -191,7 +187,7 @@ fn format_stdin(check: bool) -> Result<(), ErrBox> { } } Err(e) => { - return Err(OpError::other(e).into()); + return Err(ErrBox::error(e)); } } Ok(()) @@ -217,7 +213,9 @@ fn is_supported(path: &Path) -> bool { } } -pub fn collect_files(files: Vec) -> Result, ErrBox> { +pub fn collect_files( + files: Vec, +) -> Result, std::io::Error> { let mut target_files: Vec = vec![]; if files.is_empty() { diff --git a/cli/fmt_errors.rs b/cli/fmt_errors.rs index 2d360892ec..b2925f6257 100644 --- a/cli/fmt_errors.rs +++ b/cli/fmt_errors.rs @@ -114,7 +114,7 @@ impl JSError { ) -> ErrBox { let core_js_error = apply_source_map(&core_js_error, source_map_getter); let js_error = Self(core_js_error); - ErrBox::from(js_error) + js_error.into() } } diff --git a/cli/main.rs b/cli/main.rs index 7cf698d5a8..b194657133 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -29,6 +29,7 @@ pub mod deno_dir; pub mod diagnostics; mod diff; mod disk_cache; +pub mod errors; mod file_fetcher; pub mod flags; mod flags_allow_net; @@ -48,7 +49,6 @@ mod lockfile; mod metrics; mod module_graph; pub mod msg; -pub mod op_error; mod op_fetch_asset; pub mod ops; pub mod permissions; diff --git a/cli/module_graph.rs b/cli/module_graph.rs index 5b15c0cd47..7cbd4cfcc7 100644 --- a/cli/module_graph.rs +++ b/cli/module_graph.rs @@ -5,7 +5,6 @@ use crate::file_fetcher::SourceFile; use crate::file_fetcher::SourceFileFetcher; use crate::import_map::ImportMap; use crate::msg::MediaType; -use crate::op_error::OpError; use crate::permissions::Permissions; use crate::swc_util::Location; use crate::tsc::pre_process_file; @@ -37,7 +36,7 @@ fn err_with_location(e: ErrBox, maybe_location: Option<&Location>) -> ErrBox { location.filename, location.line ); let err_str = e.to_string(); - OpError::other(format!("{}{}", err_str, location_str)).into() + ErrBox::error(format!("{}{}", err_str, location_str)) } else { e } @@ -52,10 +51,10 @@ fn validate_no_downgrade( if let Some(referrer) = maybe_referrer.as_ref() { if let "https" = referrer.as_url().scheme() { if let "http" = module_specifier.as_url().scheme() { - let e = OpError::permission_denied( - "Modules loaded over https:// are not allowed to import modules over http://".to_string() + let e = ErrBox::new("PermissionDenied", + "Modules loaded over https:// are not allowed to import modules over http://" ); - return Err(err_with_location(e.into(), maybe_location)); + return Err(err_with_location(e, maybe_location)); }; }; }; @@ -77,10 +76,10 @@ fn validate_no_file_from_remote( match specifier_url.scheme() { "http" | "https" => {} _ => { - let e = OpError::permission_denied( + let e = ErrBox::new("PermissionDenied", "Remote modules are not allowed to statically import local modules. Use dynamic import instead.".to_string() ); - return Err(err_with_location(e.into(), maybe_location)); + return Err(err_with_location(e, maybe_location)); } } } diff --git a/cli/op_error.rs b/cli/op_error.rs deleted file mode 100644 index 32ad9c0f72..0000000000 --- a/cli/op_error.rs +++ /dev/null @@ -1,550 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. - -//! There are many types of errors in Deno: -//! - ErrBox: a generic boxed object. This is the super type of all -//! errors handled in Rust. -//! - JSError: a container for the error message and stack trace for exceptions -//! thrown in JavaScript code. We use this to pretty-print stack traces. -//! - OpError: these are errors that happen during ops, which are passed -//! back into the runtime, where an exception object is created and thrown. -//! OpErrors have an integer code associated with them - access this via the -//! `kind` field. -//! - Diagnostic: these are errors that originate in TypeScript's compiler. -//! They're similar to JSError, in that they have line numbers. -//! But Diagnostics are compile-time type errors, whereas JSErrors are runtime -//! exceptions. - -use crate::import_map::ImportMapError; -use crate::swc_util::SwcDiagnosticBuffer; -use deno_core::ErrBox; -use deno_core::ModuleResolutionError; -use rustyline::error::ReadlineError; -use std::env::VarError; -use std::error::Error; -use std::fmt; -use std::io; - -// Warning! The values in this enum are duplicated in js/errors.ts -// Update carefully! -#[derive(Clone, Copy, PartialEq, Debug)] -pub enum ErrorKind { - NotFound = 1, - PermissionDenied = 2, - ConnectionRefused = 3, - ConnectionReset = 4, - ConnectionAborted = 5, - NotConnected = 6, - AddrInUse = 7, - AddrNotAvailable = 8, - BrokenPipe = 9, - AlreadyExists = 10, - InvalidData = 13, - TimedOut = 14, - Interrupted = 15, - WriteZero = 16, - UnexpectedEof = 17, - BadResource = 18, - Http = 19, - URIError = 20, - TypeError = 21, - /// This maps to window.Error - ie. a generic error type - /// if no better context is available. - /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error - Other = 22, - Busy = 23, -} - -impl From for String { - fn from(kind: ErrorKind) -> Self { - let s = match kind { - ErrorKind::NotFound => "NotFound", - ErrorKind::PermissionDenied => "PermissionDenied", - ErrorKind::ConnectionRefused => "ConnectionRefused", - ErrorKind::ConnectionReset => "ConnectionReset", - ErrorKind::ConnectionAborted => "ConnectionAborted", - ErrorKind::NotConnected => "NotConnected", - ErrorKind::AddrInUse => "AddrInUse", - ErrorKind::AddrNotAvailable => "AddrNotAvailable", - ErrorKind::BrokenPipe => "BrokenPipe", - ErrorKind::AlreadyExists => "AlreadyExists", - ErrorKind::InvalidData => "InvalidData", - ErrorKind::TimedOut => "TimedOut", - ErrorKind::Interrupted => "Interrupted", - ErrorKind::WriteZero => "WriteZero", - ErrorKind::UnexpectedEof => "UnexpectedEof", - ErrorKind::BadResource => "BadResource", - ErrorKind::Http => "Http", - ErrorKind::URIError => "URIError", - ErrorKind::TypeError => "TypeError", - ErrorKind::Other => "Other", - ErrorKind::Busy => "Busy", - }; - - s.to_string() - } -} - -fn error_str_to_kind(kind_str: &str) -> ErrorKind { - match kind_str { - "NotFound" => ErrorKind::NotFound, - "PermissionDenied" => ErrorKind::PermissionDenied, - "ConnectionRefused" => ErrorKind::ConnectionRefused, - "ConnectionReset" => ErrorKind::ConnectionReset, - "ConnectionAborted" => ErrorKind::ConnectionAborted, - "NotConnected" => ErrorKind::NotConnected, - "AddrInUse" => ErrorKind::AddrInUse, - "AddrNotAvailable" => ErrorKind::AddrNotAvailable, - "BrokenPipe" => ErrorKind::BrokenPipe, - "AlreadyExists" => ErrorKind::AlreadyExists, - "InvalidData" => ErrorKind::InvalidData, - "TimedOut" => ErrorKind::TimedOut, - "Interrupted" => ErrorKind::Interrupted, - "WriteZero" => ErrorKind::WriteZero, - "UnexpectedEof" => ErrorKind::UnexpectedEof, - "BadResource" => ErrorKind::BadResource, - "Http" => ErrorKind::Http, - "URIError" => ErrorKind::URIError, - "TypeError" => ErrorKind::TypeError, - "Other" => ErrorKind::Other, - "Busy" => ErrorKind::Busy, - _ => panic!("unknown error kind"), - } -} - -#[derive(Debug)] -pub struct OpError { - pub kind_str: String, - pub msg: String, -} - -impl OpError { - fn new(kind: ErrorKind, msg: String) -> Self { - Self { - kind_str: kind.into(), - msg, - } - } - - pub fn not_found(msg: String) -> Self { - Self::new(ErrorKind::NotFound, msg) - } - - pub fn not_implemented() -> Self { - Self::other("not implemented".to_string()) - } - - pub fn other(msg: String) -> Self { - Self::new(ErrorKind::Other, msg) - } - - pub fn type_error(msg: String) -> Self { - Self::new(ErrorKind::TypeError, msg) - } - - pub fn http(msg: String) -> Self { - Self::new(ErrorKind::Http, msg) - } - - pub fn uri_error(msg: String) -> Self { - Self::new(ErrorKind::URIError, msg) - } - - pub fn permission_denied(msg: String) -> OpError { - Self::new(ErrorKind::PermissionDenied, msg) - } - - pub fn bad_resource(msg: String) -> OpError { - Self::new(ErrorKind::BadResource, msg) - } - - // BadResource usually needs no additional detail, hence this helper. - pub fn bad_resource_id() -> OpError { - Self::new(ErrorKind::BadResource, "Bad resource ID".to_string()) - } - - pub fn invalid_utf8() -> OpError { - Self::new(ErrorKind::InvalidData, "invalid utf8".to_string()) - } - - pub fn resource_unavailable() -> OpError { - Self::new( - ErrorKind::Busy, - "resource is unavailable because it is in use by a promise".to_string(), - ) - } - - pub fn invalid_domain_error() -> OpError { - OpError::type_error("Invalid domain.".to_string()) - } - - pub fn permission_escalation_error() -> OpError { - OpError::permission_denied( - "Arguments escalate parent permissions.".to_string(), - ) - } -} - -impl Error for OpError {} - -impl fmt::Display for OpError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad(self.msg.as_str()) - } -} - -impl From for OpError { - fn from(error: ImportMapError) -> Self { - OpError::from(&error) - } -} - -impl From<&ImportMapError> for OpError { - fn from(error: &ImportMapError) -> Self { - Self::new(ErrorKind::Other, error.to_string()) - } -} - -impl From for OpError { - fn from(error: ModuleResolutionError) -> Self { - OpError::from(&error) - } -} - -impl From<&ModuleResolutionError> for OpError { - fn from(error: &ModuleResolutionError) -> Self { - Self::new(ErrorKind::URIError, error.to_string()) - } -} - -impl From for OpError { - fn from(error: VarError) -> Self { - OpError::from(&error) - } -} - -impl From<&VarError> for OpError { - fn from(error: &VarError) -> Self { - use VarError::*; - let kind = match error { - NotPresent => ErrorKind::NotFound, - NotUnicode(..) => ErrorKind::InvalidData, - }; - - Self::new(kind, error.to_string()) - } -} - -impl From for OpError { - fn from(error: io::Error) -> Self { - OpError::from(&error) - } -} - -impl From<&io::Error> for OpError { - fn from(error: &io::Error) -> Self { - use io::ErrorKind::*; - let kind = match error.kind() { - NotFound => ErrorKind::NotFound, - PermissionDenied => ErrorKind::PermissionDenied, - ConnectionRefused => ErrorKind::ConnectionRefused, - ConnectionReset => ErrorKind::ConnectionReset, - ConnectionAborted => ErrorKind::ConnectionAborted, - NotConnected => ErrorKind::NotConnected, - AddrInUse => ErrorKind::AddrInUse, - AddrNotAvailable => ErrorKind::AddrNotAvailable, - BrokenPipe => ErrorKind::BrokenPipe, - AlreadyExists => ErrorKind::AlreadyExists, - InvalidInput => ErrorKind::TypeError, - InvalidData => ErrorKind::InvalidData, - TimedOut => ErrorKind::TimedOut, - Interrupted => ErrorKind::Interrupted, - WriteZero => ErrorKind::WriteZero, - UnexpectedEof => ErrorKind::UnexpectedEof, - Other => ErrorKind::Other, - WouldBlock => unreachable!(), - // Non-exhaustive enum - might add new variants - // in the future - _ => unreachable!(), - }; - - Self::new(kind, error.to_string()) - } -} - -impl From for OpError { - fn from(error: url::ParseError) -> Self { - OpError::from(&error) - } -} - -impl From<&url::ParseError> for OpError { - fn from(error: &url::ParseError) -> Self { - Self::new(ErrorKind::URIError, error.to_string()) - } -} -impl From for OpError { - fn from(error: reqwest::Error) -> Self { - OpError::from(&error) - } -} - -impl From<&reqwest::Error> for OpError { - fn from(error: &reqwest::Error) -> Self { - match error.source() { - Some(err_ref) => None - .or_else(|| { - err_ref - .downcast_ref::() - .map(|e| e.clone().into()) - }) - .or_else(|| { - err_ref - .downcast_ref::() - .map(|e| e.to_owned().into()) - }) - .or_else(|| { - err_ref - .downcast_ref::() - .map(|e| e.into()) - }) - .unwrap_or_else(|| Self::new(ErrorKind::Http, error.to_string())), - None => Self::new(ErrorKind::Http, error.to_string()), - } - } -} - -impl From for OpError { - fn from(error: ReadlineError) -> Self { - OpError::from(&error) - } -} - -impl From<&ReadlineError> for OpError { - fn from(error: &ReadlineError) -> Self { - use ReadlineError::*; - let kind = match error { - Io(err) => return OpError::from(err), - Eof => ErrorKind::UnexpectedEof, - Interrupted => ErrorKind::Interrupted, - #[cfg(unix)] - Errno(err) => return (*err).into(), - _ => unimplemented!(), - }; - - Self::new(kind, error.to_string()) - } -} - -impl From for OpError { - fn from(error: serde_json::error::Error) -> Self { - OpError::from(&error) - } -} - -impl From<&serde_json::error::Error> for OpError { - fn from(error: &serde_json::error::Error) -> Self { - use serde_json::error::*; - let kind = match error.classify() { - Category::Io => ErrorKind::TypeError, - Category::Syntax => ErrorKind::TypeError, - Category::Data => ErrorKind::InvalidData, - Category::Eof => ErrorKind::UnexpectedEof, - }; - - Self::new(kind, error.to_string()) - } -} - -#[cfg(unix)] -impl From for OpError { - fn from(error: nix::Error) -> Self { - use nix::errno::Errno::*; - let kind = match error { - nix::Error::Sys(EPERM) => ErrorKind::PermissionDenied, - nix::Error::Sys(EINVAL) => ErrorKind::TypeError, - nix::Error::Sys(ENOENT) => ErrorKind::NotFound, - nix::Error::Sys(ENOTTY) => ErrorKind::BadResource, - nix::Error::Sys(UnknownErrno) => unreachable!(), - nix::Error::Sys(_) => unreachable!(), - nix::Error::InvalidPath => ErrorKind::TypeError, - nix::Error::InvalidUtf8 => ErrorKind::InvalidData, - nix::Error::UnsupportedOperation => unreachable!(), - }; - - Self::new(kind, error.to_string()) - } -} - -impl From for OpError { - fn from(error: dlopen::Error) -> Self { - OpError::from(&error) - } -} - -impl From<&dlopen::Error> for OpError { - fn from(error: &dlopen::Error) -> Self { - use dlopen::Error::*; - let kind = match error { - NullCharacter(_) => ErrorKind::Other, - OpeningLibraryError(e) => return e.into(), - SymbolGettingError(e) => return e.into(), - AddrNotMatchingDll(e) => return e.into(), - NullSymbol => ErrorKind::Other, - }; - - Self::new(kind, error.to_string()) - } -} - -impl From for OpError { - fn from(error: notify::Error) -> Self { - OpError::from(&error) - } -} - -impl From<¬ify::Error> for OpError { - fn from(error: ¬ify::Error) -> Self { - use notify::ErrorKind::*; - let kind = match error.kind { - Generic(_) => ErrorKind::Other, - Io(ref e) => return e.into(), - PathNotFound => ErrorKind::NotFound, - WatchNotFound => ErrorKind::NotFound, - InvalidConfig(_) => ErrorKind::InvalidData, - }; - - Self::new(kind, error.to_string()) - } -} - -impl From for OpError { - fn from(error: SwcDiagnosticBuffer) -> Self { - OpError::from(&error) - } -} - -impl From<&SwcDiagnosticBuffer> for OpError { - fn from(error: &SwcDiagnosticBuffer) -> Self { - Self::new(ErrorKind::Other, error.diagnostics.join(", ")) - } -} - -impl From for OpError { - fn from(error: ErrBox) -> Self { - #[cfg(unix)] - fn unix_error_kind(err: &ErrBox) -> Option { - err.downcast_ref::().map(|e| (*e).into()) - } - - #[cfg(not(unix))] - fn unix_error_kind(_: &ErrBox) -> Option { - None - } - - None - .or_else(|| { - error.downcast_ref::().map(|e| { - OpError::new(error_str_to_kind(&e.kind_str), e.msg.to_string()) - }) - }) - .or_else(|| error.downcast_ref::().map(|e| e.into())) - .or_else(|| error.downcast_ref::().map(|e| e.into())) - .or_else(|| error.downcast_ref::().map(|e| e.into())) - .or_else(|| { - error - .downcast_ref::() - .map(|e| e.into()) - }) - .or_else(|| error.downcast_ref::().map(|e| e.into())) - .or_else(|| error.downcast_ref::().map(|e| e.into())) - .or_else(|| error.downcast_ref::().map(|e| e.into())) - .or_else(|| { - error - .downcast_ref::() - .map(|e| e.into()) - }) - .or_else(|| error.downcast_ref::().map(|e| e.into())) - .or_else(|| error.downcast_ref::().map(|e| e.into())) - .or_else(|| { - error - .downcast_ref::() - .map(|e| e.into()) - }) - .or_else(|| unix_error_kind(&error)) - .unwrap_or_else(|| { - panic!("Can't downcast {:?} to OpError", error); - }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - fn io_error() -> io::Error { - io::Error::from(io::ErrorKind::NotFound) - } - - fn url_error() -> url::ParseError { - url::ParseError::EmptyHost - } - - fn import_map_error() -> ImportMapError { - ImportMapError { - msg: "an import map error".to_string(), - } - } - - #[test] - fn test_simple_error() { - let err = OpError::not_found("foo".to_string()); - assert_eq!(err.kind_str, "NotFound"); - assert_eq!(err.to_string(), "foo"); - } - - #[test] - fn test_io_error() { - let err = OpError::from(io_error()); - assert_eq!(err.kind_str, "NotFound"); - assert_eq!(err.to_string(), "entity not found"); - } - - #[test] - fn test_url_error() { - let err = OpError::from(url_error()); - assert_eq!(err.kind_str, "URIError"); - assert_eq!(err.to_string(), "empty host"); - } - - // TODO find a way to easily test tokio errors and unix errors - - #[test] - fn test_import_map_error() { - let err = OpError::from(import_map_error()); - assert_eq!(err.kind_str, "Other"); - assert_eq!(err.to_string(), "an import map error"); - } - - #[test] - fn test_bad_resource() { - let err = OpError::bad_resource("Resource has been closed".to_string()); - assert_eq!(err.kind_str, "BadResource"); - assert_eq!(err.to_string(), "Resource has been closed"); - } - - #[test] - fn test_bad_resource_id() { - let err = OpError::bad_resource_id(); - assert_eq!(err.kind_str, "BadResource"); - assert_eq!(err.to_string(), "Bad resource ID"); - } - - #[test] - fn test_permission_denied() { - let err = OpError::permission_denied( - "run again with the --allow-net flag".to_string(), - ); - assert_eq!(err.kind_str, "PermissionDenied"); - assert_eq!(err.to_string(), "run again with the --allow-net flag"); - } -} diff --git a/cli/ops/compiler.rs b/cli/ops/compiler.rs index 7ad4aece3c..c35043e2d5 100644 --- a/cli/ops/compiler.rs +++ b/cli/ops/compiler.rs @@ -1,10 +1,10 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. use super::dispatch_json::{JsonOp, Value}; -use crate::op_error::OpError; use crate::ops::json_op; use crate::state::State; use deno_core::CoreIsolate; use deno_core::CoreIsolateState; +use deno_core::ErrBox; use deno_core::ZeroCopyBuf; use std::rc::Rc; use std::sync::Arc; @@ -36,18 +36,18 @@ pub fn compiler_op( &mut deno_core::CoreIsolateState, Value, &mut [ZeroCopyBuf], -) -> Result +) -> Result where D: Fn( Arc>>, Value, &mut [ZeroCopyBuf], - ) -> Result, + ) -> Result, { move |_isolate_state: &mut CoreIsolateState, args: Value, zero_copy: &mut [ZeroCopyBuf]| - -> Result { + -> Result { dispatcher(response.clone(), args, zero_copy) } } @@ -56,7 +56,7 @@ fn op_compiler_respond( response: Arc>>, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let mut r = response.lock().unwrap(); assert!( r.is_none(), diff --git a/cli/ops/dispatch_json.rs b/cli/ops/dispatch_json.rs index 5ed7e6c6d8..6e0eddac89 100644 --- a/cli/ops/dispatch_json.rs +++ b/cli/ops/dispatch_json.rs @@ -1,7 +1,7 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -use crate::op_error::OpError; use deno_core::Buf; use deno_core::CoreIsolateState; +use deno_core::ErrBox; use deno_core::Op; use deno_core::ZeroCopyBuf; use futures::future::FutureExt; @@ -11,7 +11,7 @@ pub use serde_json::Value; use std::future::Future; use std::pin::Pin; -pub type JsonResult = Result; +pub type JsonResult = Result; pub type AsyncJsonOp = Pin>>; @@ -23,17 +23,18 @@ pub enum JsonOp { AsyncUnref(AsyncJsonOp), } -fn json_err(err: OpError) -> Value { - json!({ - "message": err.msg, - "kind": err.kind_str, - }) -} - -pub fn serialize_result(promise_id: Option, result: JsonResult) -> Buf { +pub fn serialize_result( + rust_err_to_json_fn: &'static dyn deno_core::RustErrToJsonFn, + promise_id: Option, + result: JsonResult, +) -> Buf { let value = match result { Ok(v) => json!({ "ok": v, "promiseId": promise_id }), - Err(err) => json!({ "err": json_err(err), "promiseId": promise_id }), + Err(err) => { + let serialized_err = (&rust_err_to_json_fn)(&err); + let err_value: Value = serde_json::from_slice(&serialized_err).unwrap(); + json!({ "err": err_value, "promiseId": promise_id }) + } }; serde_json::to_vec(&value).unwrap().into_boxed_slice() } @@ -52,14 +53,15 @@ where &mut CoreIsolateState, Value, &mut [ZeroCopyBuf], - ) -> Result, + ) -> Result, { move |isolate_state: &mut CoreIsolateState, zero_copy: &mut [ZeroCopyBuf]| { assert!(!zero_copy.is_empty(), "Expected JSON string at position 0"); + let rust_err_to_json_fn = isolate_state.rust_err_to_json_fn; let async_args: AsyncArgs = match serde_json::from_slice(&zero_copy[0]) { Ok(args) => args, Err(e) => { - let buf = serialize_result(None, Err(OpError::from(e))); + let buf = serialize_result(rust_err_to_json_fn, None, Err(e.into())); return Op::Sync(buf); } }; @@ -67,31 +69,44 @@ where let is_sync = promise_id.is_none(); let result = serde_json::from_slice(&zero_copy[0]) - .map_err(OpError::from) + .map_err(ErrBox::from) .and_then(|args| d(isolate_state, args, &mut zero_copy[1..])); // Convert to Op match result { Ok(JsonOp::Sync(sync_value)) => { assert!(promise_id.is_none()); - Op::Sync(serialize_result(promise_id, Ok(sync_value))) + Op::Sync(serialize_result( + rust_err_to_json_fn, + promise_id, + Ok(sync_value), + )) } Ok(JsonOp::Async(fut)) => { assert!(promise_id.is_some()); let fut2 = fut.then(move |result| { - futures::future::ready(serialize_result(promise_id, result)) + futures::future::ready(serialize_result( + rust_err_to_json_fn, + promise_id, + result, + )) }); Op::Async(fut2.boxed_local()) } Ok(JsonOp::AsyncUnref(fut)) => { assert!(promise_id.is_some()); let fut2 = fut.then(move |result| { - futures::future::ready(serialize_result(promise_id, result)) + futures::future::ready(serialize_result( + rust_err_to_json_fn, + promise_id, + result, + )) }); Op::AsyncUnref(fut2.boxed_local()) } Err(sync_err) => { - let buf = serialize_result(promise_id, Err(sync_err)); + let buf = + serialize_result(rust_err_to_json_fn, promise_id, Err(sync_err)); if is_sync { Op::Sync(buf) } else { @@ -102,7 +117,7 @@ where } } -pub fn blocking_json(is_sync: bool, f: F) -> Result +pub fn blocking_json(is_sync: bool, f: F) -> Result where F: 'static + Send + FnOnce() -> JsonResult, { diff --git a/cli/ops/dispatch_minimal.rs b/cli/ops/dispatch_minimal.rs index df7d48b4e4..b0dc7ad8a0 100644 --- a/cli/ops/dispatch_minimal.rs +++ b/cli/ops/dispatch_minimal.rs @@ -4,19 +4,22 @@ //! alternative to flatbuffers using a very simple list of int32s to lay out //! messages. The first i32 is used to determine if a message a flatbuffer //! message or a "minimal" message. -use crate::op_error::OpError; -use byteorder::{LittleEndian, WriteBytesExt}; +use crate::errors::get_error_class; use deno_core::Buf; use deno_core::CoreIsolateState; +use deno_core::ErrBox; use deno_core::Op; use deno_core::ZeroCopyBuf; use futures::future::FutureExt; use std::future::Future; +use std::iter::repeat; +use std::mem::size_of_val; use std::pin::Pin; +use std::slice; pub enum MinimalOp { - Sync(Result), - Async(Pin>>>), + Sync(Result), + Async(Pin>>>), } #[derive(Copy, Clone, Debug, PartialEq)] @@ -40,24 +43,38 @@ pub struct ErrorRecord { pub promise_id: i32, pub arg: i32, pub error_len: i32, - pub error_code: Vec, + pub error_class: &'static [u8], pub error_message: Vec, } impl Into for ErrorRecord { fn into(self) -> Buf { - let v32: Vec = vec![self.promise_id, self.arg, self.error_len]; - let mut v8: Vec = Vec::new(); - for n in v32 { - v8.write_i32::(n).unwrap(); - } - let mut code = self.error_code; - let mut message = self.error_message; - v8.append(&mut code); - v8.append(&mut message); - // Align to 32bit word, padding with the space character. - v8.resize((v8.len() + 3usize) & !3usize, b' '); - v8.into_boxed_slice() + let Self { + promise_id, + arg, + error_len, + error_class, + error_message, + .. + } = self; + let header_i32 = [promise_id, arg, error_len]; + let header_u8 = unsafe { + slice::from_raw_parts( + &header_i32 as *const _ as *const u8, + size_of_val(&header_i32), + ) + }; + let padded_len = + (header_u8.len() + error_class.len() + error_message.len() + 3usize) + & !3usize; + header_u8 + .iter() + .cloned() + .chain(error_class.iter().cloned()) + .chain(error_message.into_iter()) + .chain(repeat(b' ')) + .take(padded_len) + .collect() } } @@ -71,7 +88,7 @@ fn test_error_record() { promise_id: 1, arg: -1, error_len: 11, - error_code: "BadResource".to_string().as_bytes().to_owned(), + error_class: b"BadResource", error_message: "Error".to_string().as_bytes().to_owned(), }; let buf: Buf = err_record.into(); @@ -129,13 +146,14 @@ where let mut record = match parse_min_record(&zero_copy[0]) { Some(r) => r, None => { - let e = OpError::type_error("Unparsable control buffer".to_string()); + let error = ErrBox::type_error("Unparsable control buffer"); + let error_class = get_error_class(&error); let error_record = ErrorRecord { promise_id: 0, arg: -1, - error_len: e.kind_str.len() as i32, - error_code: e.kind_str.as_bytes().to_owned(), - error_message: e.msg.as_bytes().to_owned(), + error_len: error_class.len() as i32, + error_class: error_class.as_bytes(), + error_message: error.to_string().as_bytes().to_owned(), }; return Op::Sync(error_record.into()); } @@ -151,12 +169,13 @@ where record.into() } Err(err) => { + let error_class = get_error_class(&err); let error_record = ErrorRecord { promise_id: record.promise_id, arg: -1, - error_len: err.kind_str.len() as i32, - error_code: err.kind_str.as_bytes().to_owned(), - error_message: err.msg.as_bytes().to_owned(), + error_len: error_class.len() as i32, + error_class: error_class.as_bytes(), + error_message: err.to_string().as_bytes().to_owned(), }; error_record.into() } @@ -169,12 +188,13 @@ where record.into() } Err(err) => { + let error_class = get_error_class(&err); let error_record = ErrorRecord { promise_id: record.promise_id, arg: -1, - error_len: err.kind_str.len() as i32, - error_code: err.kind_str.as_bytes().to_owned(), - error_message: err.msg.as_bytes().to_owned(), + error_len: error_class.len() as i32, + error_class: error_class.as_bytes(), + error_message: err.to_string().as_bytes().to_owned(), }; error_record.into() } diff --git a/cli/ops/errors.rs b/cli/ops/errors.rs index 02295d7f81..b9405fe541 100644 --- a/cli/ops/errors.rs +++ b/cli/ops/errors.rs @@ -1,11 +1,11 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. use super::dispatch_json::{Deserialize, JsonOp, Value}; use crate::diagnostics::Diagnostic; -use crate::op_error::OpError; use crate::source_maps::get_orig_position; use crate::source_maps::CachedMaps; use crate::state::State; use deno_core::CoreIsolate; +use deno_core::ErrBox; use deno_core::ZeroCopyBuf; use std::collections::HashMap; use std::rc::Rc; @@ -33,7 +33,7 @@ fn op_apply_source_map( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: ApplySourceMap = serde_json::from_value(args)?; let mut mappings_map: CachedMaps = HashMap::new(); @@ -57,7 +57,7 @@ fn op_format_diagnostic( _state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let diagnostic = serde_json::from_value::(args)?; Ok(JsonOp::Sync(json!(diagnostic.to_string()))) } diff --git a/cli/ops/fetch.rs b/cli/ops/fetch.rs index b320222edc..c7bfaf9f17 100644 --- a/cli/ops/fetch.rs +++ b/cli/ops/fetch.rs @@ -2,10 +2,10 @@ use super::dispatch_json::{Deserialize, JsonOp, Value}; use super::io::{StreamResource, StreamResourceHolder}; use crate::http_util::{create_http_client, HttpBody}; -use crate::op_error::OpError; use crate::state::State; use deno_core::CoreIsolate; use deno_core::CoreIsolateState; +use deno_core::ErrBox; use deno_core::ZeroCopyBuf; use futures::future::FutureExt; use http::header::HeaderName; @@ -38,7 +38,7 @@ pub fn op_fetch( state: &Rc, args: Value, data: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: FetchArgs = serde_json::from_value(args)?; let url = args.url; let resource_table_ = isolate_state.resource_table.borrow(); @@ -47,7 +47,7 @@ pub fn op_fetch( let client = if let Some(rid) = args.client_rid { let r = resource_table_ .get::(rid) - .ok_or_else(OpError::bad_resource_id)?; + .ok_or_else(ErrBox::bad_resource_id)?; &r.client } else { client_ref_mut = state.http_client.borrow_mut(); @@ -55,17 +55,16 @@ pub fn op_fetch( }; let method = match args.method { - Some(method_str) => Method::from_bytes(method_str.as_bytes()) - .map_err(|e| OpError::other(e.to_string()))?, + Some(method_str) => Method::from_bytes(method_str.as_bytes())?, None => Method::GET, }; - let url_ = url::Url::parse(&url).map_err(OpError::from)?; + let url_ = url::Url::parse(&url)?; // Check scheme before asking for net permission let scheme = url_.scheme(); if scheme != "http" && scheme != "https" { - return Err(OpError::type_error(format!( + return Err(ErrBox::type_error(format!( "scheme '{}' not supported", scheme ))); @@ -142,7 +141,7 @@ fn op_create_http_client( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: CreateHttpClientOptions = serde_json::from_value(args)?; let mut resource_table = isolate_state.resource_table.borrow_mut(); diff --git a/cli/ops/fs.rs b/cli/ops/fs.rs index 0fd6ad5efd..00736b813c 100644 --- a/cli/ops/fs.rs +++ b/cli/ops/fs.rs @@ -3,12 +3,12 @@ use super::dispatch_json::{blocking_json, Deserialize, JsonOp, Value}; use super::io::std_file_resource; use super::io::{FileMetadata, StreamResource, StreamResourceHolder}; -use crate::op_error::OpError; use crate::ops::dispatch_json::JsonResult; use crate::state::State; use deno_core::BufVec; use deno_core::CoreIsolate; use deno_core::CoreIsolateState; +use deno_core::ErrBox; use deno_core::ResourceTable; use deno_core::ZeroCopyBuf; use futures::future::FutureExt; @@ -55,8 +55,11 @@ pub fn init(i: &mut CoreIsolate, s: &Rc) { i.register_op("op_utime", s.stateful_json_op(op_utime)); } -fn into_string(s: std::ffi::OsString) -> Result { - s.into_string().map_err(|_| OpError::invalid_utf8()) +fn into_string(s: std::ffi::OsString) -> Result { + s.into_string().map_err(|s| { + let message = format!("File name or path {:?} is not valid UTF-8", s); + ErrBox::new("InvalidData", message) + }) } #[derive(Deserialize)] @@ -82,7 +85,7 @@ struct OpenOptions { fn open_helper( state: &State, args: Value, -) -> Result<(PathBuf, std::fs::OpenOptions), OpError> { +) -> Result<(PathBuf, std::fs::OpenOptions), ErrBox> { let args: OpenArgs = serde_json::from_value(args)?; let path = Path::new(&args.path).to_path_buf(); @@ -125,7 +128,7 @@ fn op_open_sync( resource_table: &mut ResourceTable, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let (path, open_options) = open_helper(state, args)?; let std_file = open_options.open(path)?; let tokio_file = tokio::fs::File::from_std(std_file); @@ -144,7 +147,7 @@ async fn op_open_async( resource_table: Rc>, args: Value, _zero_copy: BufVec, -) -> Result { +) -> Result { let (path, open_options) = open_helper(&state, args)?; let tokio_file = tokio::fs::OpenOptions::from(open_options) .open(path) @@ -173,7 +176,7 @@ fn op_seek( _state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { use std::io::{Seek, SeekFrom}; let args: SeekArgs = serde_json::from_value(args)?; let rid = args.rid as u32; @@ -185,10 +188,7 @@ fn op_seek( 1 => SeekFrom::Current(offset), 2 => SeekFrom::End(offset), _ => { - return Err(OpError::type_error(format!( - "Invalid seek mode: {}", - whence - ))); + return Err(ErrBox::type_error(format!("Invalid seek mode: {}", whence))); } }; @@ -198,8 +198,8 @@ fn op_seek( if is_sync { let mut resource_table = resource_table.borrow_mut(); let pos = std_file_resource(&mut resource_table, rid, |r| match r { - Ok(std_file) => std_file.seek(seek_from).map_err(OpError::from), - Err(_) => Err(OpError::type_error( + Ok(std_file) => std_file.seek(seek_from).map_err(ErrBox::from), + Err(_) => Err(ErrBox::type_error( "cannot seek on this type of resource".to_string(), )), })?; @@ -210,8 +210,8 @@ fn op_seek( let fut = async move { let mut resource_table = resource_table.borrow_mut(); let pos = std_file_resource(&mut resource_table, rid, |r| match r { - Ok(std_file) => std_file.seek(seek_from).map_err(OpError::from), - Err(_) => Err(OpError::type_error( + Ok(std_file) => std_file.seek(seek_from).map_err(ErrBox::from), + Err(_) => Err(ErrBox::type_error( "cannot seek on this type of resource".to_string(), )), })?; @@ -233,7 +233,7 @@ fn op_fdatasync( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { state.check_unstable("Deno.fdatasync"); let args: FdatasyncArgs = serde_json::from_value(args)?; let rid = args.rid as u32; @@ -244,8 +244,8 @@ fn op_fdatasync( if is_sync { let mut resource_table = resource_table.borrow_mut(); std_file_resource(&mut resource_table, rid, |r| match r { - Ok(std_file) => std_file.sync_data().map_err(OpError::from), - Err(_) => Err(OpError::type_error( + Ok(std_file) => std_file.sync_data().map_err(ErrBox::from), + Err(_) => Err(ErrBox::type_error( "cannot sync this type of resource".to_string(), )), })?; @@ -254,8 +254,8 @@ fn op_fdatasync( let fut = async move { let mut resource_table = resource_table.borrow_mut(); std_file_resource(&mut resource_table, rid, |r| match r { - Ok(std_file) => std_file.sync_data().map_err(OpError::from), - Err(_) => Err(OpError::type_error( + Ok(std_file) => std_file.sync_data().map_err(ErrBox::from), + Err(_) => Err(ErrBox::type_error( "cannot sync this type of resource".to_string(), )), })?; @@ -277,7 +277,7 @@ fn op_fsync( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { state.check_unstable("Deno.fsync"); let args: FsyncArgs = serde_json::from_value(args)?; let rid = args.rid as u32; @@ -288,8 +288,8 @@ fn op_fsync( if is_sync { let mut resource_table = resource_table.borrow_mut(); std_file_resource(&mut resource_table, rid, |r| match r { - Ok(std_file) => std_file.sync_all().map_err(OpError::from), - Err(_) => Err(OpError::type_error( + Ok(std_file) => std_file.sync_all().map_err(ErrBox::from), + Err(_) => Err(ErrBox::type_error( "cannot sync this type of resource".to_string(), )), })?; @@ -298,8 +298,8 @@ fn op_fsync( let fut = async move { let mut resource_table = resource_table.borrow_mut(); std_file_resource(&mut resource_table, rid, |r| match r { - Ok(std_file) => std_file.sync_all().map_err(OpError::from), - Err(_) => Err(OpError::type_error( + Ok(std_file) => std_file.sync_all().map_err(ErrBox::from), + Err(_) => Err(ErrBox::type_error( "cannot sync this type of resource".to_string(), )), })?; @@ -321,7 +321,7 @@ fn op_fstat( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { state.check_unstable("Deno.fstat"); let args: FstatArgs = serde_json::from_value(args)?; let rid = args.rid as u32; @@ -332,8 +332,8 @@ fn op_fstat( if is_sync { let mut resource_table = resource_table.borrow_mut(); let metadata = std_file_resource(&mut resource_table, rid, |r| match r { - Ok(std_file) => std_file.metadata().map_err(OpError::from), - Err(_) => Err(OpError::type_error( + Ok(std_file) => std_file.metadata().map_err(ErrBox::from), + Err(_) => Err(ErrBox::type_error( "cannot stat this type of resource".to_string(), )), })?; @@ -343,8 +343,8 @@ fn op_fstat( let mut resource_table = resource_table.borrow_mut(); let metadata = std_file_resource(&mut resource_table, rid, |r| match r { - Ok(std_file) => std_file.metadata().map_err(OpError::from), - Err(_) => Err(OpError::type_error( + Ok(std_file) => std_file.metadata().map_err(ErrBox::from), + Err(_) => Err(ErrBox::type_error( "cannot stat this type of resource".to_string(), )), })?; @@ -363,7 +363,7 @@ fn op_umask( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { state.check_unstable("Deno.umask"); let args: UmaskArgs = serde_json::from_value(args)?; // TODO implement umask for Windows @@ -372,7 +372,7 @@ fn op_umask( #[cfg(not(unix))] { let _ = args.mask; // avoid unused warning. - Err(OpError::not_implemented()) + Err(ErrBox::not_supported()) } #[cfg(unix)] { @@ -401,7 +401,7 @@ fn op_chdir( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: ChdirArgs = serde_json::from_value(args)?; let d = PathBuf::from(&args.directory); state.check_read(&d)?; @@ -422,7 +422,7 @@ fn op_mkdir( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: MkdirArgs = serde_json::from_value(args)?; let path = Path::new(&args.path).to_path_buf(); let mode = args.mode.unwrap_or(0o777) & 0o777; @@ -456,7 +456,7 @@ fn op_chmod( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: ChmodArgs = serde_json::from_value(args)?; let path = Path::new(&args.path).to_path_buf(); let mode = args.mode & 0o777; @@ -478,7 +478,7 @@ fn op_chmod( { // Still check file/dir exists on Windows let _metadata = std::fs::metadata(&path)?; - Err(OpError::not_implemented()) + Err(ErrBox::not_supported()) } }) } @@ -496,7 +496,7 @@ fn op_chown( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: ChownArgs = serde_json::from_value(args)?; let path = Path::new(&args.path).to_path_buf(); @@ -515,9 +515,7 @@ fn op_chown( } // TODO Implement chown for Windows #[cfg(not(unix))] - { - Err(OpError::not_implemented()) - } + Err(ErrBox::not_supported()) }) } @@ -533,7 +531,7 @@ fn op_remove( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: RemoveArgs = serde_json::from_value(args)?; let path = PathBuf::from(&args.path); let recursive = args.recursive; @@ -587,7 +585,7 @@ fn op_copy_file( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: CopyFileArgs = serde_json::from_value(args)?; let from = PathBuf::from(&args.from); let to = PathBuf::from(&args.to); @@ -602,7 +600,7 @@ fn op_copy_file( // See https://github.com/rust-lang/rust/issues/54800 // Once the issue is resolved, we should remove this workaround. if cfg!(unix) && !from.is_file() { - return Err(OpError::not_found("File not found".to_string())); + return Err(ErrBox::new("NotFound", "File not found")); } // returns size of from as u64 (we ignore) @@ -681,7 +679,7 @@ fn op_stat( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: StatArgs = serde_json::from_value(args)?; let path = PathBuf::from(&args.path); let lstat = args.lstat; @@ -711,7 +709,7 @@ fn op_realpath( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: RealpathArgs = serde_json::from_value(args)?; let path = PathBuf::from(&args.path); @@ -746,7 +744,7 @@ fn op_read_dir( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: ReadDirArgs = serde_json::from_value(args)?; let path = PathBuf::from(&args.path); @@ -789,7 +787,7 @@ fn op_rename( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: RenameArgs = serde_json::from_value(args)?; let oldpath = PathBuf::from(&args.oldpath); let newpath = PathBuf::from(&args.newpath); @@ -818,7 +816,7 @@ fn op_link( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { state.check_unstable("Deno.link"); let args: LinkArgs = serde_json::from_value(args)?; let oldpath = PathBuf::from(&args.oldpath); @@ -856,7 +854,7 @@ fn op_symlink( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { state.check_unstable("Deno.symlink"); let args: SymlinkArgs = serde_json::from_value(args)?; let oldpath = PathBuf::from(&args.oldpath); @@ -881,7 +879,7 @@ fn op_symlink( Some(options) => match options._type.as_ref() { "file" => symlink_file(&oldpath, &newpath)?, "dir" => symlink_dir(&oldpath, &newpath)?, - _ => return Err(OpError::type_error("unsupported type".to_string())), + _ => return Err(ErrBox::type_error("unsupported type".to_string())), }, None => { let old_meta = std::fs::metadata(&oldpath); @@ -893,7 +891,7 @@ fn op_symlink( symlink_dir(&oldpath, &newpath)? } } - Err(_) => return Err(OpError::type_error( + Err(_) => return Err(ErrBox::type_error( "you must pass a `options` argument for non-existent target path in windows" .to_string(), )), @@ -916,7 +914,7 @@ fn op_read_link( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: ReadLinkArgs = serde_json::from_value(args)?; let path = PathBuf::from(&args.path); @@ -944,7 +942,7 @@ fn op_ftruncate( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { state.check_unstable("Deno.ftruncate"); let args: FtruncateArgs = serde_json::from_value(args)?; let rid = args.rid as u32; @@ -956,8 +954,8 @@ fn op_ftruncate( if is_sync { let mut resource_table = resource_table.borrow_mut(); std_file_resource(&mut resource_table, rid, |r| match r { - Ok(std_file) => std_file.set_len(len).map_err(OpError::from), - Err(_) => Err(OpError::type_error( + Ok(std_file) => std_file.set_len(len).map_err(ErrBox::from), + Err(_) => Err(ErrBox::type_error( "cannot truncate this type of resource".to_string(), )), })?; @@ -966,8 +964,8 @@ fn op_ftruncate( let fut = async move { let mut resource_table = resource_table.borrow_mut(); std_file_resource(&mut resource_table, rid, |r| match r { - Ok(std_file) => std_file.set_len(len).map_err(OpError::from), - Err(_) => Err(OpError::type_error( + Ok(std_file) => std_file.set_len(len).map_err(ErrBox::from), + Err(_) => Err(ErrBox::type_error( "cannot truncate this type of resource".to_string(), )), })?; @@ -989,7 +987,7 @@ fn op_truncate( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: TruncateArgs = serde_json::from_value(args)?; let path = PathBuf::from(&args.path); let len = args.len; @@ -1063,7 +1061,7 @@ fn op_make_temp_dir( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: MakeTempArgs = serde_json::from_value(args)?; let dir = args.dir.map(|s| PathBuf::from(&s)); @@ -1094,7 +1092,7 @@ fn op_make_temp_file( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: MakeTempArgs = serde_json::from_value(args)?; let dir = args.dir.map(|s| PathBuf::from(&s)); @@ -1134,7 +1132,7 @@ fn op_utime( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { state.check_unstable("Deno.utime"); let args: UtimeArgs = serde_json::from_value(args)?; @@ -1154,7 +1152,7 @@ fn op_cwd( state: &Rc, _args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let path = current_dir()?; state.check_read_blind(&path, "CWD")?; let path_str = into_string(path.into_os_string())?; diff --git a/cli/ops/fs_events.rs b/cli/ops/fs_events.rs index fcaf138f82..e798796be6 100644 --- a/cli/ops/fs_events.rs +++ b/cli/ops/fs_events.rs @@ -1,6 +1,5 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. use super::dispatch_json::{Deserialize, JsonOp, Value}; -use crate::op_error::OpError; use crate::state::State; use deno_core::CoreIsolate; use deno_core::CoreIsolateState; @@ -68,7 +67,7 @@ pub fn op_fs_events_open( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { #[derive(Deserialize)] struct OpenArgs { recursive: bool, @@ -84,8 +83,7 @@ pub fn op_fs_events_open( // Ignore result, if send failed it means that watcher was already closed, // but not all messages have been flushed. let _ = sender.try_send(res2); - }) - .map_err(ErrBox::from)?; + })?; let recursive_mode = if args.recursive { RecursiveMode::Recursive } else { @@ -93,7 +91,7 @@ pub fn op_fs_events_open( }; for path in &args.paths { state.check_read(&PathBuf::from(path))?; - watcher.watch(path, recursive_mode).map_err(ErrBox::from)?; + watcher.watch(path, recursive_mode)?; } let resource = FsEventsResource { watcher, receiver }; let mut resource_table = isolate_state.resource_table.borrow_mut(); @@ -106,7 +104,7 @@ pub fn op_fs_events_poll( _state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { #[derive(Deserialize)] struct PollArgs { rid: u32, @@ -117,13 +115,13 @@ pub fn op_fs_events_poll( let mut resource_table = resource_table.borrow_mut(); let watcher = resource_table .get_mut::(rid) - .ok_or_else(OpError::bad_resource_id)?; + .ok_or_else(ErrBox::bad_resource_id)?; watcher .receiver .poll_recv(cx) .map(|maybe_result| match maybe_result { Some(Ok(value)) => Ok(json!({ "value": value, "done": false })), - Some(Err(err)) => Err(OpError::from(err)), + Some(Err(err)) => Err(err), None => Ok(json!({ "done": true })), }) }); diff --git a/cli/ops/idna.rs b/cli/ops/idna.rs index c76ceba89b..9585e09776 100644 --- a/cli/ops/idna.rs +++ b/cli/ops/idna.rs @@ -3,9 +3,9 @@ //! https://url.spec.whatwg.org/#idna use super::dispatch_json::{Deserialize, JsonOp, Value}; -use crate::op_error::OpError; use crate::state::State; use deno_core::CoreIsolate; +use deno_core::ErrBox; use deno_core::ZeroCopyBuf; use idna::{domain_to_ascii, domain_to_ascii_strict}; use std::rc::Rc; @@ -25,14 +25,16 @@ fn op_domain_to_ascii( _state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: DomainToAscii = serde_json::from_value(args)?; - let domain = if args.be_strict { + if args.be_strict { domain_to_ascii_strict(args.domain.as_str()) - .map_err(|_| OpError::invalid_domain_error())? } else { domain_to_ascii(args.domain.as_str()) - .map_err(|_| OpError::invalid_domain_error())? - }; - Ok(JsonOp::Sync(json!(domain))) + } + .map_err(|err| { + let message = format!("Invalid IDNA encoded domain name: {:?}", err); + ErrBox::new("URIError", message) + }) + .map(|domain| JsonOp::Sync(json!(domain))) } diff --git a/cli/ops/io.rs b/cli/ops/io.rs index 4d03e0ef71..6b97f542cf 100644 --- a/cli/ops/io.rs +++ b/cli/ops/io.rs @@ -1,9 +1,9 @@ use super::dispatch_minimal::MinimalOp; use crate::http_util::HttpBody; -use crate::op_error::OpError; use crate::state::State; use deno_core::CoreIsolate; use deno_core::CoreIsolateState; +use deno_core::ErrBox; use deno_core::ResourceTable; use deno_core::ZeroCopyBuf; use futures::future::poll_fn; @@ -116,8 +116,8 @@ fn get_stdio_stream( } } -fn no_buffer_specified() -> OpError { - OpError::type_error("no buffer specified".to_string()) +fn no_buffer_specified() -> ErrBox { + ErrBox::type_error("no buffer specified".to_string()) } #[cfg(unix)] @@ -159,7 +159,7 @@ impl Drop for StreamResourceHolder { } impl StreamResourceHolder { - pub fn track_task(&mut self, cx: &Context) -> Result { + pub fn track_task(&mut self, cx: &Context) -> Result { let waker = futures::task::AtomicWaker::new(); waker.register(cx.waker()); // Its OK if it overflows @@ -200,13 +200,13 @@ impl UnpinAsyncRead for T {} impl UnpinAsyncWrite for T {} /// `DenoAsyncRead` is the same as the `tokio_io::AsyncRead` trait -/// but uses an `OpError` error instead of `std::io:Error` +/// but uses an `ErrBox` error instead of `std::io:Error` pub trait DenoAsyncRead { fn poll_read( &mut self, cx: &mut Context, buf: &mut [u8], - ) -> Poll>; + ) -> Poll>; } impl DenoAsyncRead for StreamResource { @@ -214,11 +214,11 @@ impl DenoAsyncRead for StreamResource { &mut self, cx: &mut Context, buf: &mut [u8], - ) -> Poll> { + ) -> Poll> { use StreamResource::*; let f: &mut dyn UnpinAsyncRead = match self { FsFile(Some((f, _))) => f, - FsFile(None) => return Poll::Ready(Err(OpError::resource_unavailable())), + FsFile(None) => return Poll::Ready(Err(ErrBox::resource_unavailable())), Stdin(f, _) => f, TcpStream(Some(f)) => f, #[cfg(not(windows))] @@ -228,7 +228,7 @@ impl DenoAsyncRead for StreamResource { ChildStdout(f) => f, ChildStderr(f) => f, HttpBody(f) => f, - _ => return Err(OpError::bad_resource_id()).into(), + _ => return Err(ErrBox::bad_resource_id()).into(), }; let v = ready!(Pin::new(f).poll_read(cx, buf))?; Ok(v).into() @@ -260,9 +260,9 @@ pub fn op_read( std_file .read(&mut zero_copy[0]) .map(|n: usize| n as i32) - .map_err(OpError::from) + .map_err(ErrBox::from) } - Err(_) => Err(OpError::type_error( + Err(_) => Err(ErrBox::type_error( "sync read not allowed on this resource".to_string(), )), }) @@ -274,13 +274,10 @@ pub fn op_read( let mut resource_table = resource_table.borrow_mut(); let resource_holder = resource_table .get_mut::(rid as u32) - .ok_or_else(OpError::bad_resource_id)?; + .ok_or_else(ErrBox::bad_resource_id)?; let mut task_tracker_id: Option = None; - let nread = match resource_holder - .resource - .poll_read(cx, &mut zero_copy) - .map_err(OpError::from) + let nread = match resource_holder.resource.poll_read(cx, &mut zero_copy) { Poll::Ready(t) => { if let Some(id) = task_tracker_id { @@ -301,17 +298,17 @@ pub fn op_read( } /// `DenoAsyncWrite` is the same as the `tokio_io::AsyncWrite` trait -/// but uses an `OpError` error instead of `std::io:Error` +/// but uses an `ErrBox` error instead of `std::io:Error` pub trait DenoAsyncWrite { fn poll_write( &mut self, cx: &mut Context, buf: &[u8], - ) -> Poll>; + ) -> Poll>; - fn poll_close(&mut self, cx: &mut Context) -> Poll>; + fn poll_close(&mut self, cx: &mut Context) -> Poll>; - fn poll_flush(&mut self, cx: &mut Context) -> Poll>; + fn poll_flush(&mut self, cx: &mut Context) -> Poll>; } impl DenoAsyncWrite for StreamResource { @@ -319,7 +316,7 @@ impl DenoAsyncWrite for StreamResource { &mut self, cx: &mut Context, buf: &[u8], - ) -> Poll> { + ) -> Poll> { use StreamResource::*; let f: &mut dyn UnpinAsyncWrite = match self { FsFile(Some((f, _))) => f, @@ -330,14 +327,14 @@ impl DenoAsyncWrite for StreamResource { ClientTlsStream(f) => f, ServerTlsStream(f) => f, ChildStdin(f) => f, - _ => return Err(OpError::bad_resource_id()).into(), + _ => return Err(ErrBox::bad_resource_id()).into(), }; let v = ready!(Pin::new(f).poll_write(cx, buf))?; Ok(v).into() } - fn poll_flush(&mut self, cx: &mut Context) -> Poll> { + fn poll_flush(&mut self, cx: &mut Context) -> Poll> { use StreamResource::*; let f: &mut dyn UnpinAsyncWrite = match self { FsFile(Some((f, _))) => f, @@ -348,14 +345,14 @@ impl DenoAsyncWrite for StreamResource { ClientTlsStream(f) => f, ServerTlsStream(f) => f, ChildStdin(f) => f, - _ => return Err(OpError::bad_resource_id()).into(), + _ => return Err(ErrBox::bad_resource_id()).into(), }; ready!(Pin::new(f).poll_flush(cx))?; Ok(()).into() } - fn poll_close(&mut self, _cx: &mut Context) -> Poll> { + fn poll_close(&mut self, _cx: &mut Context) -> Poll> { unimplemented!() } } @@ -384,9 +381,9 @@ pub fn op_write( std_file .write(&zero_copy[0]) .map(|nwritten: usize| nwritten as i32) - .map_err(OpError::from) + .map_err(ErrBox::from) } - Err(_) => Err(OpError::type_error( + Err(_) => Err(ErrBox::type_error( "sync read not allowed on this resource".to_string(), )), }) @@ -400,7 +397,7 @@ pub fn op_write( let mut resource_table = resource_table.borrow_mut(); let resource_holder = resource_table .get_mut::(rid as u32) - .ok_or_else(OpError::bad_resource_id)?; + .ok_or_else(ErrBox::bad_resource_id)?; resource_holder.resource.poll_write(cx, &zero_copy) }) .await?; @@ -413,7 +410,7 @@ pub fn op_write( let mut resource_table = resource_table.borrow_mut(); let resource_holder = resource_table .get_mut::(rid as u32) - .ok_or_else(OpError::bad_resource_id)?; + .ok_or_else(ErrBox::bad_resource_id)?; resource_holder.resource.poll_flush(cx) }) .await?; @@ -436,11 +433,10 @@ pub fn std_file_resource( resource_table: &mut ResourceTable, rid: u32, mut f: F, -) -> Result +) -> Result where - F: FnMut( - Result<&mut std::fs::File, &mut StreamResource>, - ) -> Result, + F: + FnMut(Result<&mut std::fs::File, &mut StreamResource>) -> Result, { // First we look up the rid in the resource table. let mut r = resource_table.get_mut::(rid); @@ -469,16 +465,16 @@ where // some operation is in-flight. resource_holder.resource = StreamResource::FsFile(Some((tokio_file, metadata))); - Err(OpError::resource_unavailable()) + Err(ErrBox::resource_unavailable()) } } } else { - Err(OpError::resource_unavailable()) + Err(ErrBox::resource_unavailable()) } } _ => f(Err(&mut resource_holder.resource)), } } else { - Err(OpError::bad_resource_id()) + Err(ErrBox::bad_resource_id()) } } diff --git a/cli/ops/net.rs b/cli/ops/net.rs index 445c691063..25639cb79b 100644 --- a/cli/ops/net.rs +++ b/cli/ops/net.rs @@ -1,16 +1,15 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. use super::dispatch_json::{Deserialize, JsonOp, Value}; use super::io::{StreamResource, StreamResourceHolder}; -use crate::op_error::OpError; use crate::resolve_addr::resolve_addr; use crate::state::State; use deno_core::CoreIsolate; use deno_core::CoreIsolateState; +use deno_core::ErrBox; use deno_core::ResourceTable; use deno_core::ZeroCopyBuf; use futures::future::poll_fn; use futures::future::FutureExt; -use std::convert::From; use std::net::Shutdown; use std::net::SocketAddr; use std::rc::Rc; @@ -45,7 +44,7 @@ fn accept_tcp( isolate_state: &mut CoreIsolateState, args: AcceptArgs, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let rid = args.rid as u32; let resource_table = isolate_state.resource_table.clone(); @@ -55,10 +54,10 @@ fn accept_tcp( let listener_resource = resource_table .get_mut::(rid) .ok_or_else(|| { - OpError::bad_resource("Listener has been closed".to_string()) + ErrBox::bad_resource("Listener has been closed".to_string()) })?; let listener = &mut listener_resource.listener; - match listener.poll_accept(cx).map_err(OpError::from) { + match listener.poll_accept(cx).map_err(ErrBox::from) { Poll::Ready(Ok((stream, addr))) => { listener_resource.untrack_task(); Poll::Ready(Ok((stream, addr))) @@ -106,13 +105,13 @@ fn op_accept( _state: &Rc, args: Value, zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: AcceptArgs = serde_json::from_value(args)?; match args.transport.as_str() { "tcp" => accept_tcp(isolate_state, args, zero_copy), #[cfg(unix)] "unix" => net_unix::accept_unix(isolate_state, args.rid as u32, zero_copy), - _ => Err(OpError::other(format!( + _ => Err(ErrBox::error(format!( "Unsupported transport protocol {}", args.transport ))), @@ -130,7 +129,7 @@ fn receive_udp( _state: &Rc, args: ReceiveArgs, zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { assert_eq!(zero_copy.len(), 1, "Invalid number of arguments"); let mut zero_copy = zero_copy[0].clone(); @@ -144,12 +143,12 @@ fn receive_udp( let resource = resource_table .get_mut::(rid) .ok_or_else(|| { - OpError::bad_resource("Socket has been closed".to_string()) + ErrBox::bad_resource("Socket has been closed".to_string()) })?; let socket = &mut resource.socket; socket .poll_recv_from(cx, &mut zero_copy) - .map_err(OpError::from) + .map_err(ErrBox::from) }); let (size, remote_addr) = receive_fut.await?; Ok(json!({ @@ -170,7 +169,7 @@ fn op_datagram_receive( state: &Rc, args: Value, zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { assert_eq!(zero_copy.len(), 1, "Invalid number of arguments"); let args: ReceiveArgs = serde_json::from_value(args)?; @@ -180,7 +179,7 @@ fn op_datagram_receive( "unixpacket" => { net_unix::receive_unix_packet(isolate_state, args.rid as u32, zero_copy) } - _ => Err(OpError::other(format!( + _ => Err(ErrBox::error(format!( "Unsupported transport protocol {}", args.transport ))), @@ -200,7 +199,7 @@ fn op_datagram_send( state: &Rc, args: Value, zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { assert_eq!(zero_copy.len(), 1, "Invalid number of arguments"); let zero_copy = zero_copy[0].clone(); @@ -218,13 +217,13 @@ fn op_datagram_send( let resource = resource_table .get_mut::(rid as u32) .ok_or_else(|| { - OpError::bad_resource("Socket has been closed".to_string()) + ErrBox::bad_resource("Socket has been closed".to_string()) })?; resource .socket .poll_send_to(cx, &zero_copy, &addr) - .map_err(OpError::from) .map_ok(|byte_length| json!(byte_length)) + .map_err(ErrBox::from) }); Ok(JsonOp::Async(f.boxed_local())) } @@ -241,9 +240,8 @@ fn op_datagram_send( let resource = resource_table .get_mut::(rid as u32) .ok_or_else(|| { - OpError::other("Socket has been closed".to_string()) + ErrBox::new("NotConnected", "Socket has been closed") })?; - let socket = &mut resource.socket; let byte_length = socket .send_to(&zero_copy, &resource.local_addr.as_pathname().unwrap()) @@ -254,7 +252,7 @@ fn op_datagram_send( Ok(JsonOp::Async(op.boxed_local())) } - _ => Err(OpError::other("Wrong argument format!".to_owned())), + _ => Err(ErrBox::type_error("Wrong argument format!")), } } @@ -270,7 +268,7 @@ fn op_connect( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let resource_table = isolate_state.resource_table.clone(); match serde_json::from_value(args)? { ConnectArgs { @@ -341,7 +339,7 @@ fn op_connect( }; Ok(JsonOp::Async(op.boxed_local())) } - _ => Err(OpError::other("Wrong argument format!".to_owned())), + _ => Err(ErrBox::type_error("Wrong argument format!")), } } @@ -356,7 +354,7 @@ fn op_shutdown( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { state.check_unstable("Deno.shutdown"); let args: ShutdownArgs = serde_json::from_value(args)?; @@ -373,17 +371,16 @@ fn op_shutdown( let mut resource_table = isolate_state.resource_table.borrow_mut(); let resource_holder = resource_table .get_mut::(rid) - .ok_or_else(OpError::bad_resource_id)?; + .ok_or_else(ErrBox::bad_resource_id)?; match resource_holder.resource { StreamResource::TcpStream(Some(ref mut stream)) => { - TcpStream::shutdown(stream, shutdown_mode).map_err(OpError::from)?; + TcpStream::shutdown(stream, shutdown_mode)?; } #[cfg(unix)] StreamResource::UnixStream(ref mut stream) => { - net_unix::UnixStream::shutdown(stream, shutdown_mode) - .map_err(OpError::from)?; + net_unix::UnixStream::shutdown(stream, shutdown_mode)?; } - _ => return Err(OpError::bad_resource_id()), + _ => return Err(ErrBox::bad_resource_id()), } Ok(JsonOp::Sync(json!({}))) @@ -407,13 +404,13 @@ impl TcpListenerResource { /// can be notified when listener is closed. /// /// Throws an error if another task is already tracked. - pub fn track_task(&mut self, cx: &Context) -> Result<(), OpError> { + pub fn track_task(&mut self, cx: &Context) -> Result<(), ErrBox> { // Currently, we only allow tracking a single accept task for a listener. // This might be changed in the future with multiple workers. // Caveat: TcpListener by itself also only tracks an accept task at a time. // See https://github.com/tokio-rs/tokio/issues/846#issuecomment-454208883 if self.waker.is_some() { - return Err(OpError::other("Another accept task is ongoing".to_string())); + return Err(ErrBox::new("Busy", "Another accept task is ongoing")); } let waker = futures::task::AtomicWaker::new(); @@ -466,7 +463,7 @@ struct ListenArgs { fn listen_tcp( resource_table: &mut ResourceTable, addr: SocketAddr, -) -> Result<(u32, SocketAddr), OpError> { +) -> Result<(u32, SocketAddr), ErrBox> { let std_listener = std::net::TcpListener::bind(&addr)?; let listener = TcpListener::from_std(std_listener)?; let local_addr = listener.local_addr()?; @@ -483,7 +480,7 @@ fn listen_tcp( fn listen_udp( resource_table: &mut ResourceTable, addr: SocketAddr, -) -> Result<(u32, SocketAddr), OpError> { +) -> Result<(u32, SocketAddr), ErrBox> { let std_socket = std::net::UdpSocket::bind(&addr)?; let socket = UdpSocket::from_std(std_socket)?; let local_addr = socket.local_addr()?; @@ -498,7 +495,7 @@ fn op_listen( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let mut resource_table = isolate_state.resource_table.borrow_mut(); match serde_json::from_value(args)? { ListenArgs { @@ -563,6 +560,6 @@ fn op_listen( }))) } #[cfg(unix)] - _ => Err(OpError::other("Wrong argument format!".to_owned())), + _ => Err(ErrBox::type_error("Wrong argument format!")), } } diff --git a/cli/ops/net_unix.rs b/cli/ops/net_unix.rs index 0b2ceb75a1..d8f6c67361 100644 --- a/cli/ops/net_unix.rs +++ b/cli/ops/net_unix.rs @@ -1,7 +1,7 @@ use super::dispatch_json::{Deserialize, JsonOp}; use super::io::{StreamResource, StreamResourceHolder}; -use crate::op_error::OpError; use deno_core::CoreIsolateState; +use deno_core::ErrBox; use deno_core::ResourceTable; use deno_core::ZeroCopyBuf; use futures::future::FutureExt; @@ -30,13 +30,13 @@ pub fn accept_unix( isolate_state: &mut CoreIsolateState, rid: u32, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let resource_table = isolate_state.resource_table.clone(); { let _ = resource_table .borrow() .get::(rid) - .ok_or_else(OpError::bad_resource_id)?; + .ok_or_else(ErrBox::bad_resource_id)?; } let op = async move { let mut resource_table_ = resource_table.borrow_mut(); @@ -44,7 +44,7 @@ pub fn accept_unix( resource_table_ .get_mut::(rid) .ok_or_else(|| { - OpError::bad_resource("Listener has been closed".to_string()) + ErrBox::bad_resource("Listener has been closed".to_string()) })? }; @@ -81,7 +81,7 @@ pub fn receive_unix_packet( isolate_state: &mut CoreIsolateState, rid: u32, zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { assert_eq!(zero_copy.len(), 1, "Invalid number of arguments"); let mut zero_copy = zero_copy[0].clone(); let resource_table = isolate_state.resource_table.clone(); @@ -91,7 +91,7 @@ pub fn receive_unix_packet( let resource = resource_table_ .get_mut::(rid) .ok_or_else(|| { - OpError::bad_resource("Socket has been closed".to_string()) + ErrBox::bad_resource("Socket has been closed".to_string()) })?; let (size, remote_addr) = resource.socket.recv_from(&mut zero_copy).await?; Ok(json!({ @@ -109,7 +109,7 @@ pub fn receive_unix_packet( pub fn listen_unix( resource_table: &mut ResourceTable, addr: &Path, -) -> Result<(u32, unix::net::SocketAddr), OpError> { +) -> Result<(u32, unix::net::SocketAddr), ErrBox> { if addr.exists() { remove_file(&addr).unwrap(); } @@ -124,7 +124,7 @@ pub fn listen_unix( pub fn listen_unix_packet( resource_table: &mut ResourceTable, addr: &Path, -) -> Result<(u32, unix::net::SocketAddr), OpError> { +) -> Result<(u32, unix::net::SocketAddr), ErrBox> { if addr.exists() { remove_file(&addr).unwrap(); } diff --git a/cli/ops/os.rs b/cli/ops/os.rs index e46aff3e61..1b961164e6 100644 --- a/cli/ops/os.rs +++ b/cli/ops/os.rs @@ -1,8 +1,8 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. use super::dispatch_json::{Deserialize, JsonOp, Value}; -use crate::op_error::OpError; use crate::state::State; use deno_core::CoreIsolate; +use deno_core::ErrBox; use deno_core::ZeroCopyBuf; use std::collections::HashMap; use std::env; @@ -25,7 +25,7 @@ fn op_exec_path( state: &Rc, _args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let current_exe = env::current_exe().unwrap(); state.check_read_blind(¤t_exe, "exec_path")?; // Now apply URL parser to current exe to get fully resolved path, otherwise @@ -45,7 +45,7 @@ fn op_set_env( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: SetEnv = serde_json::from_value(args)?; state.check_env()?; env::set_var(args.key, args.value); @@ -56,7 +56,7 @@ fn op_env( state: &Rc, _args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { state.check_env()?; let v = env::vars().collect::>(); Ok(JsonOp::Sync(json!(v))) @@ -71,7 +71,7 @@ fn op_get_env( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: GetEnv = serde_json::from_value(args)?; state.check_env()?; let r = match env::var(args.key) { @@ -90,7 +90,7 @@ fn op_delete_env( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: DeleteEnv = serde_json::from_value(args)?; state.check_env()?; env::remove_var(args.key); @@ -106,7 +106,7 @@ fn op_exit( _s: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: Exit = serde_json::from_value(args)?; std::process::exit(args.code) } @@ -115,7 +115,7 @@ fn op_loadavg( state: &Rc, _args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { state.check_unstable("Deno.loadavg"); state.check_env()?; match sys_info::loadavg() { @@ -132,7 +132,7 @@ fn op_hostname( state: &Rc, _args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { state.check_unstable("Deno.hostname"); state.check_env()?; let hostname = sys_info::hostname().unwrap_or_else(|_| "".to_string()); @@ -143,7 +143,7 @@ fn op_os_release( state: &Rc, _args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { state.check_unstable("Deno.osRelease"); state.check_env()?; let release = sys_info::os_release().unwrap_or_else(|_| "".to_string()); diff --git a/cli/ops/permissions.rs b/cli/ops/permissions.rs index 4cb9985367..6ebabfa5c9 100644 --- a/cli/ops/permissions.rs +++ b/cli/ops/permissions.rs @@ -1,8 +1,8 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. use super::dispatch_json::{Deserialize, JsonOp, Value}; -use crate::op_error::OpError; use crate::state::State; use deno_core::CoreIsolate; +use deno_core::ErrBox; use deno_core::ZeroCopyBuf; use std::path::Path; use std::rc::Rc; @@ -33,7 +33,7 @@ pub fn op_query_permission( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: PermissionArgs = serde_json::from_value(args)?; let permissions = state.permissions.borrow(); let path = args.path.as_deref(); @@ -45,7 +45,12 @@ pub fn op_query_permission( "run" => permissions.query_run(), "plugin" => permissions.query_plugin(), "hrtime" => permissions.query_hrtime(), - n => return Err(OpError::other(format!("No such permission name: {}", n))), + n => { + return Err(ErrBox::new( + "ReferenceError", + format!("No such permission name: {}", n), + )) + } }; Ok(JsonOp::Sync(json!({ "state": perm.to_string() }))) } @@ -54,7 +59,7 @@ pub fn op_revoke_permission( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: PermissionArgs = serde_json::from_value(args)?; let mut permissions = state.permissions.borrow_mut(); let path = args.path.as_deref(); @@ -66,7 +71,12 @@ pub fn op_revoke_permission( "run" => permissions.revoke_run(), "plugin" => permissions.revoke_plugin(), "hrtime" => permissions.revoke_hrtime(), - n => return Err(OpError::other(format!("No such permission name: {}", n))), + n => { + return Err(ErrBox::new( + "ReferenceError", + format!("No such permission name: {}", n), + )) + } }; Ok(JsonOp::Sync(json!({ "state": perm.to_string() }))) } @@ -75,7 +85,7 @@ pub fn op_request_permission( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: PermissionArgs = serde_json::from_value(args)?; let permissions = &mut state.permissions.borrow_mut(); let path = args.path.as_deref(); @@ -87,7 +97,12 @@ pub fn op_request_permission( "run" => permissions.request_run(), "plugin" => permissions.request_plugin(), "hrtime" => permissions.request_hrtime(), - n => return Err(OpError::other(format!("No such permission name: {}", n))), + n => { + return Err(ErrBox::new( + "ReferenceError", + format!("No such permission name: {}", n), + )) + } }; Ok(JsonOp::Sync(json!({ "state": perm.to_string() }))) } diff --git a/cli/ops/plugin.rs b/cli/ops/plugin.rs index 3fb1494043..f06105498d 100644 --- a/cli/ops/plugin.rs +++ b/cli/ops/plugin.rs @@ -1,5 +1,4 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -use crate::op_error::OpError; use crate::ops::dispatch_json::Deserialize; use crate::ops::dispatch_json::JsonOp; use crate::ops::dispatch_json::Value; @@ -8,6 +7,7 @@ use crate::state::State; use deno_core::plugin_api; use deno_core::CoreIsolate; use deno_core::CoreIsolateState; +use deno_core::ErrBox; use deno_core::Op; use deno_core::OpAsyncFuture; use deno_core::OpId; @@ -38,7 +38,7 @@ pub fn op_open_plugin( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { state.check_unstable("Deno.openPlugin"); let args: OpenPluginArgs = serde_json::from_value(args).unwrap(); let filename = PathBuf::from(&args.filename); @@ -46,9 +46,7 @@ pub fn op_open_plugin( state.check_plugin(&filename)?; debug!("Loading Plugin: {:#?}", filename); - let plugin_lib = Library::open(filename) - .map(Rc::new) - .map_err(OpError::from)?; + let plugin_lib = Library::open(filename).map(Rc::new)?; let plugin_resource = PluginResource::new(&plugin_lib); let mut resource_table = isolate_state.resource_table.borrow_mut(); diff --git a/cli/ops/process.rs b/cli/ops/process.rs index bf733a78e9..1754812a23 100644 --- a/cli/ops/process.rs +++ b/cli/ops/process.rs @@ -1,17 +1,15 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. use super::dispatch_json::{Deserialize, JsonOp, Value}; use super::io::{std_file_resource, StreamResource, StreamResourceHolder}; -use crate::op_error::OpError; use crate::signal::kill; use crate::state::State; use deno_core::CoreIsolate; use deno_core::CoreIsolateState; +use deno_core::ErrBox; use deno_core::ResourceTable; use deno_core::ZeroCopyBuf; use futures::future::poll_fn; use futures::future::FutureExt; -use futures::TryFutureExt; -use std::convert::From; use std::rc::Rc; use tokio::process::Command; @@ -27,19 +25,19 @@ pub fn init(i: &mut CoreIsolate, s: &Rc) { fn clone_file( rid: u32, resource_table: &mut ResourceTable, -) -> Result { +) -> Result { std_file_resource(resource_table, rid, move |r| match r { - Ok(std_file) => std_file.try_clone().map_err(OpError::from), - Err(_) => Err(OpError::bad_resource_id()), + Ok(std_file) => std_file.try_clone().map_err(ErrBox::from), + Err(_) => Err(ErrBox::bad_resource_id()), }) } -fn subprocess_stdio_map(s: &str) -> Result { +fn subprocess_stdio_map(s: &str) -> Result { match s { "inherit" => Ok(std::process::Stdio::inherit()), "piped" => Ok(std::process::Stdio::piped()), "null" => Ok(std::process::Stdio::null()), - _ => Err(OpError::other("Invalid resource for stdio".to_string())), + _ => Err(ErrBox::type_error("Invalid resource for stdio")), } } @@ -66,7 +64,7 @@ fn op_run( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let run_args: RunArgs = serde_json::from_value(args)?; state.check_run()?; @@ -177,7 +175,7 @@ fn op_run_status( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: RunStatusArgs = serde_json::from_value(args)?; let rid = args.rid as u32; @@ -189,9 +187,9 @@ fn op_run_status( let mut resource_table = resource_table.borrow_mut(); let child_resource = resource_table .get_mut::(rid) - .ok_or_else(OpError::bad_resource_id)?; + .ok_or_else(ErrBox::bad_resource_id)?; let child = &mut child_resource.child; - child.map_err(OpError::from).poll_unpin(cx) + child.poll_unpin(cx).map_err(ErrBox::from) }) .await?; @@ -227,7 +225,7 @@ fn op_kill( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { state.check_unstable("Deno.kill"); state.check_run()?; diff --git a/cli/ops/random.rs b/cli/ops/random.rs index 275f264f9f..3ff5ad49f7 100644 --- a/cli/ops/random.rs +++ b/cli/ops/random.rs @@ -1,8 +1,8 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. use super::dispatch_json::{JsonOp, Value}; -use crate::op_error::OpError; use crate::state::State; use deno_core::CoreIsolate; +use deno_core::ErrBox; use deno_core::ZeroCopyBuf; use rand::thread_rng; use rand::Rng; @@ -19,7 +19,7 @@ fn op_get_random_values( state: &Rc, _args: Value, zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { assert_eq!(zero_copy.len(), 1); if let Some(seeded_rng) = &state.seeded_rng { diff --git a/cli/ops/repl.rs b/cli/ops/repl.rs index 88b7de8819..b051dd5748 100644 --- a/cli/ops/repl.rs +++ b/cli/ops/repl.rs @@ -1,11 +1,11 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. use super::dispatch_json::{blocking_json, Deserialize, JsonOp, Value}; -use crate::op_error::OpError; use crate::repl; use crate::repl::Repl; use crate::state::State; use deno_core::CoreIsolate; use deno_core::CoreIsolateState; +use deno_core::ErrBox; use deno_core::ZeroCopyBuf; use std::rc::Rc; use std::sync::Arc; @@ -29,7 +29,7 @@ fn op_repl_start( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: ReplStartArgs = serde_json::from_value(args)?; debug!("op_repl_start {}", args.history_file); let history_path = @@ -52,7 +52,7 @@ fn op_repl_readline( _state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: ReplReadlineArgs = serde_json::from_value(args)?; let rid = args.rid as u32; let prompt = args.prompt; @@ -60,7 +60,7 @@ fn op_repl_readline( let resource_table = isolate_state.resource_table.borrow(); let resource = resource_table .get::(rid) - .ok_or_else(OpError::bad_resource_id)?; + .ok_or_else(ErrBox::bad_resource_id)?; let repl = resource.0.clone(); blocking_json(false, move || { diff --git a/cli/ops/resources.rs b/cli/ops/resources.rs index a24b3b3823..fb3c1bc791 100644 --- a/cli/ops/resources.rs +++ b/cli/ops/resources.rs @@ -1,9 +1,9 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. use super::dispatch_json::{Deserialize, JsonOp, Value}; -use crate::op_error::OpError; use crate::state::State; use deno_core::CoreIsolate; use deno_core::CoreIsolateState; +use deno_core::ErrBox; use deno_core::ZeroCopyBuf; use std::rc::Rc; @@ -17,7 +17,7 @@ fn op_resources( _state: &Rc, _args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let serialized_resources = isolate_state.resource_table.borrow().entries(); Ok(JsonOp::Sync(json!(serialized_resources))) } @@ -28,7 +28,7 @@ fn op_close( _state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { #[derive(Deserialize)] struct CloseArgs { rid: i32, @@ -37,6 +37,6 @@ fn op_close( let mut resource_table = isolate_state.resource_table.borrow_mut(); resource_table .close(args.rid as u32) - .ok_or_else(OpError::bad_resource_id)?; + .ok_or_else(ErrBox::bad_resource_id)?; Ok(JsonOp::Sync(json!({}))) } diff --git a/cli/ops/runtime.rs b/cli/ops/runtime.rs index b4fb3c496d..26e1b2a338 100644 --- a/cli/ops/runtime.rs +++ b/cli/ops/runtime.rs @@ -1,11 +1,11 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. use super::dispatch_json::{JsonOp, Value}; use crate::colors; -use crate::op_error::OpError; use crate::state::State; use crate::version; use crate::DenoSubcommand; use deno_core::CoreIsolate; +use deno_core::ErrBox; use deno_core::ModuleSpecifier; use deno_core::ZeroCopyBuf; use std::env; @@ -21,7 +21,7 @@ fn op_start( state: &Rc, _args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let gs = &state.global_state; Ok(JsonOp::Sync(json!({ @@ -46,7 +46,7 @@ fn op_main_module( state: &Rc, _args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let main = &state.main_module.to_string(); let main_url = ModuleSpecifier::resolve_url_or_path(&main)?; if main_url.as_url().scheme() == "file" { @@ -60,7 +60,7 @@ fn op_metrics( state: &Rc, _args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let m = &state.metrics.borrow(); Ok(JsonOp::Sync(json!({ diff --git a/cli/ops/runtime_compiler.rs b/cli/ops/runtime_compiler.rs index d77ab8f08e..3aaaf019db 100644 --- a/cli/ops/runtime_compiler.rs +++ b/cli/ops/runtime_compiler.rs @@ -1,12 +1,12 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. use super::dispatch_json::{Deserialize, JsonOp, Value}; use crate::futures::FutureExt; -use crate::op_error::OpError; use crate::state::State; use crate::tsc::runtime_bundle; use crate::tsc::runtime_compile; use crate::tsc::runtime_transpile; use deno_core::CoreIsolate; +use deno_core::ErrBox; use deno_core::ZeroCopyBuf; use std::collections::HashMap; use std::rc::Rc; @@ -29,7 +29,7 @@ fn op_compile( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { state.check_unstable("Deno.compile"); let args: CompileArgs = serde_json::from_value(args)?; let global_state = state.global_state.clone(); @@ -71,7 +71,7 @@ fn op_transpile( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { state.check_unstable("Deno.transpile"); let args: TranspileArgs = serde_json::from_value(args)?; let global_state = state.global_state.clone(); diff --git a/cli/ops/signal.rs b/cli/ops/signal.rs index cb83427c89..3868c6e174 100644 --- a/cli/ops/signal.rs +++ b/cli/ops/signal.rs @@ -1,15 +1,16 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. use super::dispatch_json::{JsonOp, Value}; -use crate::op_error::OpError; use crate::state::State; use deno_core::CoreIsolate; use deno_core::CoreIsolateState; +use deno_core::ErrBox; use deno_core::ZeroCopyBuf; use std::rc::Rc; #[cfg(unix)] use super::dispatch_json::Deserialize; #[cfg(unix)] +#[cfg(unix)] use futures::future::{poll_fn, FutureExt}; #[cfg(unix)] use std::task::Waker; @@ -45,7 +46,7 @@ fn op_signal_bind( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { state.check_unstable("Deno.signal"); let args: BindSignalArgs = serde_json::from_value(args)?; let mut resource_table = isolate_state.resource_table.borrow_mut(); @@ -67,7 +68,7 @@ fn op_signal_poll( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { state.check_unstable("Deno.signal"); let args: SignalArgs = serde_json::from_value(args)?; let rid = args.rid as u32; @@ -94,7 +95,7 @@ pub fn op_signal_unbind( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { state.check_unstable("Deno.signal"); let args: SignalArgs = serde_json::from_value(args)?; let rid = args.rid as u32; @@ -109,7 +110,7 @@ pub fn op_signal_unbind( } resource_table .close(rid) - .ok_or_else(OpError::bad_resource_id)?; + .ok_or_else(ErrBox::bad_resource_id)?; Ok(JsonOp::Sync(json!({}))) } @@ -119,7 +120,7 @@ pub fn op_signal_bind( _state: &Rc, _args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { unimplemented!(); } @@ -129,7 +130,7 @@ fn op_signal_unbind( _state: &Rc, _args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { unimplemented!(); } @@ -139,6 +140,6 @@ fn op_signal_poll( _state: &Rc, _args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { unimplemented!(); } diff --git a/cli/ops/timers.rs b/cli/ops/timers.rs index e3fe14c2c7..379bacf98f 100644 --- a/cli/ops/timers.rs +++ b/cli/ops/timers.rs @@ -1,8 +1,8 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. use super::dispatch_json::{Deserialize, JsonOp, Value}; -use crate::op_error::OpError; use crate::state::State; use deno_core::CoreIsolate; +use deno_core::ErrBox; use deno_core::ZeroCopyBuf; use futures::future::FutureExt; use std::rc::Rc; @@ -22,7 +22,7 @@ fn op_global_timer_stop( state: &Rc, _args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { state.global_timer.borrow_mut().cancel(); Ok(JsonOp::Sync(json!({}))) } @@ -36,7 +36,7 @@ fn op_global_timer( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: GlobalTimerArgs = serde_json::from_value(args)?; let val = args.timeout; @@ -58,7 +58,7 @@ fn op_now( state: &Rc, _args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let seconds = state.start_time.elapsed().as_secs(); let mut subsec_nanos = state.start_time.elapsed().subsec_nanos(); let reduced_time_precision = 2_000_000; // 2ms in nanoseconds @@ -66,12 +66,8 @@ fn op_now( // If the permission is not enabled // Round the nano result on 2 milliseconds // see: https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp#Reduced_time_precision - if let Err(op_error) = state.check_hrtime() { - if op_error.kind_str == "PermissionDenied" { - subsec_nanos -= subsec_nanos % reduced_time_precision; - } else { - return Err(op_error); - } + if state.check_hrtime().is_err() { + subsec_nanos -= subsec_nanos % reduced_time_precision; } Ok(JsonOp::Sync(json!({ diff --git a/cli/ops/tls.rs b/cli/ops/tls.rs index 76962f5c8f..97dca9d90b 100644 --- a/cli/ops/tls.rs +++ b/cli/ops/tls.rs @@ -1,11 +1,11 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. use super::dispatch_json::{Deserialize, JsonOp, Value}; use super::io::{StreamResource, StreamResourceHolder}; -use crate::op_error::OpError; use crate::resolve_addr::resolve_addr; use crate::state::State; use deno_core::CoreIsolate; use deno_core::CoreIsolateState; +use deno_core::ErrBox; use deno_core::ZeroCopyBuf; use futures::future::poll_fn; use futures::future::FutureExt; @@ -59,7 +59,7 @@ pub fn op_start_tls( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { state.check_unstable("Deno.startTls"); let args: StartTLSArgs = serde_json::from_value(args)?; let rid = args.rid as u32; @@ -81,7 +81,7 @@ pub fn op_start_tls( let mut resource_table_ = resource_table.borrow_mut(); match resource_table_.remove::(rid) { Some(resource) => *resource, - None => return Err(OpError::bad_resource_id()), + None => return Err(ErrBox::bad_resource_id()), } }; @@ -127,7 +127,7 @@ pub fn op_start_tls( } })) } else { - Err(OpError::bad_resource_id()) + Err(ErrBox::bad_resource_id()) } }; Ok(JsonOp::Async(op.boxed_local())) @@ -138,7 +138,7 @@ pub fn op_connect_tls( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: ConnectTLSArgs = serde_json::from_value(args)?; let cert_file = args.cert_file.clone(); let resource_table = isolate_state.resource_table.clone(); @@ -195,31 +195,31 @@ pub fn op_connect_tls( Ok(JsonOp::Async(op.boxed_local())) } -fn load_certs(path: &str) -> Result, OpError> { +fn load_certs(path: &str) -> Result, ErrBox> { let cert_file = File::open(path)?; let reader = &mut BufReader::new(cert_file); let certs = certs(reader) - .map_err(|_| OpError::other("Unable to decode certificate".to_string()))?; + .map_err(|_| ErrBox::new("InvalidData", "Unable to decode certificate"))?; if certs.is_empty() { - let e = OpError::other("No certificates found in cert file".to_string()); + let e = ErrBox::new("InvalidData", "No certificates found in cert file"); return Err(e); } Ok(certs) } -fn key_decode_err() -> OpError { - OpError::other("Unable to decode key".to_string()) +fn key_decode_err() -> ErrBox { + ErrBox::new("InvalidData", "Unable to decode key") } -fn key_not_found_err() -> OpError { - OpError::other("No keys found in key file".to_string()) +fn key_not_found_err() -> ErrBox { + ErrBox::new("InvalidData", "No keys found in key file") } /// Starts with -----BEGIN RSA PRIVATE KEY----- -fn load_rsa_keys(path: &str) -> Result, OpError> { +fn load_rsa_keys(path: &str) -> Result, ErrBox> { let key_file = File::open(path)?; let reader = &mut BufReader::new(key_file); let keys = rsa_private_keys(reader).map_err(|_| key_decode_err())?; @@ -227,14 +227,14 @@ fn load_rsa_keys(path: &str) -> Result, OpError> { } /// Starts with -----BEGIN PRIVATE KEY----- -fn load_pkcs8_keys(path: &str) -> Result, OpError> { +fn load_pkcs8_keys(path: &str) -> Result, ErrBox> { let key_file = File::open(path)?; let reader = &mut BufReader::new(key_file); let keys = pkcs8_private_keys(reader).map_err(|_| key_decode_err())?; Ok(keys) } -fn load_keys(path: &str) -> Result, OpError> { +fn load_keys(path: &str) -> Result, ErrBox> { let path = path.to_string(); let mut keys = load_rsa_keys(&path)?; @@ -268,13 +268,13 @@ impl TlsListenerResource { /// can be notified when listener is closed. /// /// Throws an error if another task is already tracked. - pub fn track_task(&mut self, cx: &Context) -> Result<(), OpError> { + pub fn track_task(&mut self, cx: &Context) -> Result<(), ErrBox> { // Currently, we only allow tracking a single accept task for a listener. // This might be changed in the future with multiple workers. // Caveat: TcpListener by itself also only tracks an accept task at a time. // See https://github.com/tokio-rs/tokio/issues/846#issuecomment-454208883 if self.waker.is_some() { - return Err(OpError::other("Another accept task is ongoing".to_string())); + return Err(ErrBox::new("Busy", "Another accept task is ongoing")); } let waker = futures::task::AtomicWaker::new(); @@ -312,7 +312,7 @@ fn op_listen_tls( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: ListenTlsArgs = serde_json::from_value(args)?; assert_eq!(args.transport, "tcp"); @@ -362,7 +362,7 @@ fn op_accept_tls( _state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: AcceptTlsArgs = serde_json::from_value(args)?; let rid = args.rid as u32; let resource_table = isolate_state.resource_table.clone(); @@ -372,10 +372,10 @@ fn op_accept_tls( let listener_resource = resource_table .get_mut::(rid) .ok_or_else(|| { - OpError::bad_resource("Listener has been closed".to_string()) + ErrBox::bad_resource("Listener has been closed".to_string()) })?; let listener = &mut listener_resource.listener; - match listener.poll_accept(cx).map_err(OpError::from) { + match listener.poll_accept(cx).map_err(ErrBox::from) { Poll::Ready(Ok((stream, addr))) => { listener_resource.untrack_task(); Poll::Ready(Ok((stream, addr))) @@ -397,7 +397,7 @@ fn op_accept_tls( let resource_table = resource_table.borrow(); let resource = resource_table .get::(rid) - .ok_or_else(OpError::bad_resource_id) + .ok_or_else(ErrBox::bad_resource_id) .expect("Can't find tls listener"); resource.tls_acceptor.clone() }; diff --git a/cli/ops/tty.rs b/cli/ops/tty.rs index d6aaeb7854..b62b727c38 100644 --- a/cli/ops/tty.rs +++ b/cli/ops/tty.rs @@ -1,10 +1,10 @@ use super::dispatch_json::JsonOp; use super::io::std_file_resource; use super::io::{StreamResource, StreamResourceHolder}; -use crate::op_error::OpError; use crate::state::State; use deno_core::CoreIsolate; use deno_core::CoreIsolateState; +use deno_core::ErrBox; use deno_core::ZeroCopyBuf; #[cfg(unix)] use nix::sys::termios; @@ -23,15 +23,15 @@ const RAW_MODE_MASK: DWORD = wincon::ENABLE_LINE_INPUT #[cfg(windows)] fn get_windows_handle( f: &std::fs::File, -) -> Result { +) -> Result { use std::os::windows::io::AsRawHandle; use winapi::um::handleapi; let handle = f.as_raw_handle(); if handle == handleapi::INVALID_HANDLE_VALUE { - return Err(OpError::from(std::io::Error::last_os_error())); + return Err(ErrBox::last_os_error()); } else if handle.is_null() { - return Err(OpError::other("null handle".to_owned())); + return Err(ErrBox::new("ReferenceError", "null handle")); } Ok(handle) } @@ -53,7 +53,7 @@ pub fn op_set_raw( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { state.check_unstable("Deno.setRaw"); let args: SetRawArgs = serde_json::from_value(args)?; let rid = args.rid; @@ -73,7 +73,7 @@ pub fn op_set_raw( let mut resource_table = isolate_state.resource_table.borrow_mut(); let resource_holder = resource_table.get_mut::(rid); if resource_holder.is_none() { - return Err(OpError::bad_resource_id()); + return Err(ErrBox::bad_resource_id()); } let resource_holder = resource_holder.unwrap(); @@ -98,28 +98,28 @@ pub fn op_set_raw( // some operation is in-flight. resource_holder.resource = StreamResource::FsFile(Some((tokio_file, metadata))); - return Err(OpError::resource_unavailable()); + return Err(ErrBox::resource_unavailable()); } } } else { - return Err(OpError::resource_unavailable()); + return Err(ErrBox::resource_unavailable()); } } _ => { - return Err(OpError::bad_resource_id()); + return Err(ErrBox::bad_resource_id()); } }; if handle == handleapi::INVALID_HANDLE_VALUE { - return Err(OpError::from(std::io::Error::last_os_error())); + return Err(ErrBox::last_os_error()); } else if handle.is_null() { - return Err(OpError::other("null handle".to_owned())); + return Err(ErrBox::new("ReferenceError", "null handle")); } let mut original_mode: DWORD = 0; if unsafe { consoleapi::GetConsoleMode(handle, &mut original_mode) } == FALSE { - return Err(OpError::from(std::io::Error::last_os_error())); + return Err(ErrBox::last_os_error()); } let new_mode = if is_raw { original_mode & !RAW_MODE_MASK @@ -127,7 +127,7 @@ pub fn op_set_raw( original_mode | RAW_MODE_MASK }; if unsafe { consoleapi::SetConsoleMode(handle, new_mode) } == FALSE { - return Err(OpError::from(std::io::Error::last_os_error())); + return Err(ErrBox::last_os_error()); } Ok(JsonOp::Sync(json!({}))) @@ -139,7 +139,7 @@ pub fn op_set_raw( let mut resource_table = isolate_state.resource_table.borrow_mut(); let resource_holder = resource_table.get_mut::(rid); if resource_holder.is_none() { - return Err(OpError::bad_resource_id()); + return Err(ErrBox::bad_resource_id()); } if is_raw { @@ -152,10 +152,10 @@ pub fn op_set_raw( (f.as_raw_fd(), &mut metadata.tty.mode) } StreamResource::FsFile(None) => { - return Err(OpError::resource_unavailable()) + return Err(ErrBox::resource_unavailable()) } _ => { - return Err(OpError::other("Not supported".to_owned())); + return Err(ErrBox::not_supported()); } }; @@ -196,10 +196,10 @@ pub fn op_set_raw( (f.as_raw_fd(), &mut metadata.tty.mode) } StreamResource::FsFile(None) => { - return Err(OpError::resource_unavailable()); + return Err(ErrBox::resource_unavailable()); } _ => { - return Err(OpError::bad_resource_id()); + return Err(ErrBox::bad_resource_id()); } }; @@ -222,7 +222,7 @@ pub fn op_isatty( _state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: IsattyArgs = serde_json::from_value(args)?; let rid = args.rid; @@ -269,7 +269,7 @@ pub fn op_console_size( state: &Rc, args: Value, _zero_copy: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { state.check_unstable("Deno.consoleSize"); let args: ConsoleSizeArgs = serde_json::from_value(args)?; let rid = args.rid; @@ -292,10 +292,7 @@ pub fn op_console_size( &mut bufinfo, ) == 0 { - // TODO (caspervonb) use GetLastError - return Err(OpError::other( - winapi::um::errhandlingapi::GetLastError().to_string(), - )); + return Err(ErrBox::last_os_error()); } Ok(ConsoleSize { @@ -313,7 +310,7 @@ pub fn op_console_size( unsafe { let mut size: libc::winsize = std::mem::zeroed(); if libc::ioctl(fd, libc::TIOCGWINSZ, &mut size as *mut _) != 0 { - return Err(OpError::from(std::io::Error::last_os_error())); + return Err(ErrBox::last_os_error()); } // TODO (caspervonb) return a tuple instead @@ -324,7 +321,7 @@ pub fn op_console_size( } } } - Err(_) => Err(OpError::bad_resource_id()), + Err(_) => Err(ErrBox::bad_resource_id()), })?; Ok(JsonOp::Sync(json!(size))) diff --git a/cli/ops/web_worker.rs b/cli/ops/web_worker.rs index 8ad497d5cd..023a4708c8 100644 --- a/cli/ops/web_worker.rs +++ b/cli/ops/web_worker.rs @@ -1,12 +1,12 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. use super::dispatch_json::{JsonOp, Value}; -use crate::op_error::OpError; use crate::ops::json_op; use crate::state::State; use crate::web_worker::WebWorkerHandle; use crate::worker::WorkerEvent; use deno_core::CoreIsolate; use deno_core::CoreIsolateState; +use deno_core::ErrBox; use deno_core::ZeroCopyBuf; use futures::channel::mpsc; use std::convert::From; @@ -19,18 +19,18 @@ pub fn web_worker_op( &mut CoreIsolateState, Value, &mut [ZeroCopyBuf], -) -> Result +) -> Result where D: Fn( &mpsc::Sender, Value, &mut [ZeroCopyBuf], - ) -> Result, + ) -> Result, { move |_isolate_state: &mut CoreIsolateState, args: Value, zero_copy: &mut [ZeroCopyBuf]| - -> Result { dispatcher(&sender, args, zero_copy) } + -> Result { dispatcher(&sender, args, zero_copy) } } pub fn web_worker_op2( @@ -41,19 +41,19 @@ pub fn web_worker_op2( &mut CoreIsolateState, Value, &mut [ZeroCopyBuf], -) -> Result +) -> Result where D: Fn( WebWorkerHandle, &mpsc::Sender, Value, &mut [ZeroCopyBuf], - ) -> Result, + ) -> Result, { move |_isolate_state: &mut CoreIsolateState, args: Value, zero_copy: &mut [ZeroCopyBuf]| - -> Result { + -> Result { dispatcher(handle.clone(), &sender, args, zero_copy) } } @@ -86,7 +86,7 @@ fn op_worker_post_message( sender: &mpsc::Sender, _args: Value, data: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { assert_eq!(data.len(), 1, "Invalid number of arguments"); let d = Vec::from(&*data[0]).into_boxed_slice(); let mut sender = sender.clone(); @@ -102,7 +102,7 @@ fn op_worker_close( sender: &mpsc::Sender, _args: Value, _data: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let mut sender = sender.clone(); // Notify parent that we're finished sender.close_channel(); diff --git a/cli/ops/worker_host.rs b/cli/ops/worker_host.rs index 47ebd9c7f7..4cd55fef1a 100644 --- a/cli/ops/worker_host.rs +++ b/cli/ops/worker_host.rs @@ -2,7 +2,6 @@ use super::dispatch_json::{Deserialize, JsonOp, Value}; use crate::fmt_errors::JSError; use crate::global_state::GlobalState; -use crate::op_error::OpError; use crate::ops::io::get_stdio; use crate::permissions::Permissions; use crate::startup_data; @@ -184,7 +183,7 @@ fn op_create_worker( state: &Rc, args: Value, _data: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: CreateWorkerArgs = serde_json::from_value(args)?; let specifier = args.specifier.clone(); @@ -215,8 +214,7 @@ fn op_create_worker( module_specifier, use_deno_namespace, maybe_source_code, - ) - .map_err(|e| OpError::other(e.to_string()))?; + )?; // At this point all interactions with worker happen using thread // safe handler returned from previous function call parent_state @@ -236,7 +234,7 @@ fn op_host_terminate_worker( state: &Rc, args: Value, _data: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: WorkerArgs = serde_json::from_value(args)?; let id = args.id as u32; let (join_handle, worker_handle) = state @@ -304,7 +302,7 @@ fn op_host_get_message( state: &Rc, args: Value, _data: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { let args: WorkerArgs = serde_json::from_value(args)?; let id = args.id as u32; let state = state.clone(); @@ -345,7 +343,7 @@ fn op_host_post_message( state: &Rc, args: Value, data: &mut [ZeroCopyBuf], -) -> Result { +) -> Result { assert_eq!(data.len(), 1, "Invalid number of arguments"); let args: WorkerArgs = serde_json::from_value(args)?; let id = args.id as u32; @@ -354,8 +352,6 @@ fn op_host_post_message( debug!("post message to worker {}", id); let workers = state.workers.borrow(); let worker_handle = workers[&id].1.clone(); - worker_handle - .post_message(msg) - .map_err(|e| OpError::other(e.to_string()))?; + worker_handle.post_message(msg)?; Ok(JsonOp::Sync(json!({}))) } diff --git a/cli/permissions.rs b/cli/permissions.rs index 7d9fa1d45b..577ebd40ee 100644 --- a/cli/permissions.rs +++ b/cli/permissions.rs @@ -2,7 +2,7 @@ use crate::colors; use crate::flags::Flags; use crate::fs::resolve_from_cwd; -use crate::op_error::OpError; +use deno_core::ErrBox; use serde::Deserialize; use std::collections::HashSet; use std::env::current_dir; @@ -31,21 +31,21 @@ pub enum PermissionState { impl PermissionState { /// Check the permission state. - fn check(self, msg: &str, flag_name: &str) -> Result<(), OpError> { + fn check(self, msg: &str, flag_name: &str) -> Result<(), ErrBox> { if self == PermissionState::Granted { log_perm_access(msg); return Ok(()); } - let m = format!("{}, run again with the {} flag", msg, flag_name); - Err(OpError::permission_denied(m)) + let message = format!("{}, run again with the {} flag", msg, flag_name); + Err(ErrBox::new("PermissionDenied", message)) } /// Check that the permissions represented by `other` don't escalate ours. - fn check_fork(self, other: &Self) -> Result<(), OpError> { + fn check_fork(self, other: &Self) -> Result<(), ErrBox> { if self == PermissionState::Denied && other != &PermissionState::Denied || self == PermissionState::Prompt && other == &PermissionState::Granted { - return Err(OpError::permission_escalation_error()); + return Err(permission_escalation_error()); } Ok(()) } @@ -97,13 +97,13 @@ pub struct UnaryPermission { impl UnaryPermission { /// Check that the permissions represented by `other` don't escalate ours. - fn check_fork(&self, other: &Self) -> Result<(), OpError> { + fn check_fork(&self, other: &Self) -> Result<(), ErrBox> { self.global_state.check_fork(&other.global_state)?; if !self.granted_list.is_superset(&other.granted_list) { - return Err(OpError::permission_escalation_error()); + return Err(permission_escalation_error()); } if !self.denied_list.is_subset(&other.denied_list) { - return Err(OpError::permission_escalation_error()); + return Err(permission_escalation_error()); } Ok(()) } @@ -250,19 +250,19 @@ impl Permissions { pub fn query_net_url( &self, url: &Option<&str>, - ) -> Result { + ) -> Result { if url.is_none() { return Ok(self.net.global_state); } let url: &str = url.unwrap(); // If url is invalid, then throw a TypeError. - let parsed = Url::parse(url).map_err(OpError::from)?; + let parsed = Url::parse(url)?; // The url may be parsed correctly but still lack a host, i.e. "localhost:235" or "mailto:someone@somewhere.com" or "file:/1.txt" // Note that host:port combos are parsed as scheme:path if parsed.host().is_none() { - return Err(OpError::uri_error( - "invalid url, expected format: ://[:port][/subpath]" - .to_owned(), + return Err(ErrBox::new( + "URIError", + "invalid urlormat: ://[:port][/subpath]", )); } Ok(self.query_net( @@ -374,7 +374,7 @@ impl Permissions { pub fn request_net( &mut self, url: &Option<&str>, - ) -> Result { + ) -> Result { if let Some(url) = url { let state = self.query_net_url(&Some(url))?; if state == PermissionState::Prompt { @@ -486,7 +486,7 @@ impl Permissions { pub fn revoke_net( &mut self, url: &Option<&str>, - ) -> Result { + ) -> Result { if let Some(url) = url { self.net.granted_list.remove(*url); } else { @@ -526,7 +526,7 @@ impl Permissions { self.hrtime } - pub fn check_read(&self, path: &Path) -> Result<(), OpError> { + pub fn check_read(&self, path: &Path) -> Result<(), ErrBox> { let (resolved_path, display_path) = self.resolved_and_display_path(path); self.query_read(&Some(&resolved_path)).check( &format!("read access to \"{}\"", display_path.display()), @@ -540,14 +540,14 @@ impl Permissions { &self, path: &Path, display: &str, - ) -> Result<(), OpError> { + ) -> Result<(), ErrBox> { let resolved_path = resolve_from_cwd(path).unwrap(); self .query_read(&Some(&resolved_path)) .check(&format!("read access to <{}>", display), "--allow-read") } - pub fn check_write(&self, path: &Path) -> Result<(), OpError> { + pub fn check_write(&self, path: &Path) -> Result<(), ErrBox> { let (resolved_path, display_path) = self.resolved_and_display_path(path); self.query_write(&Some(&resolved_path)).check( &format!("write access to \"{}\"", display_path.display()), @@ -555,33 +555,33 @@ impl Permissions { ) } - pub fn check_net(&self, hostname: &str, port: u16) -> Result<(), OpError> { + pub fn check_net(&self, hostname: &str, port: u16) -> Result<(), ErrBox> { self.query_net(hostname, Some(port)).check( &format!("network access to \"{}:{}\"", hostname, port), "--allow-net", ) } - pub fn check_net_url(&self, url: &url::Url) -> Result<(), OpError> { + pub fn check_net_url(&self, url: &url::Url) -> Result<(), ErrBox> { let host = url .host_str() - .ok_or_else(|| OpError::uri_error("missing host".to_owned()))?; + .ok_or_else(|| ErrBox::new("URIError", "missing host"))?; self .query_net(host, url.port_or_known_default()) .check(&format!("network access to \"{}\"", url), "--allow-net") } - pub fn check_env(&self) -> Result<(), OpError> { + pub fn check_env(&self) -> Result<(), ErrBox> { self .env .check("access to environment variables", "--allow-env") } - pub fn check_run(&self) -> Result<(), OpError> { + pub fn check_run(&self) -> Result<(), ErrBox> { self.run.check("access to run a subprocess", "--allow-run") } - pub fn check_plugin(&self, path: &Path) -> Result<(), OpError> { + pub fn check_plugin(&self, path: &Path) -> Result<(), ErrBox> { let (_, display_path) = self.resolved_and_display_path(path); self.plugin.check( &format!("access to open a plugin: {}", display_path.display()), @@ -589,7 +589,7 @@ impl Permissions { ) } - pub fn check_hrtime(&self) -> Result<(), OpError> { + pub fn check_hrtime(&self) -> Result<(), ErrBox> { self .hrtime .check("access to high precision time", "--allow-run") @@ -605,7 +605,7 @@ impl Permissions { run: PermissionState, plugin: PermissionState, hrtime: PermissionState, - ) -> Result { + ) -> Result { self.read.check_fork(&read)?; self.write.check_fork(&write)?; self.net.check_fork(&net)?; @@ -715,6 +715,10 @@ fn check_host_and_port_list( && allowlist.contains(&format!("{}:{}", host, port.unwrap()))) } +fn permission_escalation_error() -> ErrBox { + ErrBox::new("PermissionDenied", "Arguments escalate parent permissions") +} + #[cfg(test)] mod tests { use super::*; diff --git a/cli/repl.rs b/cli/repl.rs index 614921d4d3..28b99cf079 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -1,6 +1,5 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. use crate::deno_dir::DenoDir; -use crate::op_error::OpError; use deno_core::ErrBox; use rustyline::Editor; use std::fs; @@ -42,11 +41,11 @@ impl Repl { .map(|_| debug!("Saved REPL history to: {:?}", self.history_file)) .map_err(|e| { eprintln!("Unable to save REPL history: {:?} {}", self.history_file, e); - ErrBox::from(e) + e.into() }) } - pub fn readline(&mut self, prompt: &str) -> Result { + pub fn readline(&mut self, prompt: &str) -> Result { self .editor .readline(&prompt) @@ -54,7 +53,8 @@ impl Repl { self.editor.add_history_entry(line.clone()); line }) - .map_err(OpError::from) + .map_err(ErrBox::from) + // Forward error to TS side for processing } } diff --git a/cli/resolve_addr.rs b/cli/resolve_addr.rs index 3081ef4315..a25bf74546 100644 --- a/cli/resolve_addr.rs +++ b/cli/resolve_addr.rs @@ -1,10 +1,10 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -use crate::op_error::OpError; +use deno_core::ErrBox; use std::net::SocketAddr; use std::net::ToSocketAddrs; /// Resolve network address. Returns a future. -pub fn resolve_addr(hostname: &str, port: u16) -> Result { +pub fn resolve_addr(hostname: &str, port: u16) -> Result { // Default to localhost if given just the port. Example: ":80" let addr: &str = if !hostname.is_empty() { &hostname @@ -21,7 +21,7 @@ pub fn resolve_addr(hostname: &str, port: u16) -> Result { addr }; let addr_port_pair = (addr, port); - let mut iter = addr_port_pair.to_socket_addrs().map_err(OpError::from)?; + let mut iter = addr_port_pair.to_socket_addrs()?; Ok(iter.next().unwrap()) } diff --git a/cli/rt/01_errors.js b/cli/rt/01_errors.js index fb2bb78c27..d5933069b4 100644 --- a/cli/rt/01_errors.js +++ b/cli/rt/01_errors.js @@ -127,6 +127,13 @@ } } + class NotSupported extends Error { + constructor(msg) { + super(msg); + this.name = "NotSupported"; + } + } + const errors = { NotFound, PermissionDenied, @@ -146,6 +153,7 @@ BadResource, Http, Busy, + NotSupported, }; window.__bootstrap.errors = { diff --git a/cli/rt/10_dispatch_minimal.js b/cli/rt/10_dispatch_minimal.js index 0a0a4f99f2..0722852565 100644 --- a/cli/rt/10_dispatch_minimal.js +++ b/cli/rt/10_dispatch_minimal.js @@ -31,11 +31,11 @@ let err; if (arg < 0) { - const codeLen = result; - const codeAndMessage = decoder.decode(ui8.subarray(12)); - const errorCode = codeAndMessage.slice(0, codeLen); - const message = codeAndMessage.slice(codeLen); - err = { kind: errorCode, message }; + const classLen = result; + const classAndMessage = decoder.decode(ui8.subarray(12)); + const errorClass = classAndMessage.slice(0, classLen); + const message = classAndMessage.slice(classLen); + err = { kind: errorClass, message }; } else if (ui8.length != 12) { throw new TypeError("Malformed response message"); } @@ -72,11 +72,7 @@ promise.resolve(record); } - async function sendAsync( - opName, - arg, - zeroCopy, - ) { + async function sendAsync(opName, arg, zeroCopy) { const promiseId = nextPromiseId(); // AKA cmdId scratch32[0] = promiseId; scratch32[1] = arg; @@ -96,11 +92,7 @@ return unwrapResponse(res); } - function sendSync( - opName, - arg, - zeroCopy, - ) { + function sendSync(opName, arg, zeroCopy) { scratch32[0] = 0; // promiseId 0 indicates sync scratch32[1] = arg; const res = core.dispatchByName(opName, scratchBytes, zeroCopy); diff --git a/cli/rt/99_main.js b/cli/rt/99_main.js index 18cc1d2513..846216ec4d 100644 --- a/cli/rt/99_main.js +++ b/cli/rt/99_main.js @@ -195,10 +195,14 @@ delete Object.prototype.__proto__; core.registerErrorClass("UnexpectedEof", errors.UnexpectedEof); core.registerErrorClass("BadResource", errors.BadResource); core.registerErrorClass("Http", errors.Http); - core.registerErrorClass("URIError", URIError); - core.registerErrorClass("TypeError", TypeError); - core.registerErrorClass("Other", Error); core.registerErrorClass("Busy", errors.Busy); + core.registerErrorClass("NotSupported", errors.NotSupported); + core.registerErrorClass("Error", Error); + core.registerErrorClass("RangeError", RangeError); + core.registerErrorClass("ReferenceError", ReferenceError); + core.registerErrorClass("SyntaxError", SyntaxError); + core.registerErrorClass("TypeError", TypeError); + core.registerErrorClass("URIError", URIError); } // https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope diff --git a/cli/signal.rs b/cli/signal.rs index f74f9ab262..fcc70d00ef 100644 --- a/cli/signal.rs +++ b/cli/signal.rs @@ -1,5 +1,5 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -use crate::op_error::OpError; +use deno_core::ErrBox; #[cfg(not(unix))] const SIGINT: i32 = 2; @@ -19,37 +19,37 @@ use winapi::{ }; #[cfg(unix)] -pub fn kill(pid: i32, signo: i32) -> Result<(), OpError> { +pub fn kill(pid: i32, signo: i32) -> Result<(), ErrBox> { use nix::sys::signal::{kill as unix_kill, Signal}; use nix::unistd::Pid; use std::convert::TryFrom; let sig = Signal::try_from(signo)?; - unix_kill(Pid::from_raw(pid), Option::Some(sig)).map_err(OpError::from) + unix_kill(Pid::from_raw(pid), Option::Some(sig)).map_err(ErrBox::from) } #[cfg(not(unix))] -pub fn kill(pid: i32, signal: i32) -> Result<(), OpError> { +pub fn kill(pid: i32, signal: i32) -> Result<(), ErrBox> { match signal { SIGINT | SIGKILL | SIGTERM => { if pid <= 0 { - return Err(OpError::type_error("unsupported pid".to_string())); + return Err(ErrBox::type_error("unsupported pid")); } unsafe { let handle = OpenProcess(PROCESS_TERMINATE, 0, pid as DWORD); if handle.is_null() { - return Err(OpError::from(std::io::Error::last_os_error())); + return Err(ErrBox::last_os_error()); } if TerminateProcess(handle, 1) == 0 { CloseHandle(handle); - return Err(OpError::from(std::io::Error::last_os_error())); + return Err(ErrBox::last_os_error()); } if CloseHandle(handle) == 0 { - return Err(OpError::from(std::io::Error::last_os_error())); + return Err(ErrBox::last_os_error()); } } } _ => { - return Err(OpError::type_error("unsupported signal".to_string())); + return Err(ErrBox::type_error("unsupported signal".to_string())); } } Ok(()) diff --git a/cli/state.rs b/cli/state.rs index 055c09916e..cb8d507f19 100644 --- a/cli/state.rs +++ b/cli/state.rs @@ -5,7 +5,6 @@ use crate::global_timer::GlobalTimer; use crate::http_util::create_http_client; use crate::import_map::ImportMap; use crate::metrics::Metrics; -use crate::op_error::OpError; use crate::ops::serialize_result; use crate::ops::JsonOp; use crate::ops::MinimalOp; @@ -64,7 +63,7 @@ impl State { dispatcher: D, ) -> impl Fn(&mut deno_core::CoreIsolateState, &mut [ZeroCopyBuf]) -> Op where - D: Fn(&Rc, Value, &mut [ZeroCopyBuf]) -> Result, + D: Fn(&Rc, Value, &mut [ZeroCopyBuf]) -> Result, { use crate::ops::json_op; self.core_op(json_op(self.stateful_op(dispatcher))) @@ -81,18 +80,22 @@ impl State { &mut ResourceTable, Value, &mut [ZeroCopyBuf], - ) -> Result, + ) -> Result, { let state = self.clone(); let resource_table = resource_table.clone(); - move |_: &mut CoreIsolateState, bufs: &mut [ZeroCopyBuf]| { + move |isolate_state: &mut CoreIsolateState, bufs: &mut [ZeroCopyBuf]| { + let rust_err_to_json_fn = isolate_state.rust_err_to_json_fn; // The first buffer should contain JSON encoded op arguments; parse them. let args: Value = match serde_json::from_slice(&bufs[0]) { Ok(v) => v, Err(e) => { - let e = OpError::from(e); - return Op::Sync(serialize_result(None, Err(e))); + return Op::Sync(serialize_result( + rust_err_to_json_fn, + None, + Err(e.into()), + )); } }; @@ -103,7 +106,7 @@ impl State { dispatcher(&state, &mut *resource_table.borrow_mut(), args, zero_copy); // Convert to Op. - Op::Sync(serialize_result(None, result)) + Op::Sync(serialize_result(rust_err_to_json_fn, None, result)) } } @@ -115,18 +118,19 @@ impl State { where D: FnOnce(Rc, Rc>, Value, BufVec) -> F + Clone, - F: Future> + 'static, + F: Future> + 'static, { let state = self.clone(); let resource_table = resource_table.clone(); - move |_: &mut CoreIsolateState, bufs: &mut [ZeroCopyBuf]| { + move |isolate_state: &mut CoreIsolateState, bufs: &mut [ZeroCopyBuf]| { + let rust_err_to_json_fn = isolate_state.rust_err_to_json_fn; // The first buffer should contain JSON encoded op arguments; parse them. let args: Value = match serde_json::from_slice(&bufs[0]) { Ok(v) => v, Err(e) => { - let e = OpError::from(e); - return Op::Sync(serialize_result(None, Err(e))); + let e = e.into(); + return Op::Sync(serialize_result(rust_err_to_json_fn, None, Err(e))); } }; @@ -134,8 +138,8 @@ impl State { let promise_id = match args.get("promiseId").and_then(|v| v.as_u64()) { Some(i) => i, None => { - let e = OpError::type_error("`promiseId` invalid/missing".to_owned()); - return Op::Sync(serialize_result(None, Err(e))); + let e = ErrBox::new("TypeError", "`promiseId` invalid/missing"); + return Op::Sync(serialize_result(rust_err_to_json_fn, None, Err(e))); } }; @@ -152,8 +156,10 @@ impl State { // Convert to Op. Op::Async( - async move { serialize_result(Some(promise_id), fut.await) } - .boxed_local(), + async move { + serialize_result(rust_err_to_json_fn, Some(promise_id), fut.await) + } + .boxed_local(), ) } } @@ -168,7 +174,7 @@ impl State { &Rc, Value, &mut [ZeroCopyBuf], - ) -> Result, + ) -> Result, { use crate::ops::json_op; self.core_op(json_op(self.stateful_op2(dispatcher))) @@ -276,15 +282,15 @@ impl State { &mut deno_core::CoreIsolateState, Value, &mut [ZeroCopyBuf], - ) -> Result + ) -> Result where - D: Fn(&Rc, Value, &mut [ZeroCopyBuf]) -> Result, + D: Fn(&Rc, Value, &mut [ZeroCopyBuf]) -> Result, { let state = self.clone(); move |_isolate_state: &mut deno_core::CoreIsolateState, args: Value, zero_copy: &mut [ZeroCopyBuf]| - -> Result { dispatcher(&state, args, zero_copy) } + -> Result { dispatcher(&state, args, zero_copy) } } pub fn stateful_op2( @@ -294,20 +300,20 @@ impl State { &mut deno_core::CoreIsolateState, Value, &mut [ZeroCopyBuf], - ) -> Result + ) -> Result where D: Fn( &mut deno_core::CoreIsolateState, &Rc, Value, &mut [ZeroCopyBuf], - ) -> Result, + ) -> Result, { let state = self.clone(); move |isolate_state: &mut deno_core::CoreIsolateState, args: Value, zero_copy: &mut [ZeroCopyBuf]| - -> Result { + -> Result { dispatcher(isolate_state, &state, args, zero_copy) } } @@ -488,7 +494,7 @@ impl State { } #[inline] - pub fn check_read(&self, path: &Path) -> Result<(), OpError> { + pub fn check_read(&self, path: &Path) -> Result<(), ErrBox> { self.permissions.borrow().check_read(path) } @@ -499,49 +505,49 @@ impl State { &self, path: &Path, display: &str, - ) -> Result<(), OpError> { + ) -> Result<(), ErrBox> { self.permissions.borrow().check_read_blind(path, display) } #[inline] - pub fn check_write(&self, path: &Path) -> Result<(), OpError> { + pub fn check_write(&self, path: &Path) -> Result<(), ErrBox> { self.permissions.borrow().check_write(path) } #[inline] - pub fn check_env(&self) -> Result<(), OpError> { + pub fn check_env(&self) -> Result<(), ErrBox> { self.permissions.borrow().check_env() } #[inline] - pub fn check_net(&self, hostname: &str, port: u16) -> Result<(), OpError> { + pub fn check_net(&self, hostname: &str, port: u16) -> Result<(), ErrBox> { self.permissions.borrow().check_net(hostname, port) } #[inline] - pub fn check_net_url(&self, url: &url::Url) -> Result<(), OpError> { + pub fn check_net_url(&self, url: &url::Url) -> Result<(), ErrBox> { self.permissions.borrow().check_net_url(url) } #[inline] - pub fn check_run(&self) -> Result<(), OpError> { + pub fn check_run(&self) -> Result<(), ErrBox> { self.permissions.borrow().check_run() } #[inline] - pub fn check_hrtime(&self) -> Result<(), OpError> { + pub fn check_hrtime(&self) -> Result<(), ErrBox> { self.permissions.borrow().check_hrtime() } #[inline] - pub fn check_plugin(&self, filename: &Path) -> Result<(), OpError> { + pub fn check_plugin(&self, filename: &Path) -> Result<(), ErrBox> { self.permissions.borrow().check_plugin(filename) } pub fn check_dyn_import( &self, module_specifier: &ModuleSpecifier, - ) -> Result<(), OpError> { + ) -> Result<(), ErrBox> { let u = module_specifier.as_url(); // TODO(bartlomieju): temporary fix to prevent hitting `unreachable` // statement that is actually reachable... diff --git a/cli/swc_util.rs b/cli/swc_util.rs index 934bed01a7..7c7d018323 100644 --- a/cli/swc_util.rs +++ b/cli/swc_util.rs @@ -236,8 +236,7 @@ impl AstParser { media_type: MediaType, source_code: &str, ) -> Result { - let parse_result = self.parse_module(file_name, media_type, source_code); - let module = parse_result?; + let module = self.parse_module(file_name, media_type, source_code)?; let program = Program::Module(module); let mut compiler_pass = chain!(typescript::strip(), fixer(Some(&self.comments))); @@ -261,7 +260,7 @@ impl AstParser { }; program.emit_with(&mut emitter)?; } - let mut src = String::from_utf8(buf).map_err(ErrBox::from)?; + let mut src = String::from_utf8(buf)?; { let mut buf = vec![]; self diff --git a/cli/tests/unit/dispatch_json_test.ts b/cli/tests/unit/dispatch_json_test.ts index e5200aa5b9..fe05122d5f 100644 --- a/cli/tests/unit/dispatch_json_test.ts +++ b/cli/tests/unit/dispatch_json_test.ts @@ -39,7 +39,7 @@ unitTest(function malformedJsonControlBuffer(): void { const resText = new TextDecoder().decode(resBuf); const resObj = JSON.parse(resText); assertStrictEquals(resObj.ok, undefined); - assertStrictEquals(resObj.err.kind, "TypeError"); + assertStrictEquals(resObj.err.kind, "SyntaxError"); assertMatch(resObj.err.message, /\bexpected value\b/); }); diff --git a/cli/tsc.rs b/cli/tsc.rs index 5fa8b67a35..d509d99cea 100644 --- a/cli/tsc.rs +++ b/cli/tsc.rs @@ -12,7 +12,6 @@ use crate::module_graph::ModuleGraph; use crate::module_graph::ModuleGraphLoader; use crate::msg; use crate::msg::MediaType; -use crate::op_error::OpError; use crate::ops; use crate::permissions::Permissions; use crate::source_maps::SourceMapGetter; @@ -618,7 +617,7 @@ impl TsCompiler { let compile_response: CompileResponse = serde_json::from_str(&json_str)?; if !compile_response.diagnostics.items.is_empty() { - return Err(ErrBox::from(compile_response.diagnostics)); + return Err(ErrBox::error(compile_response.diagnostics.to_string())); } maybe_log_stats(compile_response.stats); @@ -722,7 +721,7 @@ impl TsCompiler { maybe_log_stats(bundle_response.stats); if !bundle_response.diagnostics.items.is_empty() { - return Err(ErrBox::from(bundle_response.diagnostics)); + return Err(ErrBox::error(bundle_response.diagnostics.to_string())); } assert!(bundle_response.bundle_output.is_some()); @@ -1071,7 +1070,7 @@ async fn create_runtime_module_graph( root_name: &str, sources: &Option>, maybe_options: &Option, -) -> Result<(Vec, ModuleGraph), OpError> { +) -> Result<(Vec, ModuleGraph), ErrBox> { let mut root_names = vec![]; let mut module_graph_loader = ModuleGraphLoader::new( global_state.file_fetcher.clone(), @@ -1117,14 +1116,14 @@ async fn create_runtime_module_graph( } /// Because TS compiler can raise runtime error, we need to -/// manually convert formatted JSError into and OpError. -fn js_error_to_op_error(error: ErrBox) -> OpError { +/// manually convert formatted JSError into and ErrBox. +fn js_error_to_errbox(error: ErrBox) -> ErrBox { match error.downcast::() { Ok(js_error) => { let msg = format!("Error in TS compiler:\n{}", js_error); - OpError::other(msg) + ErrBox::error(msg) } - Err(error) => error.into(), + Err(error) => error, } } @@ -1135,7 +1134,7 @@ pub async fn runtime_compile( root_name: &str, sources: &Option>, maybe_options: &Option, -) -> Result { +) -> Result { let (root_names, module_graph) = create_runtime_module_graph( &global_state, permissions.clone(), @@ -1161,7 +1160,7 @@ pub async fn runtime_compile( let json_str = execute_in_same_thread(global_state, permissions, req_msg) .await - .map_err(js_error_to_op_error)?; + .map_err(js_error_to_errbox)?; let response: RuntimeCompileResponse = serde_json::from_str(&json_str)?; @@ -1182,7 +1181,7 @@ pub async fn runtime_bundle( root_name: &str, sources: &Option>, maybe_options: &Option, -) -> Result { +) -> Result { let (root_names, module_graph) = create_runtime_module_graph( &global_state, permissions.clone(), @@ -1206,7 +1205,7 @@ pub async fn runtime_bundle( let json_str = execute_in_same_thread(global_state, permissions, req_msg) .await - .map_err(js_error_to_op_error)?; + .map_err(js_error_to_errbox)?; let _response: RuntimeBundleResponse = serde_json::from_str(&json_str)?; // We're returning `Ok()` instead of `Err()` because it's not runtime // error if there were diagnostics produced; we want to let user handle @@ -1220,7 +1219,7 @@ pub async fn runtime_transpile( permissions: Permissions, sources: &HashMap, options: &Option, -) -> Result { +) -> Result { let req_msg = json!({ "type": msg::CompilerRequestType::RuntimeTranspile, "sources": sources, @@ -1230,7 +1229,7 @@ pub async fn runtime_transpile( let json_str = execute_in_same_thread(global_state, permissions, req_msg) .await - .map_err(js_error_to_op_error)?; + .map_err(js_error_to_errbox)?; let v = serde_json::from_str::(&json_str) .expect("Error decoding JSON string."); Ok(v) diff --git a/cli/upgrade.rs b/cli/upgrade.rs index 244bf6c8af..1009e5c774 100644 --- a/cli/upgrade.rs +++ b/cli/upgrade.rs @@ -10,7 +10,6 @@ extern crate semver_parser; use crate::futures::FutureExt; use crate::http_util::fetch_once; use crate::http_util::FetchOnceResult; -use crate::op_error::OpError; use crate::ErrBox; use regex::Regex; use reqwest::{redirect::Policy, Client}; @@ -166,7 +165,7 @@ fn compose_url_to_exec(version: &Version) -> Result { "https://github.com/denoland/deno/releases/download/v{}/{}", version, ARCHIVE_NAME ); - Ok(Url::parse(&s)?) + Url::parse(&s).map_err(ErrBox::from) } fn find_version(text: &str) -> Result { @@ -175,10 +174,10 @@ fn find_version(text: &str) -> Result { let mat = _mat.as_str(); return Ok(mat[1..mat.len() - 1].to_string()); } - Err(OpError::other("Cannot read latest tag version".to_string()).into()) + Err(ErrBox::new("NotFound", "Cannot read latest tag version")) } -fn unpack(archive_data: Vec) -> Result { +fn unpack(archive_data: Vec) -> Result { // We use into_path so that the tempdir is not automatically deleted. This is // useful for debugging upgrade, but also so this function can return a path // to the newly uncompressed file without fear of the tempdir being deleted. @@ -244,7 +243,7 @@ fn unpack(archive_data: Vec) -> Result { Ok(exe_path) } -fn replace_exe(new: &Path, old: &Path) -> Result<(), ErrBox> { +fn replace_exe(new: &Path, old: &Path) -> Result<(), std::io::Error> { if cfg!(windows) { // On windows you cannot replace the currently running executable. // so first we rename it to deno.old.exe diff --git a/cli/worker.rs b/cli/worker.rs index 928d3316fb..cfdcaeda7b 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -1,4 +1,5 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +use crate::errors::rust_err_to_json; use crate::fmt_errors::JSError; use crate::global_state::GlobalState; use crate::inspector::DenoInspector; @@ -51,7 +52,8 @@ impl WorkerHandle { /// Post message to worker as a host. pub fn post_message(&self, buf: Buf) -> Result<(), ErrBox> { let mut sender = self.sender.clone(); - sender.try_send(buf).map_err(ErrBox::from) + sender.try_send(buf)?; + Ok(()) } /// Get the event with lock. @@ -114,6 +116,7 @@ impl Worker { core_state.set_js_error_create_fn(move |core_js_error| { JSError::create(core_js_error, &global_state.ts_compiler) }); + core_state.set_rust_err_to_json_fn(&rust_err_to_json); } let inspector = { diff --git a/core/core_isolate.rs b/core/core_isolate.rs index 160e37f1d6..f4acbd1ac6 100644 --- a/core/core_isolate.rs +++ b/core/core_isolate.rs @@ -87,6 +87,17 @@ impl StartupData<'_> { type JSErrorCreateFn = dyn Fn(JSError) -> ErrBox; +pub trait RustErrToJsonFn: for<'e> Fn(&'e ErrBox) -> Box<[u8]> {} +impl RustErrToJsonFn for F where for<'e> F: Fn(&'e ErrBox) -> Box<[u8]> {} + +fn rust_err_to_json(e: &ErrBox) -> Box<[u8]> { + let value = json!({ + "kind": "Error", + "message": e.to_string() + }); + serde_json::to_vec(&value).unwrap().into_boxed_slice() +} + /// Objects that need to live as long as the isolate #[derive(Default)] struct IsolateAllocations { @@ -123,6 +134,7 @@ pub struct CoreIsolateState { pub(crate) js_macrotask_cb: Option>, pub(crate) pending_promise_exceptions: HashMap>, pub(crate) js_error_create_fn: Box, + pub rust_err_to_json_fn: &'static dyn RustErrToJsonFn, pub(crate) shared: SharedQueue, pending_ops: FuturesUnordered, pending_unref_ops: FuturesUnordered, @@ -307,6 +319,7 @@ impl CoreIsolate { js_recv_cb: None, js_macrotask_cb: None, js_error_create_fn: Box::new(JSError::create), + rust_err_to_json_fn: &rust_err_to_json, shared: SharedQueue::new(RECOMMENDED_SIZE), pending_ops: FuturesUnordered::new(), pending_unref_ops: FuturesUnordered::new(), @@ -539,8 +552,8 @@ fn serialize_result( Err(err) => json!({ "promiseId": promise_id , "err": { + "kind": "Error", "message": err.to_string(), - "kind": "Other", // TODO(ry) Figure out how to propagate errors. } }), }; @@ -668,6 +681,13 @@ impl CoreIsolateState { self.js_error_create_fn = Box::new(f); } + pub fn set_rust_err_to_json_fn( + &mut self, + f: &'static (impl for<'e> Fn(&'e ErrBox) -> Box<[u8]> + 'static), + ) { + self.rust_err_to_json_fn = f; + } + pub fn dispatch_op<'s>( &mut self, scope: &mut v8::HandleScope<'s>, diff --git a/core/errors.rs b/core/errors.rs index 218d7c6197..3dfe5824c9 100644 --- a/core/errors.rs +++ b/core/errors.rs @@ -3,11 +3,12 @@ use rusty_v8 as v8; use std::any::Any; use std::any::TypeId; +use std::borrow::Cow; use std::convert::TryFrom; use std::convert::TryInto; use std::error::Error; use std::fmt; -use std::ops::Deref; +use std::io; // The Send and Sync traits are required because deno is multithreaded and we // need to be able to handle errors across threads. @@ -15,60 +16,97 @@ pub trait AnyError: Any + Error + Send + Sync + 'static {} impl AnyError for T where T: Any + Error + Send + Sync + Sized + 'static {} #[derive(Debug)] -pub struct ErrBox(Box); - -impl dyn AnyError { - pub fn downcast_ref(&self) -> Option<&T> { - if Any::type_id(self) == TypeId::of::() { - let target = self as *const Self as *const T; - let target = unsafe { &*target }; - Some(target) - } else { - None - } - } +pub enum ErrBox { + Simple { + class: &'static str, + message: Cow<'static, str>, + }, + Boxed(Box), } impl ErrBox { - pub fn downcast(self) -> Result { - if Any::type_id(&*self.0) == TypeId::of::() { - let target = Box::into_raw(self.0) as *mut T; - let target = unsafe { Box::from_raw(target) }; - Ok(*target) - } else { - Err(self) + pub fn new( + class: &'static str, + message: impl Into>, + ) -> Self { + Self::Simple { + class, + message: message.into(), } } -} -impl AsRef for ErrBox { - fn as_ref(&self) -> &dyn AnyError { - self.0.as_ref() + pub fn bad_resource(message: impl Into>) -> Self { + Self::new("BadResource", message) } -} -impl Deref for ErrBox { - type Target = Box; - fn deref(&self) -> &Self::Target { - &self.0 + pub fn bad_resource_id() -> Self { + Self::new("BadResource", "Bad resource ID") } -} -impl From for ErrBox { - fn from(error: T) -> Self { - Self(Box::new(error)) + pub fn error(message: impl Into>) -> Self { + Self::new("Error", message) } -} -impl From> for ErrBox { - fn from(boxed: Box) -> Self { - Self(boxed) + pub fn not_supported() -> Self { + Self::new("NotSupported", "The operation is supported") + } + + pub fn resource_unavailable() -> Self { + Self::new( + "Busy", + "Resource is unavailable because it is in use by a promise", + ) + } + + pub fn type_error(message: impl Into>) -> Self { + Self::new("TypeError", message) + } + + pub fn last_os_error() -> Self { + Self::from(io::Error::last_os_error()) + } + + pub fn downcast(self) -> Result { + match self { + Self::Boxed(error) if Any::type_id(&*error) == TypeId::of::() => { + let error = Box::into_raw(error) as *mut T; + let error = unsafe { Box::from_raw(error) }; + Ok(*error) + } + other => Err(other), + } + } + + pub fn downcast_ref(&self) -> Option<&T> { + match self { + Self::Boxed(error) if Any::type_id(&**error) == TypeId::of::() => { + let error = &**error as *const dyn AnyError as *const T; + let error = unsafe { &*error }; + Some(error) + } + _ => None, + } } } impl fmt::Display for ErrBox { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.0.fmt(f) + match self { + Self::Simple { message, .. } => f.write_str(message), + Self::Boxed(error) => error.fmt(f), + } + } +} + +impl From for ErrBox { + fn from(error: T) -> Self { + Self::Boxed(Box::new(error)) + } +} + +impl From> for ErrBox { + fn from(boxed: Box) -> Self { + Self::Boxed(boxed) } } @@ -116,7 +154,7 @@ fn get_property<'a>( impl JSError { pub(crate) fn create(js_error: Self) -> ErrBox { - ErrBox::from(js_error) + js_error.into() } pub fn from_v8_exception( @@ -362,6 +400,7 @@ pub(crate) fn attach_handle_to_error( err: ErrBox, handle: v8::Local, ) -> ErrBox { + // TODO(bartomieju): this is a special case... ErrWithV8Handle::new(scope, err, handle).into() } @@ -406,3 +445,20 @@ impl fmt::Debug for ErrWithV8Handle { self.err.fmt(f) } } + +#[cfg(tests)] +mod tests { + #[test] + fn test_bad_resource() { + let err = ErrBox::bad_resource("Resource has been closed".to_string()); + assert_eq!(err.1, "BadResource"); + assert_eq!(err.to_string(), "Resource has been closed"); + } + + #[test] + fn test_bad_resource_id() { + let err = ErrBox::bad_resource_id(); + assert_eq!(err.1, "BadResource"); + assert_eq!(err.to_string(), "Bad resource ID"); + } +} diff --git a/core/es_isolate.rs b/core/es_isolate.rs index 3ede1a5e42..a829a3a3b0 100644 --- a/core/es_isolate.rs +++ b/core/es_isolate.rs @@ -784,8 +784,7 @@ pub mod tests { _maybe_referrer: Option, _is_dyn_import: bool, ) -> Pin> { - async { Err(ErrBox::from(io::Error::from(io::ErrorKind::NotFound))) } - .boxed() + async { Err(io::Error::from(io::ErrorKind::NotFound).into()) }.boxed() } } diff --git a/core/lib.rs b/core/lib.rs index 940e0c0269..78a2fc2449 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -28,6 +28,7 @@ pub use crate::core_isolate::js_check; pub use crate::core_isolate::CoreIsolate; pub use crate::core_isolate::CoreIsolateState; pub use crate::core_isolate::HeapLimits; +pub use crate::core_isolate::RustErrToJsonFn; pub use crate::core_isolate::Script; pub use crate::core_isolate::Snapshot; pub use crate::core_isolate::StartupData;