Combine CLI Errors (#2487)

This commit is contained in:
Kitson Kelly 2019-06-20 12:07:01 +10:00 committed by Ryan Dahl
parent 43f48386d7
commit 425df50484
35 changed files with 1305 additions and 759 deletions

View File

@ -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() {

View File

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

View File

@ -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.

538
cli/deno_error.rs Normal file
View File

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

View File

@ -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\ndeno/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()));
}
}

View File

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

View File

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

289
cli/fmt_errors.rs Normal file
View File

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

View File

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

View File

@ -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(),
));
}

View File

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

View File

@ -133,12 +133,16 @@ enum ErrorKind: byte {
// custom errors
InvalidUri,
InvalidSeekMode,
OpNotAvaiable,
OpNotAvailable,
WorkerInitFailed,
UnixError,
NoAsyncSupport,
NoSyncSupport,
ImportMapError,
// other kinds
Diagnostic,
JSError,
}
table Cwd {}

View File

@ -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() {

View File

@ -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(

View File

@ -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;

View File

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

View File

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

View File

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

View File

@ -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,77 +61,11 @@ 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),
)
}
},
},
};
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,
}
}
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);
}
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,
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.
// 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
@ -232,15 +90,149 @@ fn builtin_source_map(script_name: &str) -> Option<Vec<u8>> {
}
}
fn parse_map_string<G: SourceMapGetter>(
script_name: &str,
/// 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,
) -> 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())
})
) -> 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,
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,
}
}
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,
}
}
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 get_orig_position<G: SourceMapGetter>(
script_name: String,
line: i64,
column: i64,
mappings_map: &mut CachedMaps,
getter: &G,
) -> (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();
@ -398,6 +418,25 @@ mod tests {
assert!(actual.frames[0].script_name.ends_with("js/util.ts"));
}
#[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"}"#;

View File

@ -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;

View File

@ -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();

View File

@ -142,8 +142,6 @@ impl<L: Loader> RecursiveLoad<L> {
}
}
// TODO(ry) This is basically the same thing as RustOrJsError. They should be
// combined into one type.
#[derive(Debug, PartialEq)]
pub enum JSErrorOr<E> {
JSError(JSError),

View File

@ -1,9 +1,11 @@
[WILDCARD]hello
before error
world
[WILDCARD]tests/async_error.ts:4:10
throw Error("error");
error: Uncaught Error: error
[WILDCARD]tests/async_error.ts:4:9
4 throw Error("error");
^
Uncaught Error: error
at foo ([WILDCARD]tests/async_error.ts:4:9)
at [WILDCARD]tests/async_error.ts:7:1

View File

@ -1,7 +1,9 @@
[WILDCARD]Unsupported compiler options in "[WILDCARD]config.tsconfig.json"
The following options were ignored:
module, target
[WILDCARD]tests/config.ts:3:5 - error TS2532: Object is possibly 'undefined'.
[WILDCARD]error TS2532: Object is possibly 'undefined'.
[WILDCARD]tests/config.ts:3:5
3 if (map.get("bar").foo) {
~~~~~~~~~~~~~~

View File

@ -1,4 +1,9 @@
[WILDCARD]Error: bad
at foo (file://[WILDCARD]tests/error_001.ts:2:9)
at bar (file://[WILDCARD]tests/error_001.ts:6:3)
at file://[WILDCARD]tests/error_001.ts:9:1
[WILDCARD]error: Uncaught Error: bad
[WILDCARD]tests/error_001.ts:2:9
2 throw Error("bad");
^
at foo ([WILDCARD]tests/error_001.ts:2:9)
at bar ([WILDCARD]tests/error_001.ts:6:3)
at [WILDCARD]tests/error_001.ts:9:1

View File

@ -1,4 +1,9 @@
[WILDCARD]Error: exception from mod1
at throwsError (file://[WILDCARD]/tests/subdir/mod1.ts:16:9)
at foo (file://[WILDCARD]/tests/error_002.ts:4:3)
at file://[WILDCARD]/tests/error_002.ts:7:1
[WILDCARD]error: Uncaught Error: exception from mod1
[WILDCARD]tests/subdir/mod1.ts:16:9
16 throw Error("exception from mod1");
^
at throwsError ([WILDCARD]tests/subdir/mod1.ts:16:9)
at foo ([WILDCARD]tests/error_002.ts:4:3)
at [WILDCARD]tests/error_002.ts:7:1

View File

@ -1,18 +1,24 @@
[WILDCARD]/tests/error_003_typescript.ts:20:3 - error TS2322: Type '(o: T) => { v: any; f: (x: B) => string; }[]' is not assignable to type '(r: B) => Value<B>[]'.
[WILDCARD]error TS2322: Type '(o: T) => { v: any; f: (x: B) => string; }[]' is not assignable to type '(r: B) => Value<B>[]'.
Types of parameters 'o' and 'r' are incompatible.
Type 'B' is not assignable to type 'T'.
'B' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{}'.
[WILDCARD]tests/error_003_typescript.ts:20:3
20 values: o => [
~~~~~~
[WILDCARD]/tests/error_003_typescript.ts:8:3
The expected type comes from property 'values' which is declared here on type 'C<B>'
[WILDCARD]tests/error_003_typescript.ts:8:3
8 values?: (r: T) => Array<Value<T>>;
~~~~~~
The expected type comes from property 'values' which is declared here on type 'C<B>'
[WILDCARD]/tests/error_003_typescript.ts:22:12 - error TS2339: Property 't' does not exist on type 'T'.
error TS2339: Property 't' does not exist on type 'T'.
[WILDCARD]tests/error_003_typescript.ts:22:12
22 v: o.t,
^

View File

@ -1,4 +1,5 @@
[WILDCARD]Uncaught NotFound: Cannot resolve module "[WILDCARD]/bad-module.ts" from "[WILDCARD]/tests/error_004_missing_module.ts"
[WILDCARD]error: Uncaught NotFound: Cannot resolve module "[WILDCARD]/bad-module.ts" from "[WILDCARD]tests/error_004_missing_module.ts"
[WILDCARD] js/errors.ts:[WILDCARD]
at DenoError (js/errors.ts:[WILDCARD])
at maybeError (js/errors.ts:[WILDCARD])
at maybeThrowError (js/errors.ts:[WILDCARD])

View File

@ -1,4 +1,5 @@
[WILDCARD]NotFound: Cannot resolve module "[WILDCARD]/bad-module.ts" from "[WILDCARD]/tests/error_005_missing_dynamic_import.ts"
[WILDCARD]error: Uncaught NotFound: Cannot resolve module "[WILDCARD]/bad-module.ts" from "[WILDCARD]tests/error_005_missing_dynamic_import.ts"
[WILDCARD] js/errors.ts:[WILDCARD]
at DenoError (js/errors.ts:[WILDCARD])
at maybeError (js/errors.ts:[WILDCARD])
at maybeThrowError (js/errors.ts:[WILDCARD])

View File

@ -1,4 +1,5 @@
[WILDCARD]Uncaught NotFound: Cannot resolve module "[WILDCARD]/non-existent" from "[WILDCARD]/tests/error_006_import_ext_failure.ts"
[WILDCARD]error: Uncaught NotFound: Cannot resolve module "[WILDCARD]/non-existent" from "[WILDCARD]tests/error_006_import_ext_failure.ts"
[WILDCARD] js/errors.ts:[WILDCARD]
at DenoError (js/errors.ts:[WILDCARD])
at maybeError (js/errors.ts:[WILDCARD])
at maybeThrowError (js/errors.ts:[WILDCARD])

View File

@ -1 +1 @@
[WILDCARD]Uncaught #<Object>
[WILDCARD]error: Uncaught #<Object>

View File

@ -1,5 +1,7 @@
[WILDCARD]tests/error_008_checkjs.js:2:0
consol.log("hello world!");
^
Uncaught ReferenceError: consol is not defined
[WILDCARD]error: Uncaught ReferenceError: consol is not defined
[WILDCARD]tests/error_008_checkjs.js:2:1
2 consol.log("hello world!");
^
at [WILDCARD]tests/error_008_checkjs.js:2:1

View File

@ -1,4 +1,5 @@
[WILDCARD]Uncaught RelativeUrlWithCannotBeABaseBase: relative URL with a cannot-be-a-base base
[WILDCARD]error: Uncaught RelativeUrlWithCannotBeABaseBase: relative URL with a cannot-be-a-base base
[WILDCARD] js/errors.ts:[WILDCARD]
at DenoError (js/errors.ts:[WILDCARD])
at maybeError (js/errors.ts:[WILDCARD])
at maybeThrowError (js/errors.ts:[WILDCARD])

View File

@ -1,4 +1,5 @@
[WILDCARD]Uncaught RelativeUrlWithCannotBeABaseBase: relative URL with a cannot-be-a-base base
[WILDCARD]error: Uncaught RelativeUrlWithCannotBeABaseBase: relative URL with a cannot-be-a-base base
[WILDCARD] js/errors.ts:[WILDCARD]
at DenoError (js/errors.ts:[WILDCARD])
at maybeError (js/errors.ts:[WILDCARD])
at maybeThrowError (js/errors.ts:[WILDCARD])

View File

@ -1,4 +1,6 @@
[WILDCARD]tests/error_syntax.js:3:5
(the following is a syntax error ^^ ! )
^^^^^^^^^
Uncaught SyntaxError: Unexpected identifier
error: Uncaught SyntaxError: Unexpected identifier
[WILDCARD]tests/error_syntax.js:3:6
3 (the following is a syntax error ^^ ! )
~~~~~~~~~