summaryrefslogtreecommitdiff
path: root/cli
diff options
context:
space:
mode:
Diffstat (limited to 'cli')
-rw-r--r--cli/ansi.rs8
-rw-r--r--cli/compiler.rs27
-rw-r--r--cli/deno_dir.rs30
-rw-r--r--cli/deno_error.rs538
-rw-r--r--cli/diagnostics.rs125
-rw-r--r--cli/dispatch_minimal.rs16
-rw-r--r--cli/errors.rs295
-rw-r--r--cli/fmt_errors.rs289
-rw-r--r--cli/fs.rs8
-rw-r--r--cli/http_util.rs14
-rw-r--r--cli/main.rs19
-rw-r--r--cli/msg.fbs6
-rw-r--r--cli/msg_util.rs6
-rw-r--r--cli/ops.rs101
-rw-r--r--cli/permissions.rs4
-rw-r--r--cli/repl.rs4
-rw-r--r--cli/resources.rs26
-rw-r--r--cli/signal.rs4
-rw-r--r--cli/source_maps.rs (renamed from cli/js_errors.rs)391
-rw-r--r--cli/state.rs4
-rw-r--r--cli/worker.rs52
21 files changed, 1243 insertions, 724 deletions
diff --git a/cli/ansi.rs b/cli/ansi.rs
index b9e9fe123..32936ace0 100644
--- a/cli/ansi.rs
+++ b/cli/ansi.rs
@@ -79,14 +79,6 @@ pub fn red(s: String) -> impl fmt::Display {
style.paint(s)
}
-pub fn grey(s: String) -> impl fmt::Display {
- let mut style = Style::new();
- if use_color() {
- style = style.fg(Fixed(8));
- }
- style.paint(s)
-}
-
pub fn bold(s: String) -> impl fmt::Display {
let mut style = Style::new();
if use_color() {
diff --git a/cli/compiler.rs b/cli/compiler.rs
index de5b5c609..b6fb0375c 100644
--- a/cli/compiler.rs
+++ b/cli/compiler.rs
@@ -1,4 +1,6 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+use crate::deno_error::err_check;
+use crate::deno_error::DenoError;
use crate::diagnostics::Diagnostic;
use crate::msg;
use crate::resources;
@@ -6,7 +8,6 @@ use crate::startup_data;
use crate::state::*;
use crate::tokio_util;
use crate::worker::Worker;
-use deno::js_check;
use deno::Buf;
use futures::Future;
use futures::Stream;
@@ -92,7 +93,7 @@ pub fn bundle_async(
state: ThreadSafeState,
module_name: String,
out_file: String,
-) -> impl Future<Item = (), Error = Diagnostic> {
+) -> impl Future<Item = (), Error = DenoError> {
debug!(
"Invoking the compiler to bundle. module_name: {}",
module_name
@@ -112,9 +113,9 @@ pub fn bundle_async(
// as was done previously.
state.clone(),
);
- js_check(worker.execute("denoMain()"));
- js_check(worker.execute("workerMain()"));
- js_check(worker.execute("compilerMain()"));
+ err_check(worker.execute("denoMain()"));
+ err_check(worker.execute("workerMain()"));
+ err_check(worker.execute("compilerMain()"));
let resource = worker.state.resource.clone();
let compiler_rid = resource.rid;
@@ -140,7 +141,7 @@ pub fn bundle_async(
let json_str = std::str::from_utf8(&msg).unwrap();
debug!("Message: {}", json_str);
if let Some(diagnostics) = Diagnostic::from_emit_result(json_str) {
- return Err(diagnostics);
+ return Err(DenoError::from(diagnostics));
}
}
@@ -152,7 +153,7 @@ pub fn bundle_async(
pub fn compile_async(
state: ThreadSafeState,
module_meta_data: &ModuleMetaData,
-) -> impl Future<Item = ModuleMetaData, Error = Diagnostic> {
+) -> impl Future<Item = ModuleMetaData, Error = DenoError> {
let module_name = module_meta_data.module_name.clone();
debug!(
@@ -174,9 +175,9 @@ pub fn compile_async(
// as was done previously.
state.clone(),
);
- js_check(worker.execute("denoMain()"));
- js_check(worker.execute("workerMain()"));
- js_check(worker.execute("compilerMain()"));
+ err_check(worker.execute("denoMain()"));
+ err_check(worker.execute("workerMain()"));
+ err_check(worker.execute("compilerMain()"));
let compiling_job = state.progress.add(format!("Compiling {}", module_name));
@@ -205,7 +206,7 @@ pub fn compile_async(
let json_str = std::str::from_utf8(&msg).unwrap();
debug!("Message: {}", json_str);
if let Some(diagnostics) = Diagnostic::from_emit_result(json_str) {
- return Err(diagnostics);
+ return Err(DenoError::from(diagnostics));
}
}
@@ -235,7 +236,7 @@ pub fn compile_async(
pub fn compile_sync(
state: ThreadSafeState,
module_meta_data: &ModuleMetaData,
-) -> Result<ModuleMetaData, Diagnostic> {
+) -> Result<ModuleMetaData, DenoError> {
tokio_util::block_on(compile_async(state, module_meta_data))
}
@@ -306,6 +307,6 @@ mod tests {
]);
let out =
bundle_async(state, module_name, String::from("$deno$/bundle.js"));
- assert_eq!(tokio_util::block_on(out), Ok(()));
+ assert!(tokio_util::block_on(out).is_ok());
}
}
diff --git a/cli/deno_dir.rs b/cli/deno_dir.rs
index c0ee051b8..ba0217bbe 100644
--- a/cli/deno_dir.rs
+++ b/cli/deno_dir.rs
@@ -1,14 +1,14 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
use crate::compiler::ModuleMetaData;
-use crate::errors;
-use crate::errors::DenoError;
-use crate::errors::DenoResult;
-use crate::errors::ErrorKind;
+use crate::deno_error;
+use crate::deno_error::DenoError;
+use crate::deno_error::DenoResult;
+use crate::deno_error::ErrorKind;
use crate::fs as deno_fs;
use crate::http_util;
-use crate::js_errors::SourceMapGetter;
use crate::msg;
use crate::progress::Progress;
+use crate::source_maps::SourceMapGetter;
use crate::tokio_util;
use crate::version;
use dirs;
@@ -152,7 +152,7 @@ impl DenoDir {
referrer: &str,
use_cache: bool,
no_fetch: bool,
- ) -> impl Future<Item = ModuleMetaData, Error = errors::DenoError> {
+ ) -> impl Future<Item = ModuleMetaData, Error = deno_error::DenoError> {
debug!(
"fetch_module_meta_data. specifier {} referrer {}",
specifier, referrer
@@ -187,7 +187,7 @@ impl DenoDir {
Err(err) => {
if err.kind() == ErrorKind::NotFound {
// For NotFound, change the message to something better.
- return Err(errors::new(
+ return Err(deno_error::new(
ErrorKind::NotFound,
format!(
"Cannot resolve module \"{}\" from \"{}\"",
@@ -255,7 +255,7 @@ impl DenoDir {
referrer: &str,
use_cache: bool,
no_fetch: bool,
- ) -> Result<ModuleMetaData, errors::DenoError> {
+ ) -> Result<ModuleMetaData, deno_error::DenoError> {
tokio_util::block_on(
self
.fetch_module_meta_data_async(specifier, referrer, use_cache, no_fetch),
@@ -349,6 +349,20 @@ impl SourceMapGetter for DenoDir {
},
}
}
+
+ fn get_source_line(&self, script_name: &str, line: usize) -> Option<String> {
+ match self.fetch_module_meta_data(script_name, ".", true, true) {
+ Ok(out) => match str::from_utf8(&out.source_code) {
+ Ok(v) => {
+ let lines: Vec<&str> = v.lines().collect();
+ assert!(lines.len() > line);
+ Some(lines[line].to_string())
+ }
+ _ => None,
+ },
+ _ => None,
+ }
+ }
}
/// This fetches source code, locally or remotely.
diff --git a/cli/deno_error.rs b/cli/deno_error.rs
new file mode 100644
index 000000000..6e14f3902
--- /dev/null
+++ b/cli/deno_error.rs
@@ -0,0 +1,538 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+use crate::diagnostics;
+use crate::fmt_errors::JSErrorColor;
+use crate::import_map;
+pub use crate::msg::ErrorKind;
+use crate::resolve_addr::ResolveAddrError;
+use crate::source_maps::apply_source_map;
+use crate::source_maps::SourceMapGetter;
+use deno::JSError;
+use hyper;
+#[cfg(unix)]
+use nix::{errno::Errno, Error as UnixError};
+use std;
+use std::fmt;
+use std::io;
+use std::str;
+use url;
+
+pub type DenoResult<T> = std::result::Result<T, DenoError>;
+
+#[derive(Debug)]
+pub struct DenoError {
+ repr: Repr,
+}
+
+#[derive(Debug)]
+enum Repr {
+ Simple(ErrorKind, String),
+ IoErr(io::Error),
+ UrlErr(url::ParseError),
+ HyperErr(hyper::Error),
+ ImportMapErr(import_map::ImportMapError),
+ Diagnostic(diagnostics::Diagnostic),
+ JSError(JSError),
+}
+
+/// Create a new simple DenoError.
+pub fn new(kind: ErrorKind, msg: String) -> DenoError {
+ DenoError {
+ repr: Repr::Simple(kind, msg),
+ }
+}
+
+impl DenoError {
+ pub fn kind(&self) -> ErrorKind {
+ match self.repr {
+ Repr::Simple(kind, ref _msg) => kind,
+ // Repr::Simple(kind) => kind,
+ Repr::IoErr(ref err) => {
+ use std::io::ErrorKind::*;
+ match err.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,
+ WouldBlock => ErrorKind::WouldBlock,
+ InvalidInput => ErrorKind::InvalidInput,
+ InvalidData => ErrorKind::InvalidData,
+ TimedOut => ErrorKind::TimedOut,
+ Interrupted => ErrorKind::Interrupted,
+ WriteZero => ErrorKind::WriteZero,
+ Other => ErrorKind::Other,
+ UnexpectedEof => ErrorKind::UnexpectedEof,
+ _ => unreachable!(),
+ }
+ }
+ Repr::UrlErr(ref err) => {
+ use url::ParseError::*;
+ match err {
+ EmptyHost => ErrorKind::EmptyHost,
+ IdnaError => ErrorKind::IdnaError,
+ InvalidPort => ErrorKind::InvalidPort,
+ InvalidIpv4Address => ErrorKind::InvalidIpv4Address,
+ InvalidIpv6Address => ErrorKind::InvalidIpv6Address,
+ InvalidDomainCharacter => ErrorKind::InvalidDomainCharacter,
+ RelativeUrlWithoutBase => ErrorKind::RelativeUrlWithoutBase,
+ RelativeUrlWithCannotBeABaseBase => {
+ ErrorKind::RelativeUrlWithCannotBeABaseBase
+ }
+ SetHostOnCannotBeABaseUrl => ErrorKind::SetHostOnCannotBeABaseUrl,
+ Overflow => ErrorKind::Overflow,
+ }
+ }
+ Repr::HyperErr(ref err) => {
+ // For some reason hyper::errors::Kind is private.
+ if err.is_parse() {
+ ErrorKind::HttpParse
+ } else if err.is_user() {
+ ErrorKind::HttpUser
+ } else if err.is_canceled() {
+ ErrorKind::HttpCanceled
+ } else if err.is_closed() {
+ ErrorKind::HttpClosed
+ } else {
+ ErrorKind::HttpOther
+ }
+ }
+ Repr::ImportMapErr(ref _err) => ErrorKind::ImportMapError,
+ Repr::Diagnostic(ref _err) => ErrorKind::Diagnostic,
+ Repr::JSError(ref _err) => ErrorKind::JSError,
+ }
+ }
+
+ pub fn apply_source_map<G: SourceMapGetter>(self, getter: &G) -> Self {
+ if let Repr::JSError(js_error) = self.repr {
+ return DenoError {
+ repr: Repr::JSError(apply_source_map(&js_error, getter)),
+ };
+ } else {
+ panic!("attempt to apply source map an unremappable error")
+ }
+ }
+}
+
+impl fmt::Display for DenoError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self.repr {
+ Repr::Simple(_kind, ref err_str) => f.pad(err_str),
+ Repr::IoErr(ref err) => err.fmt(f),
+ Repr::UrlErr(ref err) => err.fmt(f),
+ Repr::HyperErr(ref err) => err.fmt(f),
+ Repr::ImportMapErr(ref err) => f.pad(&err.msg),
+ Repr::Diagnostic(ref err) => err.fmt(f),
+ Repr::JSError(ref err) => JSErrorColor(err).fmt(f),
+ }
+ }
+}
+
+impl std::error::Error for DenoError {
+ fn description(&self) -> &str {
+ match self.repr {
+ Repr::Simple(_kind, ref msg) => msg.as_str(),
+ Repr::IoErr(ref err) => err.description(),
+ Repr::UrlErr(ref err) => err.description(),
+ Repr::HyperErr(ref err) => err.description(),
+ Repr::ImportMapErr(ref err) => &err.msg,
+ Repr::Diagnostic(ref err) => &err.items[0].message,
+ Repr::JSError(ref err) => &err.description(),
+ }
+ }
+
+ fn cause(&self) -> Option<&dyn std::error::Error> {
+ match self.repr {
+ Repr::Simple(_kind, ref _msg) => None,
+ Repr::IoErr(ref err) => Some(err),
+ Repr::UrlErr(ref err) => Some(err),
+ Repr::HyperErr(ref err) => Some(err),
+ Repr::ImportMapErr(ref _err) => None,
+ Repr::Diagnostic(ref _err) => None,
+ Repr::JSError(ref err) => Some(err),
+ }
+ }
+}
+
+impl From<io::Error> for DenoError {
+ #[inline]
+ fn from(err: io::Error) -> Self {
+ Self {
+ repr: Repr::IoErr(err),
+ }
+ }
+}
+
+impl From<url::ParseError> for DenoError {
+ #[inline]
+ fn from(err: url::ParseError) -> Self {
+ Self {
+ repr: Repr::UrlErr(err),
+ }
+ }
+}
+
+impl From<hyper::Error> for DenoError {
+ #[inline]
+ fn from(err: hyper::Error) -> Self {
+ Self {
+ repr: Repr::HyperErr(err),
+ }
+ }
+}
+
+impl From<ResolveAddrError> for DenoError {
+ fn from(e: ResolveAddrError) -> Self {
+ match e {
+ ResolveAddrError::Syntax => Self {
+ repr: Repr::Simple(
+ ErrorKind::InvalidInput,
+ "invalid address syntax".to_string(),
+ ),
+ },
+ ResolveAddrError::Resolution(io_err) => Self {
+ repr: Repr::IoErr(io_err),
+ },
+ }
+ }
+}
+
+#[cfg(unix)]
+impl From<UnixError> for DenoError {
+ fn from(e: UnixError) -> Self {
+ match e {
+ UnixError::Sys(Errno::EPERM) => Self {
+ repr: Repr::Simple(
+ ErrorKind::PermissionDenied,
+ Errno::EPERM.desc().to_owned(),
+ ),
+ },
+ UnixError::Sys(Errno::EINVAL) => Self {
+ repr: Repr::Simple(
+ ErrorKind::InvalidInput,
+ Errno::EINVAL.desc().to_owned(),
+ ),
+ },
+ UnixError::Sys(Errno::ENOENT) => Self {
+ repr: Repr::Simple(
+ ErrorKind::NotFound,
+ Errno::ENOENT.desc().to_owned(),
+ ),
+ },
+ UnixError::Sys(err) => Self {
+ repr: Repr::Simple(ErrorKind::UnixError, err.desc().to_owned()),
+ },
+ _ => Self {
+ repr: Repr::Simple(ErrorKind::Other, format!("{}", e)),
+ },
+ }
+ }
+}
+
+impl From<import_map::ImportMapError> for DenoError {
+ fn from(err: import_map::ImportMapError) -> Self {
+ Self {
+ repr: Repr::ImportMapErr(err),
+ }
+ }
+}
+
+impl From<diagnostics::Diagnostic> for DenoError {
+ fn from(diagnostic: diagnostics::Diagnostic) -> Self {
+ Self {
+ repr: Repr::Diagnostic(diagnostic),
+ }
+ }
+}
+
+impl From<JSError> for DenoError {
+ fn from(err: JSError) -> Self {
+ Self {
+ repr: Repr::JSError(err),
+ }
+ }
+}
+
+pub fn bad_resource() -> DenoError {
+ new(ErrorKind::BadResource, String::from("bad resource id"))
+}
+
+pub fn permission_denied() -> DenoError {
+ new(
+ ErrorKind::PermissionDenied,
+ String::from("permission denied"),
+ )
+}
+
+pub fn op_not_implemented() -> DenoError {
+ new(
+ ErrorKind::OpNotAvailable,
+ String::from("op not implemented"),
+ )
+}
+
+pub fn worker_init_failed() -> DenoError {
+ // TODO(afinch7) pass worker error data through here
+ new(
+ ErrorKind::WorkerInitFailed,
+ String::from("worker init failed"),
+ )
+}
+
+pub fn no_buffer_specified() -> DenoError {
+ new(ErrorKind::InvalidInput, String::from("no buffer specified"))
+}
+
+pub fn no_async_support() -> DenoError {
+ new(
+ ErrorKind::NoAsyncSupport,
+ String::from("op doesn't support async calls"),
+ )
+}
+
+pub fn no_sync_support() -> DenoError {
+ new(
+ ErrorKind::NoSyncSupport,
+ String::from("op doesn't support sync calls"),
+ )
+}
+
+pub fn err_check<R>(r: Result<R, DenoError>) {
+ if let Err(e) = r {
+ panic!(e.to_string());
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::ansi::strip_ansi_codes;
+ use crate::diagnostics::Diagnostic;
+ use crate::diagnostics::DiagnosticCategory;
+ use crate::diagnostics::DiagnosticItem;
+ use crate::import_map::ImportMapError;
+ use deno::StackFrame;
+
+ fn js_error() -> JSError {
+ JSError {
+ message: "Error: foo bar".to_string(),
+ source_line: None,
+ script_resource_name: None,
+ line_number: None,
+ start_position: None,
+ end_position: None,
+ error_level: None,
+ start_column: None,
+ end_column: None,
+ frames: vec![
+ StackFrame {
+ line: 4,
+ column: 16,
+ script_name: "foo_bar.ts".to_string(),
+ function_name: "foo".to_string(),
+ is_eval: false,
+ is_constructor: false,
+ is_wasm: false,
+ },
+ StackFrame {
+ line: 5,
+ column: 20,
+ script_name: "bar_baz.ts".to_string(),
+ function_name: "qat".to_string(),
+ is_eval: false,
+ is_constructor: false,
+ is_wasm: false,
+ },
+ StackFrame {
+ line: 1,
+ column: 1,
+ script_name: "deno_main.js".to_string(),
+ function_name: "".to_string(),
+ is_eval: false,
+ is_constructor: false,
+ is_wasm: false,
+ },
+ ],
+ }
+ }
+
+ fn diagnostic() -> Diagnostic {
+ Diagnostic {
+ items: vec![
+ DiagnosticItem {
+ message: "Example 1".to_string(),
+ message_chain: None,
+ code: 2322,
+ category: DiagnosticCategory::Error,
+ start_position: Some(267),
+ end_position: Some(273),
+ source_line: Some(" values: o => [".to_string()),
+ line_number: Some(18),
+ script_resource_name: Some(
+ "deno/tests/complex_diagnostics.ts".to_string(),
+ ),
+ start_column: Some(2),
+ end_column: Some(8),
+ related_information: None,
+ },
+ DiagnosticItem {
+ message: "Example 2".to_string(),
+ message_chain: None,
+ code: 2000,
+ category: DiagnosticCategory::Error,
+ start_position: Some(2),
+ end_position: Some(2),
+ source_line: Some(" values: undefined,".to_string()),
+ line_number: Some(128),
+ script_resource_name: Some("/foo/bar.ts".to_string()),
+ start_column: Some(2),
+ end_column: Some(8),
+ related_information: None,
+ },
+ ],
+ }
+ }
+
+ struct MockSourceMapGetter {}
+
+ impl SourceMapGetter for MockSourceMapGetter {
+ fn get_source_map(&self, _script_name: &str) -> Option<Vec<u8>> {
+ Some(vec![])
+ }
+
+ fn get_source_line(
+ &self,
+ _script_name: &str,
+ _line: usize,
+ ) -> Option<String> {
+ None
+ }
+ }
+
+ fn io_error() -> io::Error {
+ 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 = new(ErrorKind::NoError, "foo".to_string());
+ assert_eq!(err.kind(), ErrorKind::NoError);
+ assert_eq!(err.to_string(), "foo");
+ }
+
+ #[test]
+ fn test_io_error() {
+ let err = DenoError::from(io_error());
+ assert_eq!(err.kind(), ErrorKind::NotFound);
+ assert_eq!(err.to_string(), "entity not found");
+ }
+
+ #[test]
+ fn test_url_error() {
+ let err = DenoError::from(url_error());
+ assert_eq!(err.kind(), ErrorKind::EmptyHost);
+ assert_eq!(err.to_string(), "empty host");
+ }
+
+ // TODO find a way to easily test tokio errors and unix errors
+
+ #[test]
+ fn test_diagnostic() {
+ let err = DenoError::from(diagnostic());
+ assert_eq!(err.kind(), ErrorKind::Diagnostic);
+ assert_eq!(strip_ansi_codes(&err.to_string()), "error TS2322: Example 1\n\n► deno/tests/complex_diagnostics.ts:19:3\n\n19 values: o => [\n ~~~~~~\n\nerror TS2000: Example 2\n\n► /foo/bar.ts:129:3\n\n129 values: undefined,\n ~~~~~~\n\n\nFound 2 errors.\n");
+ }
+
+ #[test]
+ fn test_js_error() {
+ let err = DenoError::from(js_error());
+ assert_eq!(err.kind(), ErrorKind::JSError);
+ assert_eq!(strip_ansi_codes(&err.to_string()), "error: Error: foo bar\n at foo (foo_bar.ts:5:17)\n at qat (bar_baz.ts:6:21)\n at deno_main.js:2:2");
+ }
+
+ #[test]
+ fn test_import_map_error() {
+ let err = DenoError::from(import_map_error());
+ assert_eq!(err.kind(), ErrorKind::ImportMapError);
+ assert_eq!(err.to_string(), "an import map error");
+ }
+
+ #[test]
+ fn test_bad_resource() {
+ let err = bad_resource();
+ assert_eq!(err.kind(), ErrorKind::BadResource);
+ assert_eq!(err.to_string(), "bad resource id");
+ }
+
+ #[test]
+ fn test_permission_denied() {
+ let err = permission_denied();
+ assert_eq!(err.kind(), ErrorKind::PermissionDenied);
+ assert_eq!(err.to_string(), "permission denied");
+ }
+
+ #[test]
+ fn test_op_not_implemented() {
+ let err = op_not_implemented();
+ assert_eq!(err.kind(), ErrorKind::OpNotAvailable);
+ assert_eq!(err.to_string(), "op not implemented");
+ }
+
+ #[test]
+ fn test_worker_init_failed() {
+ let err = worker_init_failed();
+ assert_eq!(err.kind(), ErrorKind::WorkerInitFailed);
+ assert_eq!(err.to_string(), "worker init failed");
+ }
+
+ #[test]
+ fn test_no_buffer_specified() {
+ let err = no_buffer_specified();
+ assert_eq!(err.kind(), ErrorKind::InvalidInput);
+ assert_eq!(err.to_string(), "no buffer specified");
+ }
+
+ #[test]
+ fn test_no_async_support() {
+ let err = no_async_support();
+ assert_eq!(err.kind(), ErrorKind::NoAsyncSupport);
+ assert_eq!(err.to_string(), "op doesn't support async calls");
+ }
+
+ #[test]
+ fn test_no_sync_support() {
+ let err = no_sync_support();
+ assert_eq!(err.kind(), ErrorKind::NoSyncSupport);
+ assert_eq!(err.to_string(), "op doesn't support sync calls");
+ }
+
+ #[test]
+ #[should_panic]
+ fn test_apply_source_map_invalid() {
+ let getter = MockSourceMapGetter {};
+ let err = new(ErrorKind::NotFound, "not found".to_string());
+ err.apply_source_map(&getter);
+ }
+
+ #[test]
+ #[should_panic]
+ fn test_err_check() {
+ err_check(
+ Err(new(ErrorKind::NotFound, "foo".to_string())) as Result<(), DenoError>
+ );
+ }
+}
diff --git a/cli/diagnostics.rs b/cli/diagnostics.rs
index af384f277..69b58459e 100644
--- a/cli/diagnostics.rs
+++ b/cli/diagnostics.rs
@@ -2,20 +2,13 @@
//! This module encodes TypeScript errors (diagnostics) into Rust structs and
//! contains code for printing them to the console.
use crate::ansi;
+use crate::fmt_errors::format_maybe_source_line;
+use crate::fmt_errors::format_maybe_source_name;
+use crate::fmt_errors::DisplayFormatter;
use serde_json;
use serde_json::value::Value;
use std::fmt;
-// A trait which specifies parts of a diagnostic like item needs to be able to
-// generate to conform its display to other diagnostic like items
-pub trait DisplayFormatter {
- fn format_category_and_code(&self) -> String;
- fn format_message(&self, level: usize) -> String;
- fn format_related_info(&self) -> String;
- fn format_source_line(&self, level: usize) -> String;
- fn format_source_name(&self, level: usize) -> String;
-}
-
#[derive(Debug, PartialEq, Clone)]
pub struct Diagnostic {
pub items: Vec<DiagnosticItem>,
@@ -179,23 +172,21 @@ impl DiagnosticItem {
}
}
-// TODO should chare logic with cli/js_errors, possibly with JSError
-// implementing the `DisplayFormatter` trait.
impl DisplayFormatter for DiagnosticItem {
fn format_category_and_code(&self) -> String {
let category = match self.category {
DiagnosticCategory::Error => {
- format!("- {}", ansi::red("error".to_string()))
+ format!("{}", ansi::red_bold("error".to_string()))
}
- DiagnosticCategory::Warning => "- warn".to_string(),
- DiagnosticCategory::Debug => "- debug".to_string(),
- DiagnosticCategory::Info => "- info".to_string(),
+ DiagnosticCategory::Warning => "warn".to_string(),
+ DiagnosticCategory::Debug => "debug".to_string(),
+ DiagnosticCategory::Info => "info".to_string(),
_ => "".to_string(),
};
- let code = ansi::grey(format!(" TS{}:", self.code.to_string())).to_string();
+ let code = ansi::bold(format!(" TS{}", self.code.to_string())).to_string();
- format!("{}{} ", category, code)
+ format!("{}{}: ", category, code)
}
fn format_message(&self, level: usize) -> String {
@@ -229,10 +220,10 @@ impl DisplayFormatter for DiagnosticItem {
for related_diagnostic in related_information {
let rd = &related_diagnostic;
s.push_str(&format!(
- "\n{}{}{}\n",
- rd.format_source_name(2),
+ "\n{}\n\n ► {}{}\n",
+ rd.format_message(2),
+ rd.format_source_name(),
rd.format_source_line(4),
- rd.format_message(4),
));
}
@@ -240,74 +231,24 @@ impl DisplayFormatter for DiagnosticItem {
}
fn format_source_line(&self, level: usize) -> String {
- if self.source_line.is_none() {
- return "".to_string();
- }
-
- let source_line = self.source_line.as_ref().unwrap();
- // sometimes source_line gets set with an empty string, which then outputs
- // an empty source line when displayed, so need just short circuit here
- if source_line.is_empty() {
- return "".to_string();
- }
-
- assert!(self.line_number.is_some());
- assert!(self.start_column.is_some());
- assert!(self.end_column.is_some());
- let line = (1 + self.line_number.unwrap()).to_string();
- let line_color = ansi::black_on_white(line.to_string());
- let line_len = line.clone().len();
- let line_padding =
- ansi::black_on_white(format!("{:indent$}", "", indent = line_len))
- .to_string();
- let mut s = String::new();
- let start_column = self.start_column.unwrap();
- let end_column = self.end_column.unwrap();
- // TypeScript uses `~` always, but V8 would utilise `^` always, even when
- // doing ranges, so here, if we only have one marker (very common with V8
- // errors) we will use `^` instead.
- let underline_char = if (end_column - start_column) <= 1 {
- '^'
- } else {
- '~'
- };
- for i in 0..end_column {
- if i >= start_column {
- s.push(underline_char);
- } else {
- s.push(' ');
- }
- }
- let color_underline = match self.category {
- DiagnosticCategory::Error => ansi::red(s).to_string(),
- _ => ansi::cyan(s).to_string(),
- };
-
- let indent = format!("{:indent$}", "", indent = level);
-
- format!(
- "\n\n{}{} {}\n{}{} {}\n",
- indent, line_color, source_line, indent, line_padding, color_underline
+ format_maybe_source_line(
+ self.source_line.clone(),
+ self.line_number,
+ self.start_column,
+ self.end_column,
+ match self.category {
+ DiagnosticCategory::Error => true,
+ _ => false,
+ },
+ level,
)
}
- fn format_source_name(&self, level: usize) -> String {
- if self.script_resource_name.is_none() {
- return "".to_string();
- }
-
- let script_name = ansi::cyan(self.script_resource_name.clone().unwrap());
- assert!(self.line_number.is_some());
- assert!(self.start_column.is_some());
- let line = ansi::yellow((1 + self.line_number.unwrap()).to_string());
- let column = ansi::yellow((1 + self.start_column.unwrap()).to_string());
- format!(
- "{:indent$}{}:{}:{} ",
- "",
- script_name,
- line,
- column,
- indent = level
+ fn format_source_name(&self) -> String {
+ format_maybe_source_name(
+ self.script_resource_name.clone(),
+ self.line_number,
+ self.start_column,
)
}
}
@@ -316,15 +257,13 @@ impl fmt::Display for DiagnosticItem {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
- "{}{}{}{}{}",
- self.format_source_name(0),
+ "{}{}\n\n► {}{}{}",
self.format_category_and_code(),
self.format_message(0),
+ self.format_source_name(),
self.format_source_line(0),
self.format_related_info(),
- )?;
-
- Ok(())
+ )
}
}
@@ -655,14 +594,14 @@ mod tests {
#[test]
fn diagnostic_to_string1() {
let d = diagnostic1();
- let expected = "deno/tests/complex_diagnostics.ts:19:3 - error TS2322: Type \'(o: T) => { v: any; f: (x: B) => string; }[]\' is not assignable to type \'(r: B) => Value<B>[]\'.\n Types of parameters \'o\' and \'r\' are incompatible.\n Type \'B\' is not assignable to type \'T\'.\n\n19 values: o => [\n ~~~~~~\n\n deno/tests/complex_diagnostics.ts:7:3 \n\n 7 values?: (r: T) => Array<Value<T>>;\n ~~~~~~\n The expected type comes from property \'values\' which is declared here on type \'SettingsInterface<B>\'\n";
+ let expected = "error TS2322: Type \'(o: T) => { v: any; f: (x: B) => string; }[]\' is not assignable to type \'(r: B) => Value<B>[]\'.\n Types of parameters \'o\' and \'r\' are incompatible.\n Type \'B\' is not assignable to type \'T\'.\n\n► deno/tests/complex_diagnostics.ts:19:3\n\n19 values: o => [\n ~~~~~~\n\n The expected type comes from property \'values\' which is declared here on type \'SettingsInterface<B>\'\n\n ► deno/tests/complex_diagnostics.ts:7:3\n\n 7 values?: (r: T) => Array<Value<T>>;\n ~~~~~~\n\n";
assert_eq!(expected, strip_ansi_codes(&d.to_string()));
}
#[test]
fn diagnostic_to_string2() {
let d = diagnostic2();
- let expected = "deno/tests/complex_diagnostics.ts:19:3 - error TS2322: Example 1\n\n19 values: o => [\n ~~~~~~\n\n/foo/bar.ts:129:3 - error TS2000: Example 2\n\n129 values: undefined,\n ~~~~~~\n\n\nFound 2 errors.\n";
+ let expected = "error TS2322: Example 1\n\n► deno/tests/complex_diagnostics.ts:19:3\n\n19 values: o => [\n ~~~~~~\n\nerror TS2000: Example 2\n\n► /foo/bar.ts:129:3\n\n129 values: undefined,\n ~~~~~~\n\n\nFound 2 errors.\n";
assert_eq!(expected, strip_ansi_codes(&d.to_string()));
}
}
diff --git a/cli/dispatch_minimal.rs b/cli/dispatch_minimal.rs
index e00203576..bda9ea535 100644
--- a/cli/dispatch_minimal.rs
+++ b/cli/dispatch_minimal.rs
@@ -124,27 +124,27 @@ pub fn dispatch_minimal(
}
mod ops {
- use crate::errors;
+ use crate::deno_error;
use crate::resources;
use crate::tokio_write;
use deno::PinnedBuf;
use futures::Future;
- type MinimalOp = dyn Future<Item = i32, Error = errors::DenoError> + Send;
+ type MinimalOp = dyn Future<Item = i32, Error = deno_error::DenoError> + Send;
pub fn read(rid: i32, zero_copy: Option<PinnedBuf>) -> Box<MinimalOp> {
debug!("read rid={}", rid);
let zero_copy = match zero_copy {
None => {
- return Box::new(futures::future::err(errors::no_buffer_specified()))
+ return Box::new(futures::future::err(deno_error::no_buffer_specified()))
}
Some(buf) => buf,
};
match resources::lookup(rid as u32) {
- None => Box::new(futures::future::err(errors::bad_resource())),
+ None => Box::new(futures::future::err(deno_error::bad_resource())),
Some(resource) => Box::new(
tokio::io::read(resource, zero_copy)
- .map_err(errors::DenoError::from)
+ .map_err(deno_error::DenoError::from)
.and_then(move |(_resource, _buf, nread)| Ok(nread as i32)),
),
}
@@ -154,15 +154,15 @@ mod ops {
debug!("write rid={}", rid);
let zero_copy = match zero_copy {
None => {
- return Box::new(futures::future::err(errors::no_buffer_specified()))
+ return Box::new(futures::future::err(deno_error::no_buffer_specified()))
}
Some(buf) => buf,
};
match resources::lookup(rid as u32) {
- None => Box::new(futures::future::err(errors::bad_resource())),
+ None => Box::new(futures::future::err(deno_error::bad_resource())),
Some(resource) => Box::new(
tokio_write::write(resource, zero_copy)
- .map_err(errors::DenoError::from)
+ .map_err(deno_error::DenoError::from)
.and_then(move |(_resource, _buf, nwritten)| Ok(nwritten as i32)),
),
}
diff --git a/cli/errors.rs b/cli/errors.rs
deleted file mode 100644
index 67eb54ea7..000000000
--- a/cli/errors.rs
+++ /dev/null
@@ -1,295 +0,0 @@
-// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
-use crate::import_map::ImportMapError;
-use crate::js_errors::JSErrorColor;
-pub use crate::msg::ErrorKind;
-use crate::resolve_addr::ResolveAddrError;
-use deno::JSError;
-use hyper;
-#[cfg(unix)]
-use nix::{errno::Errno, Error as UnixError};
-use std;
-use std::fmt;
-use std::io;
-use url;
-
-pub type DenoResult<T> = std::result::Result<T, DenoError>;
-
-#[derive(Debug)]
-pub struct DenoError {
- repr: Repr,
-}
-
-#[derive(Debug)]
-enum Repr {
- Simple(ErrorKind, String),
- IoErr(io::Error),
- UrlErr(url::ParseError),
- HyperErr(hyper::Error),
- ImportMapErr(ImportMapError),
-}
-
-pub fn new(kind: ErrorKind, msg: String) -> DenoError {
- DenoError {
- repr: Repr::Simple(kind, msg),
- }
-}
-
-impl DenoError {
- pub fn kind(&self) -> ErrorKind {
- match self.repr {
- Repr::Simple(kind, ref _msg) => kind,
- // Repr::Simple(kind) => kind,
- Repr::IoErr(ref err) => {
- use std::io::ErrorKind::*;
- match err.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,
- WouldBlock => ErrorKind::WouldBlock,
- InvalidInput => ErrorKind::InvalidInput,
- InvalidData => ErrorKind::InvalidData,
- TimedOut => ErrorKind::TimedOut,
- Interrupted => ErrorKind::Interrupted,
- WriteZero => ErrorKind::WriteZero,
- Other => ErrorKind::Other,
- UnexpectedEof => ErrorKind::UnexpectedEof,
- _ => unreachable!(),
- }
- }
- Repr::UrlErr(ref err) => {
- use url::ParseError::*;
- match err {
- EmptyHost => ErrorKind::EmptyHost,
- IdnaError => ErrorKind::IdnaError,
- InvalidPort => ErrorKind::InvalidPort,
- InvalidIpv4Address => ErrorKind::InvalidIpv4Address,
- InvalidIpv6Address => ErrorKind::InvalidIpv6Address,
- InvalidDomainCharacter => ErrorKind::InvalidDomainCharacter,
- RelativeUrlWithoutBase => ErrorKind::RelativeUrlWithoutBase,
- RelativeUrlWithCannotBeABaseBase => {
- ErrorKind::RelativeUrlWithCannotBeABaseBase
- }
- SetHostOnCannotBeABaseUrl => ErrorKind::SetHostOnCannotBeABaseUrl,
- Overflow => ErrorKind::Overflow,
- }
- }
- Repr::HyperErr(ref err) => {
- // For some reason hyper::errors::Kind is private.
- if err.is_parse() {
- ErrorKind::HttpParse
- } else if err.is_user() {
- ErrorKind::HttpUser
- } else if err.is_canceled() {
- ErrorKind::HttpCanceled
- } else if err.is_closed() {
- ErrorKind::HttpClosed
- } else {
- ErrorKind::HttpOther
- }
- }
- Repr::ImportMapErr(ref _err) => ErrorKind::ImportMapError,
- }
- }
-}
-
-impl fmt::Display for DenoError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self.repr {
- Repr::Simple(_kind, ref err_str) => f.pad(err_str),
- Repr::IoErr(ref err) => err.fmt(f),
- Repr::UrlErr(ref err) => err.fmt(f),
- Repr::HyperErr(ref err) => err.fmt(f),
- Repr::ImportMapErr(ref err) => f.pad(&err.msg),
- }
- }
-}
-
-impl std::error::Error for DenoError {
- fn description(&self) -> &str {
- match self.repr {
- Repr::Simple(_kind, ref msg) => msg.as_str(),
- Repr::IoErr(ref err) => err.description(),
- Repr::UrlErr(ref err) => err.description(),
- Repr::HyperErr(ref err) => err.description(),
- Repr::ImportMapErr(ref err) => &err.msg,
- }
- }
-
- fn cause(&self) -> Option<&dyn std::error::Error> {
- match self.repr {
- Repr::Simple(_kind, ref _msg) => None,
- Repr::IoErr(ref err) => Some(err),
- Repr::UrlErr(ref err) => Some(err),
- Repr::HyperErr(ref err) => Some(err),
- Repr::ImportMapErr(ref _err) => None,
- }
- }
-}
-
-impl From<io::Error> for DenoError {
- #[inline]
- fn from(err: io::Error) -> Self {
- Self {
- repr: Repr::IoErr(err),
- }
- }
-}
-
-impl From<url::ParseError> for DenoError {
- #[inline]
- fn from(err: url::ParseError) -> Self {
- Self {
- repr: Repr::UrlErr(err),
- }
- }
-}
-
-impl From<hyper::Error> for DenoError {
- #[inline]
- fn from(err: hyper::Error) -> Self {
- Self {
- repr: Repr::HyperErr(err),
- }
- }
-}
-
-impl From<ResolveAddrError> for DenoError {
- fn from(e: ResolveAddrError) -> Self {
- match e {
- ResolveAddrError::Syntax => Self {
- repr: Repr::Simple(
- ErrorKind::InvalidInput,
- "invalid address syntax".to_string(),
- ),
- },
- ResolveAddrError::Resolution(io_err) => Self {
- repr: Repr::IoErr(io_err),
- },
- }
- }
-}
-
-#[cfg(unix)]
-impl From<UnixError> for DenoError {
- fn from(e: UnixError) -> Self {
- match e {
- UnixError::Sys(Errno::EPERM) => Self {
- repr: Repr::Simple(
- ErrorKind::PermissionDenied,
- Errno::EPERM.desc().to_owned(),
- ),
- },
- UnixError::Sys(Errno::EINVAL) => Self {
- repr: Repr::Simple(
- ErrorKind::InvalidInput,
- Errno::EINVAL.desc().to_owned(),
- ),
- },
- UnixError::Sys(Errno::ENOENT) => Self {
- repr: Repr::Simple(
- ErrorKind::NotFound,
- Errno::ENOENT.desc().to_owned(),
- ),
- },
- UnixError::Sys(err) => Self {
- repr: Repr::Simple(ErrorKind::UnixError, err.desc().to_owned()),
- },
- _ => Self {
- repr: Repr::Simple(ErrorKind::Other, format!("{}", e)),
- },
- }
- }
-}
-
-impl From<ImportMapError> for DenoError {
- fn from(err: ImportMapError) -> Self {
- Self {
- repr: Repr::ImportMapErr(err),
- }
- }
-}
-
-pub fn bad_resource() -> DenoError {
- new(ErrorKind::BadResource, String::from("bad resource id"))
-}
-
-pub fn permission_denied() -> DenoError {
- new(
- ErrorKind::PermissionDenied,
- String::from("permission denied"),
- )
-}
-
-pub fn op_not_implemented() -> DenoError {
- new(ErrorKind::OpNotAvaiable, String::from("op not implemented"))
-}
-
-pub fn worker_init_failed() -> DenoError {
- // TODO(afinch7) pass worker error data through here
- new(
- ErrorKind::WorkerInitFailed,
- String::from("worker init failed"),
- )
-}
-
-pub fn no_buffer_specified() -> DenoError {
- new(ErrorKind::InvalidInput, String::from("no buffer specified"))
-}
-
-pub fn no_async_support() -> DenoError {
- new(
- ErrorKind::NoAsyncSupport,
- String::from("op doesn't support async calls"),
- )
-}
-
-pub fn no_sync_support() -> DenoError {
- new(
- ErrorKind::NoSyncSupport,
- String::from("op doesn't support sync calls"),
- )
-}
-
-#[derive(Debug)]
-pub enum RustOrJsError {
- Rust(DenoError),
- Js(JSError),
-}
-
-impl From<DenoError> for RustOrJsError {
- fn from(e: DenoError) -> Self {
- RustOrJsError::Rust(e)
- }
-}
-
-impl From<JSError> for RustOrJsError {
- fn from(e: JSError) -> Self {
- RustOrJsError::Js(e)
- }
-}
-
-impl fmt::Display for RustOrJsError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- RustOrJsError::Rust(e) => e.fmt(f),
- RustOrJsError::Js(e) => JSErrorColor(e).fmt(f),
- }
- }
-}
-
-// TODO(ry) This is ugly. They are essentially the same type.
-impl From<deno::JSErrorOr<DenoError>> for RustOrJsError {
- fn from(e: deno::JSErrorOr<DenoError>) -> Self {
- match e {
- deno::JSErrorOr::JSError(err) => RustOrJsError::Js(err),
- deno::JSErrorOr::Other(err) => RustOrJsError::Rust(err),
- }
- }
-}
diff --git a/cli/fmt_errors.rs b/cli/fmt_errors.rs
new file mode 100644
index 000000000..957e799ca
--- /dev/null
+++ b/cli/fmt_errors.rs
@@ -0,0 +1,289 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+//! This mod provides DenoError to unify errors across Deno.
+use crate::ansi;
+use deno::JSError;
+use deno::StackFrame;
+use std::fmt;
+
+/// A trait which specifies parts of a diagnostic like item needs to be able to
+/// generate to conform its display to other diagnostic like items
+pub trait DisplayFormatter {
+ fn format_category_and_code(&self) -> String;
+ fn format_message(&self, level: usize) -> String;
+ fn format_related_info(&self) -> String;
+ fn format_source_line(&self, level: usize) -> String;
+ fn format_source_name(&self) -> String;
+}
+
+fn format_source_name(script_name: String, line: i64, column: i64) -> String {
+ let script_name_c = ansi::cyan(script_name);
+ let line_c = ansi::yellow((1 + line).to_string());
+ let column_c = ansi::yellow((1 + column).to_string());
+ format!("{}:{}:{}", script_name_c, line_c, column_c,)
+}
+
+/// Formats optional source, line and column into a single string.
+pub fn format_maybe_source_name(
+ script_name: Option<String>,
+ line: Option<i64>,
+ column: Option<i64>,
+) -> String {
+ if script_name.is_none() {
+ return "".to_string();
+ }
+
+ assert!(line.is_some());
+ assert!(column.is_some());
+ format_source_name(script_name.unwrap(), line.unwrap(), column.unwrap())
+}
+
+/// Take an optional source line and associated information to format it into
+/// a pretty printed version of that line.
+pub fn format_maybe_source_line(
+ source_line: Option<String>,
+ line_number: Option<i64>,
+ start_column: Option<i64>,
+ end_column: Option<i64>,
+ is_error: bool,
+ level: usize,
+) -> String {
+ if source_line.is_none() || line_number.is_none() {
+ return "".to_string();
+ }
+
+ let source_line = source_line.as_ref().unwrap();
+ // sometimes source_line gets set with an empty string, which then outputs
+ // an empty source line when displayed, so need just short circuit here
+ if source_line.is_empty() {
+ return "".to_string();
+ }
+
+ assert!(start_column.is_some());
+ assert!(end_column.is_some());
+ let line = (1 + line_number.unwrap()).to_string();
+ let line_color = ansi::black_on_white(line.to_string());
+ let line_len = line.clone().len();
+ let line_padding =
+ ansi::black_on_white(format!("{:indent$}", "", indent = line_len))
+ .to_string();
+ let mut s = String::new();
+ let start_column = start_column.unwrap();
+ let end_column = end_column.unwrap();
+ // TypeScript uses `~` always, but V8 would utilise `^` always, even when
+ // doing ranges, so here, if we only have one marker (very common with V8
+ // errors) we will use `^` instead.
+ let underline_char = if (end_column - start_column) <= 1 {
+ '^'
+ } else {
+ '~'
+ };
+ for i in 0..end_column {
+ if i >= start_column {
+ s.push(underline_char);
+ } else {
+ s.push(' ');
+ }
+ }
+ let color_underline = if is_error {
+ ansi::red(s).to_string()
+ } else {
+ ansi::cyan(s).to_string()
+ };
+
+ let indent = format!("{:indent$}", "", indent = level);
+
+ format!(
+ "\n\n{}{} {}\n{}{} {}\n",
+ indent, line_color, source_line, indent, line_padding, color_underline
+ )
+}
+
+/// Format a message to preface with `error: ` with ansi codes for red.
+pub fn format_error_message(msg: String) -> String {
+ let preamble = ansi::red("error:".to_string());
+ format!("{} {}", preamble, msg)
+}
+
+/// Wrapper around JSError which provides color to_string.
+pub struct JSErrorColor<'a>(pub &'a JSError);
+
+impl<'a> DisplayFormatter for JSErrorColor<'a> {
+ fn format_category_and_code(&self) -> String {
+ "".to_string()
+ }
+
+ fn format_message(&self, _level: usize) -> String {
+ format!(
+ "{}{}",
+ ansi::red_bold("error: ".to_string()),
+ self.0.message.clone()
+ )
+ }
+
+ fn format_related_info(&self) -> String {
+ "".to_string()
+ }
+
+ fn format_source_line(&self, level: usize) -> String {
+ format_maybe_source_line(
+ self.0.source_line.clone(),
+ self.0.line_number,
+ self.0.start_column,
+ self.0.end_column,
+ true,
+ level,
+ )
+ }
+
+ fn format_source_name(&self) -> String {
+ let e = self.0;
+ if e.script_resource_name.is_none() {
+ return "".to_string();
+ }
+
+ format!(
+ "\n► {}",
+ format_maybe_source_name(
+ e.script_resource_name.clone(),
+ e.line_number,
+ e.start_column,
+ )
+ )
+ }
+}
+
+impl<'a> fmt::Display for JSErrorColor<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(
+ f,
+ "{}{}{}",
+ self.format_message(0),
+ self.format_source_name(),
+ self.format_source_line(0),
+ )?;
+
+ for frame in &self.0.frames {
+ write!(f, "\n{}", StackFrameColor(&frame).to_string())?;
+ }
+ Ok(())
+ }
+}
+
+struct StackFrameColor<'a>(&'a StackFrame);
+
+impl<'a> fmt::Display for StackFrameColor<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let frame = self.0;
+ // Note when we print to string, we change from 0-indexed to 1-indexed.
+ let function_name = ansi::italic_bold(frame.function_name.clone());
+ let script_line_column =
+ format_source_name(frame.script_name.clone(), frame.line, frame.column);
+
+ if !frame.function_name.is_empty() {
+ write!(f, " at {} ({})", function_name, script_line_column)
+ } else if frame.is_eval {
+ write!(f, " at eval ({})", script_line_column)
+ } else {
+ write!(f, " at {}", script_line_column)
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::ansi::strip_ansi_codes;
+
+ fn error1() -> JSError {
+ JSError {
+ message: "Error: foo bar".to_string(),
+ source_line: None,
+ script_resource_name: None,
+ line_number: None,
+ start_position: None,
+ end_position: None,
+ error_level: None,
+ start_column: None,
+ end_column: None,
+ frames: vec![
+ StackFrame {
+ line: 4,
+ column: 16,
+ script_name: "foo_bar.ts".to_string(),
+ function_name: "foo".to_string(),
+ is_eval: false,
+ is_constructor: false,
+ is_wasm: false,
+ },
+ StackFrame {
+ line: 5,
+ column: 20,
+ script_name: "bar_baz.ts".to_string(),
+ function_name: "qat".to_string(),
+ is_eval: false,
+ is_constructor: false,
+ is_wasm: false,
+ },
+ StackFrame {
+ line: 1,
+ column: 1,
+ script_name: "deno_main.js".to_string(),
+ function_name: "".to_string(),
+ is_eval: false,
+ is_constructor: false,
+ is_wasm: false,
+ },
+ ],
+ }
+ }
+
+ #[test]
+ fn js_error_to_string() {
+ let e = error1();
+ assert_eq!("error: Error: foo bar\n at foo (foo_bar.ts:5:17)\n at qat (bar_baz.ts:6:21)\n at deno_main.js:2:2", strip_ansi_codes(&JSErrorColor(&e).to_string()));
+ }
+
+ #[test]
+ fn test_format_none_source_name() {
+ let actual = format_maybe_source_name(None, None, None);
+ assert_eq!(actual, "");
+ }
+
+ #[test]
+ fn test_format_some_source_name() {
+ let actual = format_maybe_source_name(
+ Some("file://foo/bar.ts".to_string()),
+ Some(1),
+ Some(2),
+ );
+ assert_eq!(strip_ansi_codes(&actual), "file://foo/bar.ts:2:3");
+ }
+
+ #[test]
+ fn test_format_none_source_line() {
+ let actual = format_maybe_source_line(None, None, None, None, false, 0);
+ assert_eq!(actual, "");
+ }
+
+ #[test]
+ fn test_format_some_source_line() {
+ let actual = format_maybe_source_line(
+ Some("console.log('foo');".to_string()),
+ Some(8),
+ Some(8),
+ Some(11),
+ true,
+ 0,
+ );
+ assert_eq!(
+ strip_ansi_codes(&actual),
+ "\n\n9 console.log(\'foo\');\n ~~~\n"
+ );
+ }
+
+ #[test]
+ fn test_format_error_message() {
+ let actual = format_error_message("foo".to_string());
+ assert_eq!(strip_ansi_codes(&actual), "error: foo");
+ }
+}
diff --git a/cli/fs.rs b/cli/fs.rs
index 73d7701e6..3b5709804 100644
--- a/cli/fs.rs
+++ b/cli/fs.rs
@@ -15,7 +15,7 @@ use std::os::unix::fs::DirBuilderExt;
#[cfg(any(unix))]
use std::os::unix::fs::PermissionsExt;
-use crate::errors::DenoResult;
+use crate::deno_error::DenoResult;
pub fn write_file<T: AsRef<[u8]>>(
filename: &Path,
@@ -115,7 +115,7 @@ pub fn normalize_path(path: &Path) -> String {
#[cfg(unix)]
pub fn chown(path: &str, uid: u32, gid: u32) -> DenoResult<()> {
- use crate::errors::DenoError;
+ use crate::deno_error::DenoError;
let nix_uid = Uid::from_raw(uid);
let nix_gid = Gid::from_raw(gid);
unix_chown(path, Option::Some(nix_uid), Option::Some(nix_gid))
@@ -126,6 +126,6 @@ pub fn chown(path: &str, uid: u32, gid: u32) -> DenoResult<()> {
pub fn chown(_path: &str, _uid: u32, _gid: u32) -> DenoResult<()> {
// Noop
// TODO: implement chown for Windows
- use crate::errors;
- Err(errors::op_not_implemented())
+ use crate::deno_error;
+ Err(deno_error::op_not_implemented())
}
diff --git a/cli/http_util.rs b/cli/http_util.rs
index 13d1ce45b..a3310b1bf 100644
--- a/cli/http_util.rs
+++ b/cli/http_util.rs
@@ -1,6 +1,6 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
-use crate::errors;
-use crate::errors::DenoError;
+use crate::deno_error;
+use crate::deno_error::DenoError;
#[cfg(test)]
use futures::future::{loop_fn, Loop};
use futures::{future, Future, Stream};
@@ -58,7 +58,7 @@ fn resolve_uri_from_location(base_uri: &Uri, location: &str) -> Uri {
}
#[cfg(test)]
-use crate::errors::DenoResult;
+use crate::deno_error::DenoResult;
#[cfg(test)]
use crate::tokio_util;
#[cfg(test)]
@@ -108,8 +108,8 @@ pub fn fetch_string_once(
} else if response.status().is_client_error()
|| response.status().is_server_error()
{
- return Box::new(future::err(errors::new(
- errors::ErrorKind::Other,
+ return Box::new(future::err(deno_error::new(
+ deno_error::ErrorKind::Other,
format!("Import '{}' failed: {}", &url, response.status()),
)));
}
@@ -165,8 +165,8 @@ pub fn fetch_string(
return Ok(Loop::Continue((client, new_url)));
}
if !response.status().is_success() {
- return Err(errors::new(
- errors::ErrorKind::NotFound,
+ return Err(deno_error::new(
+ deno_error::ErrorKind::NotFound,
"module not found".to_string(),
));
}
diff --git a/cli/main.rs b/cli/main.rs
index ef132927e..d56b07479 100644
--- a/cli/main.rs
+++ b/cli/main.rs
@@ -17,16 +17,16 @@ extern crate rand;
mod ansi;
pub mod compiler;
pub mod deno_dir;
+pub mod deno_error;
pub mod diagnostics;
mod dispatch_minimal;
-pub mod errors;
pub mod flags;
+pub mod fmt_errors;
mod fs;
mod global_timer;
mod http_body;
mod http_util;
mod import_map;
-pub mod js_errors;
pub mod msg;
pub mod msg_util;
pub mod ops;
@@ -36,6 +36,7 @@ mod repl;
pub mod resolve_addr;
pub mod resources;
mod signal;
+pub mod source_maps;
mod startup_data;
pub mod state;
mod tokio_util;
@@ -44,7 +45,7 @@ pub mod version;
pub mod worker;
use crate::compiler::bundle_async;
-use crate::errors::RustOrJsError;
+use crate::deno_error::DenoError;
use crate::progress::Progress;
use crate::state::ThreadSafeState;
use crate::worker::Worker;
@@ -82,14 +83,14 @@ impl log::Log for Logger {
fn flush(&self) {}
}
-fn print_err_and_exit(err: RustOrJsError) {
+fn print_err_and_exit(err: DenoError) {
eprintln!("{}", err.to_string());
std::process::exit(1);
}
fn js_check<E>(r: Result<(), E>)
where
- E: Into<RustOrJsError>,
+ E: Into<DenoError>,
{
if let Err(err) = r {
print_err_and_exit(err.into());
@@ -258,9 +259,7 @@ fn xeval_command(flags: DenoFlags, argv: Vec<String>) {
.then(|result| {
js_check(result);
Ok(())
- }).map_err(|(err, _worker): (RustOrJsError, Worker)| {
- print_err_and_exit(err)
- })
+ }).map_err(|(err, _worker): (DenoError, Worker)| print_err_and_exit(err))
});
tokio_util::run(main_future);
}
@@ -295,9 +294,7 @@ fn run_repl(flags: DenoFlags, argv: Vec<String>) {
.then(|result| {
js_check(result);
Ok(())
- }).map_err(|(err, _worker): (RustOrJsError, Worker)| {
- print_err_and_exit(err)
- })
+ }).map_err(|(err, _worker): (DenoError, Worker)| print_err_and_exit(err))
});
tokio_util::run(main_future);
}
diff --git a/cli/msg.fbs b/cli/msg.fbs
index 7e8292740..d76e70d85 100644
--- a/cli/msg.fbs
+++ b/cli/msg.fbs
@@ -133,12 +133,16 @@ enum ErrorKind: byte {
// custom errors
InvalidUri,
InvalidSeekMode,
- OpNotAvaiable,
+ OpNotAvailable,
WorkerInitFailed,
UnixError,
NoAsyncSupport,
NoSyncSupport,
ImportMapError,
+
+ // other kinds
+ Diagnostic,
+ JSError,
}
table Cwd {}
diff --git a/cli/msg_util.rs b/cli/msg_util.rs
index 71bcc19d9..0ecb7f0d6 100644
--- a/cli/msg_util.rs
+++ b/cli/msg_util.rs
@@ -1,7 +1,7 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
// Helpers for serialization.
-use crate::errors;
-use crate::errors::DenoResult;
+use crate::deno_error;
+use crate::deno_error::DenoResult;
use crate::msg;
use flatbuffers;
@@ -104,7 +104,7 @@ pub fn deserialize_request(
let u = header_msg.url().unwrap();
let u = Uri::from_str(u)
- .map_err(|e| errors::new(msg::ErrorKind::InvalidUri, e.to_string()))?;
+ .map_err(|e| deno_error::new(msg::ErrorKind::InvalidUri, e.to_string()))?;
*r.uri_mut() = u;
if let Some(method) = header_msg.method() {
diff --git a/cli/ops.rs b/cli/ops.rs
index a9279f070..d4add61e0 100644
--- a/cli/ops.rs
+++ b/cli/ops.rs
@@ -2,14 +2,15 @@
use atty;
use crate::ansi;
use crate::deno_dir::resolve_path;
+use crate::deno_error;
+use crate::deno_error::err_check;
+use crate::deno_error::DenoError;
+use crate::deno_error::DenoResult;
+use crate::deno_error::ErrorKind;
use crate::dispatch_minimal::dispatch_minimal;
use crate::dispatch_minimal::parse_min_record;
-use crate::errors;
-use crate::errors::{DenoError, DenoResult, ErrorKind};
use crate::fs as deno_fs;
use crate::http_util;
-use crate::js_errors::apply_source_map;
-use crate::js_errors::JSErrorColor;
use crate::msg;
use crate::msg_util;
use crate::rand;
@@ -25,7 +26,6 @@ use crate::tokio_util;
use crate::tokio_write;
use crate::version;
use crate::worker::Worker;
-use deno::js_check;
use deno::Buf;
use deno::CoreOp;
use deno::JSError;
@@ -401,11 +401,11 @@ fn op_format_error(
let orig_error = String::from(inner.error().unwrap());
let js_error = JSError::from_v8_exception(&orig_error).unwrap();
- let js_error_mapped = apply_source_map(&js_error, &state.dir);
- let js_error_string = JSErrorColor(&js_error_mapped).to_string();
+ let error_mapped = DenoError::from(js_error).apply_source_map(&state.dir);
+ let error_string = error_mapped.to_string();
let mut builder = FlatBufferBuilder::new();
- let new_error = builder.create_string(&js_error_string);
+ let new_error = builder.create_string(&error_string);
let inner = msg::FormatErrorRes::create(
&mut builder,
@@ -496,7 +496,7 @@ fn op_fetch_module_meta_data(
data: Option<PinnedBuf>,
) -> CliOpResult {
if !base.sync() {
- return Err(errors::no_async_support());
+ return Err(deno_error::no_async_support());
}
assert!(data.is_none());
let inner = base.inner_as_fetch_module_meta_data().unwrap();
@@ -563,7 +563,7 @@ fn op_global_timer_stop(
data: Option<PinnedBuf>,
) -> CliOpResult {
if !base.sync() {
- return Err(errors::no_async_support());
+ return Err(deno_error::no_async_support());
}
assert!(data.is_none());
let state = state;
@@ -578,7 +578,7 @@ fn op_global_timer(
data: Option<PinnedBuf>,
) -> CliOpResult {
if base.sync() {
- return Err(errors::no_sync_support());
+ return Err(deno_error::no_sync_support());
}
assert!(data.is_none());
let cmd_id = base.cmd_id();
@@ -1002,7 +1002,7 @@ fn op_close(
let inner = base.inner_as_close().unwrap();
let rid = inner.rid();
match resources::lookup(rid) {
- None => Err(errors::bad_resource()),
+ None => Err(deno_error::bad_resource()),
Some(resource) => {
resource.close();
ok_buf(empty_buf())
@@ -1033,7 +1033,7 @@ fn op_shutdown(
let rid = inner.rid();
let how = inner.how();
match resources::lookup(rid) {
- None => Err(errors::bad_resource()),
+ None => Err(deno_error::bad_resource()),
Some(mut resource) => {
let shutdown_mode = match how {
0 => Shutdown::Read,
@@ -1059,7 +1059,7 @@ fn op_read(
let rid = inner.rid();
match resources::lookup(rid) {
- None => Err(errors::bad_resource()),
+ None => Err(deno_error::bad_resource()),
Some(resource) => {
let op = tokio::io::read(resource, data.unwrap())
.map_err(DenoError::from)
@@ -1102,7 +1102,7 @@ fn op_write(
let rid = inner.rid();
match resources::lookup(rid) {
- None => Err(errors::bad_resource()),
+ None => Err(deno_error::bad_resource()),
Some(resource) => {
let op = tokio_write::write(resource, data.unwrap())
.map_err(DenoError::from)
@@ -1146,7 +1146,7 @@ fn op_seek(
let whence = inner.whence();
match resources::lookup(rid) {
- None => Err(errors::bad_resource()),
+ None => Err(deno_error::bad_resource()),
Some(resource) => {
let op = resources::seek(resource, offset, whence)
.and_then(move |_| Ok(empty_buf()));
@@ -1205,7 +1205,7 @@ fn op_copy_file(
// See https://github.com/rust-lang/rust/issues/54800
// Once the issue is reolved, we should remove this workaround.
if cfg!(unix) && !from.is_file() {
- return Err(errors::new(
+ return Err(deno_error::new(
ErrorKind::NotFound,
"File not found".to_string(),
));
@@ -1417,7 +1417,10 @@ fn op_symlink(
state.check_write(&newname_)?;
// TODO Use type for Windows.
if cfg!(windows) {
- return Err(errors::new(ErrorKind::Other, "Not implemented".to_string()));
+ return Err(deno_error::new(
+ ErrorKind::Other,
+ "Not implemented".to_string(),
+ ));
}
blocking(base.sync(), move || {
debug!("op_symlink {} {}", oldname.display(), newname.display());
@@ -1638,7 +1641,7 @@ fn op_accept(
let server_rid = inner.rid();
match resources::lookup(server_rid) {
- None => Err(errors::bad_resource()),
+ None => Err(deno_error::bad_resource()),
Some(server_resource) => {
let op = tokio_util::accept(server_resource)
.map_err(DenoError::from)
@@ -1767,7 +1770,7 @@ fn op_run(
data: Option<PinnedBuf>,
) -> CliOpResult {
if !base.sync() {
- return Err(errors::no_async_support());
+ return Err(deno_error::no_async_support());
}
let cmd_id = base.cmd_id();
@@ -1906,7 +1909,7 @@ fn op_worker_get_message(
data: Option<PinnedBuf>,
) -> CliOpResult {
if base.sync() {
- return Err(errors::no_sync_support());
+ return Err(deno_error::no_sync_support());
}
assert!(data.is_none());
let cmd_id = base.cmd_id();
@@ -1952,7 +1955,7 @@ fn op_worker_post_message(
};
tx.send(d)
.wait()
- .map_err(|e| errors::new(ErrorKind::Other, e.to_string()))?;
+ .map_err(|e| deno_error::new(ErrorKind::Other, e.to_string()))?;
let builder = &mut FlatBufferBuilder::new();
ok_buf(serialize_response(
@@ -1988,34 +1991,32 @@ fn op_create_worker(
let mut worker =
Worker::new(name, startup_data::deno_isolate_init(), child_state);
- js_check(worker.execute("denoMain()"));
- js_check(worker.execute("workerMain()"));
+ err_check(worker.execute("denoMain()"));
+ err_check(worker.execute("workerMain()"));
let module_specifier = ModuleSpecifier::resolve_root(specifier)?;
- let op = worker
- .execute_mod_async(&module_specifier, false)
- .and_then(move |()| {
- let mut workers_tl = parent_state.workers.lock().unwrap();
- workers_tl.insert(rid, worker.shared());
- let builder = &mut FlatBufferBuilder::new();
- let msg_inner = msg::CreateWorkerRes::create(
- builder,
- &msg::CreateWorkerResArgs { rid },
- );
- Ok(serialize_response(
- cmd_id,
- builder,
- msg::BaseArgs {
- inner: Some(msg_inner.as_union_value()),
- inner_type: msg::Any::CreateWorkerRes,
- ..Default::default()
- },
- ))
- }).map_err(|err| match err {
- errors::RustOrJsError::Js(_) => errors::worker_init_failed(),
- errors::RustOrJsError::Rust(err) => err,
- });
+ let op =
+ worker
+ .execute_mod_async(&module_specifier, false)
+ .and_then(move |()| {
+ let mut workers_tl = parent_state.workers.lock().unwrap();
+ workers_tl.insert(rid, worker.shared());
+ let builder = &mut FlatBufferBuilder::new();
+ let msg_inner = msg::CreateWorkerRes::create(
+ builder,
+ &msg::CreateWorkerResArgs { rid },
+ );
+ Ok(serialize_response(
+ cmd_id,
+ builder,
+ msg::BaseArgs {
+ inner: Some(msg_inner.as_union_value()),
+ inner_type: msg::Any::CreateWorkerRes,
+ ..Default::default()
+ },
+ ))
+ });
let result = op.wait()?;
Ok(Op::Sync(result))
@@ -2028,7 +2029,7 @@ fn op_host_get_worker_closed(
data: Option<PinnedBuf>,
) -> CliOpResult {
if base.sync() {
- return Err(errors::no_sync_support());
+ return Err(deno_error::no_sync_support());
}
assert!(data.is_none());
let cmd_id = base.cmd_id();
@@ -2063,7 +2064,7 @@ fn op_host_get_message(
data: Option<PinnedBuf>,
) -> CliOpResult {
if base.sync() {
- return Err(errors::no_sync_support());
+ return Err(deno_error::no_sync_support());
}
assert!(data.is_none());
let cmd_id = base.cmd_id();
@@ -2107,7 +2108,7 @@ fn op_host_post_message(
resources::post_message_to_worker(rid, d)
.wait()
- .map_err(|e| errors::new(ErrorKind::Other, e.to_string()))?;
+ .map_err(|e| deno_error::new(ErrorKind::Other, e.to_string()))?;
let builder = &mut FlatBufferBuilder::new();
ok_buf(serialize_response(
diff --git a/cli/permissions.rs b/cli/permissions.rs
index e2e3e22a4..222016532 100644
--- a/cli/permissions.rs
+++ b/cli/permissions.rs
@@ -4,8 +4,8 @@ use atty;
use crate::flags::DenoFlags;
use ansi_term::Style;
-use crate::errors::permission_denied;
-use crate::errors::DenoResult;
+use crate::deno_error::permission_denied;
+use crate::deno_error::DenoResult;
use std::collections::HashSet;
use std::fmt;
use std::io;
diff --git a/cli/repl.rs b/cli/repl.rs
index 86fa92e08..e0d029439 100644
--- a/cli/repl.rs
+++ b/cli/repl.rs
@@ -5,8 +5,8 @@ use crate::msg::ErrorKind;
use std::error::Error;
use crate::deno_dir::DenoDir;
-use crate::errors::new as deno_error;
-use crate::errors::DenoResult;
+use crate::deno_error::new as deno_error;
+use crate::deno_error::DenoResult;
use std::path::PathBuf;
#[cfg(not(windows))]
diff --git a/cli/resources.rs b/cli/resources.rs
index a6cee811f..e98327b63 100644
--- a/cli/resources.rs
+++ b/cli/resources.rs
@@ -8,10 +8,10 @@
// descriptors". This module implements a global resource table. Ops (AKA
// handlers) look up resources by their integer id here.
-use crate::errors;
-use crate::errors::bad_resource;
-use crate::errors::DenoError;
-use crate::errors::DenoResult;
+use crate::deno_error;
+use crate::deno_error::bad_resource;
+use crate::deno_error::DenoError;
+use crate::deno_error::DenoResult;
use crate::http_body::HttpBody;
use crate::repl::Repl;
use crate::state::WorkerChannels;
@@ -372,10 +372,9 @@ impl Future for WorkerReceiver {
let mut table = RESOURCE_TABLE.lock().unwrap();
let maybe_repr = table.get_mut(&self.rid);
match maybe_repr {
- Some(Repr::Worker(ref mut wc)) => wc
- .1
- .poll()
- .map_err(|err| errors::new(errors::ErrorKind::Other, err.to_string())),
+ Some(Repr::Worker(ref mut wc)) => wc.1.poll().map_err(|err| {
+ deno_error::new(deno_error::ErrorKind::Other, err.to_string())
+ }),
_ => Err(bad_resource()),
}
}
@@ -398,10 +397,9 @@ impl Stream for WorkerReceiverStream {
let mut table = RESOURCE_TABLE.lock().unwrap();
let maybe_repr = table.get_mut(&self.rid);
match maybe_repr {
- Some(Repr::Worker(ref mut wc)) => wc
- .1
- .poll()
- .map_err(|err| errors::new(errors::ErrorKind::Other, err.to_string())),
+ Some(Repr::Worker(ref mut wc)) => wc.1.poll().map_err(|err| {
+ deno_error::new(deno_error::ErrorKind::Other, err.to_string())
+ }),
_ => Err(bad_resource()),
}
}
@@ -535,8 +533,8 @@ pub fn seek(
1 => SeekFrom::Current(i64::from(offset)),
2 => SeekFrom::End(i64::from(offset)),
_ => {
- return Box::new(futures::future::err(errors::new(
- errors::ErrorKind::InvalidSeekMode,
+ return Box::new(futures::future::err(deno_error::new(
+ deno_error::ErrorKind::InvalidSeekMode,
format!("Invalid seek mode: {}", whence),
)));
}
diff --git a/cli/signal.rs b/cli/signal.rs
index 7d67ba743..bedb43ad2 100644
--- a/cli/signal.rs
+++ b/cli/signal.rs
@@ -3,11 +3,11 @@ use nix::sys::signal::{kill as unix_kill, Signal};
#[cfg(unix)]
use nix::unistd::Pid;
-use crate::errors::DenoResult;
+use crate::deno_error::DenoResult;
#[cfg(unix)]
pub fn kill(pid: i32, signo: i32) -> DenoResult<()> {
- use crate::errors::DenoError;
+ use crate::deno_error::DenoError;
let sig = Signal::from_c_int(signo)?;
unix_kill(Pid::from_raw(pid), Option::Some(sig)).map_err(DenoError::from)
}
diff --git a/cli/js_errors.rs b/cli/source_maps.rs
index ae49ed636..11c3a3532 100644
--- a/cli/js_errors.rs
+++ b/cli/source_maps.rs
@@ -1,23 +1,18 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
-//! This mod adds source maps and ANSI color display to deno::JSError.
-use crate::ansi;
+//! This mod provides functions to remap a deno::JSError based on a source map
use deno::JSError;
use deno::StackFrame;
+use serde_json;
use source_map_mappings::parse_mappings;
use source_map_mappings::Bias;
use source_map_mappings::Mappings;
use std::collections::HashMap;
-use std::fmt;
use std::str;
-/// Wrapper around JSError which provides color to_string.
-pub struct JSErrorColor<'a>(pub &'a JSError);
-
-struct StackFrameColor<'a>(&'a StackFrame);
-
pub trait SourceMapGetter {
/// Returns the raw source map file.
fn get_source_map(&self, script_name: &str) -> Option<Vec<u8>>;
+ fn get_source_line(&self, script_name: &str, line: usize) -> Option<String>;
}
/// Cached filename lookups. The key can be None if a previous lookup failed to
@@ -29,80 +24,9 @@ struct SourceMap {
sources: Vec<String>,
}
-impl<'a> fmt::Display for StackFrameColor<'a> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- let frame = self.0;
- // Note when we print to string, we change from 0-indexed to 1-indexed.
- let function_name = ansi::italic_bold(frame.function_name.clone());
- let script_line_column =
- format_script_line_column(&frame.script_name, frame.line, frame.column);
-
- if !frame.function_name.is_empty() {
- write!(f, " at {} ({})", function_name, script_line_column)
- } else if frame.is_eval {
- write!(f, " at eval ({})", script_line_column)
- } else {
- write!(f, " at {}", script_line_column)
- }
- }
-}
-
-fn format_script_line_column(
- script_name: &str,
- line: i64,
- column: i64,
-) -> String {
- // TODO match this style with how typescript displays errors.
- let line = ansi::yellow((1 + line).to_string());
- let column = ansi::yellow((1 + column).to_string());
- let script_name = ansi::cyan(script_name.to_string());
- format!("{}:{}:{}", script_name, line, column)
-}
-
-impl<'a> fmt::Display for JSErrorColor<'a> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- let e = self.0;
- if e.script_resource_name.is_some() {
- let script_resource_name = e.script_resource_name.as_ref().unwrap();
- // Avoid showing internal code from gen/cli/bundle/main.js
- if script_resource_name != "gen/cli/bundle/main.js"
- && script_resource_name != "gen/cli/bundle/compiler.js"
- {
- if e.line_number.is_some() && e.start_column.is_some() {
- assert!(e.line_number.is_some());
- assert!(e.start_column.is_some());
- let script_line_column = format_script_line_column(
- script_resource_name,
- e.line_number.unwrap() - 1,
- e.start_column.unwrap() - 1,
- );
- write!(f, "{}", script_line_column)?;
- }
- if e.source_line.is_some() {
- write!(f, "\n{}\n", e.source_line.as_ref().unwrap())?;
- let mut s = String::new();
- for i in 0..e.end_column.unwrap() {
- if i >= e.start_column.unwrap() {
- s.push('^');
- } else {
- s.push(' ');
- }
- }
- writeln!(f, "{}", ansi::red_bold(s))?;
- }
- }
- }
-
- write!(f, "{}", ansi::bold(e.message.clone()))?;
-
- for frame in &e.frames {
- write!(f, "\n{}", StackFrameColor(&frame).to_string())?;
- }
- Ok(())
- }
-}
-
impl SourceMap {
+ /// Take a JSON string and attempt to decode it, returning an optional
+ /// instance of `SourceMap`.
fn from_json(json_str: &str) -> Option<Self> {
// Ugly. Maybe use serde_derive.
match serde_json::from_str::<serde_json::Value>(json_str) {
@@ -137,110 +61,178 @@ impl SourceMap {
}
}
-fn frame_apply_source_map<G: SourceMapGetter>(
- frame: &StackFrame,
- mappings_map: &mut CachedMaps,
- getter: &G,
-) -> StackFrame {
- let maybe_sm = get_mappings(frame.script_name.as_ref(), mappings_map, getter);
- let frame_pos = (
- frame.script_name.to_owned(),
- frame.line as i64,
- frame.column as i64,
- );
- let (script_name, line, column) = match maybe_sm {
- None => frame_pos,
- Some(sm) => match sm.mappings.original_location_for(
- frame.line as u32,
- frame.column as u32,
- Bias::default(),
- ) {
- None => frame_pos,
- Some(mapping) => match &mapping.original {
- None => frame_pos,
- Some(original) => {
- let orig_source = sm.sources[original.source as usize].clone();
- (
- orig_source,
- i64::from(original.original_line),
- i64::from(original.original_column),
- )
- }
- },
- },
- };
+// The bundle does not get built for 'cargo check', so we don't embed the
+// bundle source map. The built in source map is the source map for the main
+// JavaScript bundle which is then used to create the snapshot. Runtime stack
+// traces can contain positions within the bundle which we will map to the
+// original Deno TypeScript code.
+#[cfg(feature = "check-only")]
+fn builtin_source_map(_: &str) -> Option<Vec<u8>> {
+ None
+}
- StackFrame {
- script_name,
- function_name: frame.function_name.clone(),
- line,
- column,
- is_eval: frame.is_eval,
- is_constructor: frame.is_constructor,
- is_wasm: frame.is_wasm,
+#[cfg(not(feature = "check-only"))]
+fn builtin_source_map(script_name: &str) -> Option<Vec<u8>> {
+ match script_name {
+ "gen/cli/bundle/main.js" => Some(
+ include_bytes!(concat!(
+ env!("GN_OUT_DIR"),
+ "/gen/cli/bundle/main.js.map"
+ )).to_vec(),
+ ),
+ "gen/cli/bundle/compiler.js" => Some(
+ include_bytes!(concat!(
+ env!("GN_OUT_DIR"),
+ "/gen/cli/bundle/compiler.js.map"
+ )).to_vec(),
+ ),
+ _ => None,
}
}
+/// Apply a source map to a JSError, returning a JSError where the filenames,
+/// the lines and the columns point to their original source location, not their
+/// transpiled location if applicable.
pub fn apply_source_map<G: SourceMapGetter>(
js_error: &JSError,
getter: &G,
) -> JSError {
let mut mappings_map: CachedMaps = HashMap::new();
+
let mut frames = Vec::<StackFrame>::new();
for frame in &js_error.frames {
let f = frame_apply_source_map(&frame, &mut mappings_map, getter);
frames.push(f);
}
+
+ let (script_resource_name, line_number, start_column) =
+ get_maybe_orig_position(
+ js_error.script_resource_name.clone(),
+ js_error.line_number,
+ js_error.start_column,
+ &mut mappings_map,
+ getter,
+ );
+ // It is better to just move end_column to be the same distance away from
+ // start column because sometimes the code point is not available in the
+ // source file map.
+ let end_column = match js_error.end_column {
+ Some(ec) => {
+ if start_column.is_some() {
+ Some(ec - (js_error.start_column.unwrap() - start_column.unwrap()))
+ } else {
+ None
+ }
+ }
+ _ => None,
+ };
+ // if there is a source line that we might be different in the source file, we
+ // will go fetch it from the getter
+ let source_line = if js_error.source_line.is_some()
+ && script_resource_name.is_some()
+ && line_number.is_some()
+ {
+ getter.get_source_line(
+ &js_error.script_resource_name.clone().unwrap(),
+ line_number.unwrap() as usize,
+ )
+ } else {
+ js_error.source_line.clone()
+ };
+
JSError {
message: js_error.message.clone(),
frames,
error_level: js_error.error_level,
- source_line: js_error.source_line.clone(),
- // TODO the following need to be source mapped:
- script_resource_name: js_error.script_resource_name.clone(),
- line_number: js_error.line_number,
+ source_line,
+ script_resource_name,
+ line_number,
+ start_column,
+ end_column,
+ // These are difficult to map to their original position and they are not
+ // currently used in any output, so we don't remap them.
start_position: js_error.start_position,
end_position: js_error.end_position,
- start_column: js_error.start_column,
- end_column: js_error.end_column,
}
}
-// The bundle does not get built for 'cargo check', so we don't embed the
-// bundle source map.
-#[cfg(feature = "check-only")]
-fn builtin_source_map(_: &str) -> Option<Vec<u8>> {
- None
+fn frame_apply_source_map<G: SourceMapGetter>(
+ frame: &StackFrame,
+ mappings_map: &mut CachedMaps,
+ getter: &G,
+) -> StackFrame {
+ let (script_name, line, column) = get_orig_position(
+ frame.script_name.to_string(),
+ frame.line,
+ frame.column,
+ mappings_map,
+ getter,
+ );
+
+ StackFrame {
+ script_name,
+ function_name: frame.function_name.clone(),
+ line,
+ column,
+ is_eval: frame.is_eval,
+ is_constructor: frame.is_constructor,
+ is_wasm: frame.is_wasm,
+ }
}
-#[cfg(not(feature = "check-only"))]
-fn builtin_source_map(script_name: &str) -> Option<Vec<u8>> {
- match script_name {
- "gen/cli/bundle/main.js" => Some(
- include_bytes!(concat!(
- env!("GN_OUT_DIR"),
- "/gen/cli/bundle/main.js.map"
- )).to_vec(),
- ),
- "gen/cli/bundle/compiler.js" => Some(
- include_bytes!(concat!(
- env!("GN_OUT_DIR"),
- "/gen/cli/bundle/compiler.js.map"
- )).to_vec(),
- ),
- _ => None,
+fn get_maybe_orig_position<G: SourceMapGetter>(
+ script_name: Option<String>,
+ line: Option<i64>,
+ column: Option<i64>,
+ mappings_map: &mut CachedMaps,
+ getter: &G,
+) -> (Option<String>, Option<i64>, Option<i64>) {
+ match (script_name, line, column) {
+ (Some(script_name_v), Some(line_v), Some(column_v)) => {
+ let (script_name, line, column) = get_orig_position(
+ script_name_v,
+ line_v - 1,
+ column_v,
+ mappings_map,
+ getter,
+ );
+ (Some(script_name), Some(line), Some(column))
+ }
+ _ => (None, None, None),
}
}
-fn parse_map_string<G: SourceMapGetter>(
- script_name: &str,
+fn get_orig_position<G: SourceMapGetter>(
+ script_name: String,
+ line: i64,
+ column: i64,
+ mappings_map: &mut CachedMaps,
getter: &G,
-) -> Option<SourceMap> {
- builtin_source_map(script_name)
- .or_else(|| getter.get_source_map(script_name))
- .and_then(|raw_source_map| {
- SourceMap::from_json(str::from_utf8(&raw_source_map).unwrap())
- })
+) -> (String, i64, i64) {
+ let maybe_sm = get_mappings(&script_name, mappings_map, getter);
+ let default_pos = (script_name, line, column);
+
+ match maybe_sm {
+ None => default_pos,
+ Some(sm) => match sm.mappings.original_location_for(
+ line as u32,
+ column as u32,
+ Bias::default(),
+ ) {
+ None => default_pos,
+ Some(mapping) => match &mapping.original {
+ None => default_pos,
+ Some(original) => {
+ let orig_source = sm.sources[original.source as usize].clone();
+ (
+ orig_source,
+ i64::from(original.original_line),
+ i64::from(original.original_column),
+ )
+ }
+ },
+ },
+ }
}
fn get_mappings<'a, G: SourceMapGetter>(
@@ -253,10 +245,57 @@ fn get_mappings<'a, G: SourceMapGetter>(
.or_insert_with(|| parse_map_string(script_name, getter))
}
+// TODO(kitsonk) parsed source maps should probably be cached in state in
+// the module meta data.
+fn parse_map_string<G: SourceMapGetter>(
+ script_name: &str,
+ getter: &G,
+) -> Option<SourceMap> {
+ builtin_source_map(script_name)
+ .or_else(|| getter.get_source_map(script_name))
+ .and_then(|raw_source_map| {
+ SourceMap::from_json(str::from_utf8(&raw_source_map).unwrap())
+ })
+}
+
#[cfg(test)]
mod tests {
use super::*;
- use crate::ansi::strip_ansi_codes;
+
+ struct MockSourceMapGetter {}
+
+ impl SourceMapGetter for MockSourceMapGetter {
+ fn get_source_map(&self, script_name: &str) -> Option<Vec<u8>> {
+ let s = match script_name {
+ "foo_bar.ts" => r#"{"sources": ["foo_bar.ts"], "mappings":";;;IAIA,OAAO,CAAC,GAAG,CAAC,qBAAqB,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC;IAC/C,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACjD,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAE3C,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC"}"#,
+ "bar_baz.ts" => r#"{"sources": ["bar_baz.ts"], "mappings":";;;IAEA,CAAC,KAAK,IAAI,EAAE;QACV,MAAM,GAAG,GAAG,sDAAa,OAAO,2BAAC,CAAC;QAClC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC,CAAC,EAAE,CAAC;IAEQ,QAAA,GAAG,GAAG,KAAK,CAAC;IAEzB,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC"}"#,
+ _ => return None,
+ };
+ Some(s.as_bytes().to_owned())
+ }
+
+ fn get_source_line(
+ &self,
+ script_name: &str,
+ line: usize,
+ ) -> Option<String> {
+ let s = match script_name {
+ "foo_bar.ts" => vec![
+ "console.log('foo');",
+ "console.log('foo');",
+ "console.log('foo');",
+ "console.log('foo');",
+ "console.log('foo');",
+ ],
+ _ => return None,
+ };
+ if s.len() > line {
+ Some(s[line].to_string())
+ } else {
+ None
+ }
+ }
+ }
fn error1() -> JSError {
JSError {
@@ -301,25 +340,6 @@ mod tests {
}
}
- struct MockSourceMapGetter {}
-
- impl SourceMapGetter for MockSourceMapGetter {
- fn get_source_map(&self, script_name: &str) -> Option<Vec<u8>> {
- let s = match script_name {
- "foo_bar.ts" => r#"{"sources": ["foo_bar.ts"], "mappings":";;;IAIA,OAAO,CAAC,GAAG,CAAC,qBAAqB,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC;IAC/C,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACjD,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAE3C,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC"}"#,
- "bar_baz.ts" => r#"{"sources": ["bar_baz.ts"], "mappings":";;;IAEA,CAAC,KAAK,IAAI,EAAE;QACV,MAAM,GAAG,GAAG,sDAAa,OAAO,2BAAC,CAAC;QAClC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC,CAAC,EAAE,CAAC;IAEQ,QAAA,GAAG,GAAG,KAAK,CAAC;IAEzB,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC"}"#,
- _ => return None,
- };
- Some(s.as_bytes().to_owned())
- }
- }
-
- #[test]
- fn js_error_to_string() {
- let e = error1();
- assert_eq!("Error: foo bar\n at foo (foo_bar.ts:5:17)\n at qat (bar_baz.ts:6:21)\n at deno_main.js:2:2", strip_ansi_codes(&e.to_string()));
- }
-
#[test]
fn js_error_apply_source_map_1() {
let e = error1();
@@ -399,6 +419,25 @@ mod tests {
}
#[test]
+ fn js_error_apply_source_map_line() {
+ let e = JSError {
+ message: "TypeError: baz".to_string(),
+ source_line: Some("foo".to_string()),
+ script_resource_name: Some("foo_bar.ts".to_string()),
+ line_number: Some(4),
+ start_position: None,
+ end_position: None,
+ error_level: None,
+ start_column: Some(16),
+ end_column: None,
+ frames: vec![],
+ };
+ let getter = MockSourceMapGetter {};
+ let actual = apply_source_map(&e, &getter);
+ assert_eq!(actual.source_line, Some("console.log('foo');".to_string()));
+ }
+
+ #[test]
fn source_map_from_json() {
let json = r#"{"version":3,"file":"error_001.js","sourceRoot":"","sources":["file:///Users/rld/src/deno/tests/error_001.ts"],"names":[],"mappings":"AAAA,SAAS,GAAG;IACV,MAAM,KAAK,CAAC,KAAK,CAAC,CAAC;AACrB,CAAC;AAED,SAAS,GAAG;IACV,GAAG,EAAE,CAAC;AACR,CAAC;AAED,GAAG,EAAE,CAAC"}"#;
let sm = SourceMap::from_json(json).unwrap();
diff --git a/cli/state.rs b/cli/state.rs
index aa4690d44..69a2bf74d 100644
--- a/cli/state.rs
+++ b/cli/state.rs
@@ -2,8 +2,8 @@
use crate::compiler::compile_async;
use crate::compiler::ModuleMetaData;
use crate::deno_dir;
-use crate::errors::DenoError;
-use crate::errors::DenoResult;
+use crate::deno_error::DenoError;
+use crate::deno_error::DenoResult;
use crate::flags;
use crate::global_timer::GlobalTimer;
use crate::import_map::ImportMap;
diff --git a/cli/worker.rs b/cli/worker.rs
index 9170a293f..65da666e8 100644
--- a/cli/worker.rs
+++ b/cli/worker.rs
@@ -1,11 +1,8 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
-use crate::errors::DenoError;
-use crate::errors::RustOrJsError;
-use crate::js_errors;
+use crate::deno_error::DenoError;
use crate::state::ThreadSafeState;
use crate::tokio_util;
use deno;
-use deno::JSError;
use deno::ModuleSpecifier;
use deno::StartupData;
use futures::Async;
@@ -39,7 +36,7 @@ impl Worker {
}
/// Same as execute2() but the filename defaults to "<anonymous>".
- pub fn execute(&mut self, js_source: &str) -> Result<(), JSError> {
+ pub fn execute(&mut self, js_source: &str) -> Result<(), DenoError> {
self.execute2("<anonymous>", js_source)
}
@@ -49,9 +46,12 @@ impl Worker {
&mut self,
js_filename: &str,
js_source: &str,
- ) -> Result<(), JSError> {
+ ) -> Result<(), DenoError> {
let mut isolate = self.isolate.lock().unwrap();
- isolate.execute(js_filename, js_source)
+ match isolate.execute(js_filename, js_source) {
+ Ok(_) => Ok(()),
+ Err(err) => Err(DenoError::from(err)),
+ }
}
/// Executes the provided JavaScript module.
@@ -59,7 +59,7 @@ impl Worker {
&mut self,
module_specifier: &ModuleSpecifier,
is_prefetch: bool,
- ) -> impl Future<Item = (), Error = RustOrJsError> {
+ ) -> impl Future<Item = (), Error = DenoError> {
let worker = self.clone();
let worker_ = worker.clone();
let loader = self.state.clone();
@@ -87,12 +87,12 @@ impl Worker {
}
}).map_err(move |err| {
worker_.state.progress.done();
- // Convert to RustOrJsError AND apply_source_map.
+ // Convert to DenoError AND apply_source_map.
match err {
deno::JSErrorOr::JSError(err) => {
- RustOrJsError::Js(worker_.apply_source_map(err))
+ worker_.apply_source_map(DenoError::from(err))
}
- deno::JSErrorOr::Other(err) => RustOrJsError::Rust(err),
+ deno::JSErrorOr::Other(err) => err,
}
})
}
@@ -102,29 +102,32 @@ impl Worker {
&mut self,
module_specifier: &ModuleSpecifier,
is_prefetch: bool,
- ) -> Result<(), RustOrJsError> {
+ ) -> Result<(), DenoError> {
tokio_util::block_on(self.execute_mod_async(module_specifier, is_prefetch))
}
/// Applies source map to the error.
- fn apply_source_map(&self, err: JSError) -> JSError {
- js_errors::apply_source_map(&err, &self.state.dir)
+ fn apply_source_map(&self, err: DenoError) -> DenoError {
+ err.apply_source_map(&self.state.dir)
}
}
impl Future for Worker {
type Item = ();
- type Error = JSError;
+ type Error = DenoError;
fn poll(&mut self) -> Result<Async<()>, Self::Error> {
let mut isolate = self.isolate.lock().unwrap();
- isolate.poll().map_err(|err| self.apply_source_map(err))
+ isolate
+ .poll()
+ .map_err(|err| self.apply_source_map(DenoError::from(err)))
}
}
#[cfg(test)]
mod tests {
use super::*;
+ use crate::deno_error::err_check;
use crate::flags;
use crate::ops::op_selector_std;
use crate::progress::Progress;
@@ -132,7 +135,6 @@ mod tests {
use crate::startup_data;
use crate::state::ThreadSafeState;
use crate::tokio_util;
- use deno::js_check;
use futures::future::lazy;
use std::sync::atomic::Ordering;
@@ -208,7 +210,7 @@ mod tests {
startup_data::deno_isolate_init(),
state,
);
- js_check(worker.execute("denoMain()"));
+ err_check(worker.execute("denoMain()"));
let result = worker.execute_mod(&module_specifier, false);
if let Err(err) = result {
eprintln!("execute_mod err {:?}", err);
@@ -229,8 +231,8 @@ mod tests {
]);
let mut worker =
Worker::new("TEST".to_string(), startup_data::deno_isolate_init(), state);
- js_check(worker.execute("denoMain()"));
- js_check(worker.execute("workerMain()"));
+ err_check(worker.execute("denoMain()"));
+ err_check(worker.execute("workerMain()"));
worker
}
@@ -251,7 +253,7 @@ mod tests {
console.log("after postMessage");
}
"#;
- js_check(worker.execute(source));
+ err_check(worker.execute(source));
let resource = worker.state.resource.clone();
let resource_ = resource.clone();
@@ -259,7 +261,7 @@ mod tests {
tokio::spawn(lazy(move || {
worker.then(move |r| -> Result<(), ()> {
resource_.close();
- js_check(r);
+ err_check(r);
Ok(())
})
}));
@@ -289,7 +291,7 @@ mod tests {
fn removed_from_resource_table_on_close() {
tokio_util::init(|| {
let mut worker = create_test_worker();
- js_check(
+ err_check(
worker.execute("onmessage = () => { delete window.onmessage; }"),
);
@@ -300,7 +302,7 @@ mod tests {
.then(move |r| -> Result<(), ()> {
resource.close();
println!("workers.rs after resource close");
- js_check(r);
+ err_check(r);
Ok(())
}).shared();
@@ -322,7 +324,7 @@ mod tests {
#[test]
fn execute_mod_resolve_error() {
tokio_util::init(|| {
- // "foo" is not a vailid module specifier so this should return an error.
+ // "foo" is not a valid module specifier so this should return an error.
let mut worker = create_test_worker();
let module_specifier =
ModuleSpecifier::resolve_root("does-not-exist").unwrap();