mirror of
https://github.com/denoland/deno.git
synced 2025-01-21 21:50:00 -05:00
feat(runtime): give OS errors .code attributes (#12591)
This adds `.code` attributes to errors returned by the op-layer, facilitating classifying OS errors and helping node-compat. Similar to Node, these `.code` attributes are stringified names of unix ERRNOs, the mapping tables are generated by [tools/codegen_error_codes.js](https://gist.github.com/AaronO/dfa1106cc6c7e2a6ebe4dba9d5248858) and derived from libuv and rust's std internals
This commit is contained in:
parent
efe956b4fd
commit
44511e4f33
8 changed files with 225 additions and 4 deletions
5
Cargo.lock
generated
5
Cargo.lock
generated
|
@ -744,6 +744,7 @@ dependencies = [
|
||||||
"futures",
|
"futures",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"pin-project",
|
"pin-project",
|
||||||
|
@ -2021,9 +2022,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.103"
|
version = "0.2.106"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
|
checksum = "a60553f9a9e039a333b4e9b20573b9e9b9c0bb3a11e201ccc48ef4283456d673"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libffi"
|
name = "libffi"
|
||||||
|
|
|
@ -62,7 +62,7 @@ http = "0.2.4"
|
||||||
import_map = "0.3.3"
|
import_map = "0.3.3"
|
||||||
jsonc-parser = { version = "0.17.0", features = ["serde"] }
|
jsonc-parser = { version = "0.17.0", features = ["serde"] }
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
libc = "0.2.101"
|
libc = "0.2.106"
|
||||||
log = { version = "0.4.14", features = ["serde"] }
|
log = { version = "0.4.14", features = ["serde"] }
|
||||||
lspower = "1.1.0"
|
lspower = "1.1.0"
|
||||||
notify = "5.0.0-pre.12"
|
notify = "5.0.0-pre.12"
|
||||||
|
|
|
@ -119,6 +119,10 @@
|
||||||
const err = errorBuilder ? errorBuilder(res.message) : new Error(
|
const err = errorBuilder ? errorBuilder(res.message) : new Error(
|
||||||
`Unregistered error class: "${className}"\n ${res.message}\n Classes of errors returned from ops should be registered via Deno.core.registerErrorClass().`,
|
`Unregistered error class: "${className}"\n ${res.message}\n Classes of errors returned from ops should be registered via Deno.core.registerErrorClass().`,
|
||||||
);
|
);
|
||||||
|
// Set .code if error was a known OS error, see error_codes.rs
|
||||||
|
if (res.code) {
|
||||||
|
err.code = res.code;
|
||||||
|
}
|
||||||
// Strip unwrapOpResult() and errorBuilder() calls from stack trace
|
// Strip unwrapOpResult() and errorBuilder() calls from stack trace
|
||||||
ErrorCaptureStackTrace(err, unwrapOpResult);
|
ErrorCaptureStackTrace(err, unwrapOpResult);
|
||||||
throw err;
|
throw err;
|
||||||
|
|
|
@ -18,6 +18,7 @@ futures = "0.3.16"
|
||||||
# TODO(lucacasonato): unlock when https://github.com/tkaitchuck/aHash/issues/95 is resolved
|
# TODO(lucacasonato): unlock when https://github.com/tkaitchuck/aHash/issues/95 is resolved
|
||||||
indexmap = "=1.6.2"
|
indexmap = "=1.6.2"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
|
libc = "0.2.106"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
parking_lot = "0.11.1"
|
parking_lot = "0.11.1"
|
||||||
pin-project = "1.0.7"
|
pin-project = "1.0.7"
|
||||||
|
|
212
core/error_codes.rs
Normal file
212
core/error_codes.rs
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
use crate::error::AnyError;
|
||||||
|
|
||||||
|
pub(crate) fn get_error_code(err: &AnyError) -> Option<&'static str> {
|
||||||
|
err
|
||||||
|
.downcast_ref::<std::io::Error>()
|
||||||
|
.map(|e| match e.raw_os_error() {
|
||||||
|
Some(code) => get_os_error_code(code),
|
||||||
|
None => get_io_error_code(e),
|
||||||
|
})
|
||||||
|
.and_then(|code| match code.is_empty() {
|
||||||
|
true => None,
|
||||||
|
false => Some(code),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_io_error_code(err: &std::io::Error) -> &'static str {
|
||||||
|
// not exhaustive but simple and possibly sufficient once `io_error_more` is stabilized (https://github.com/rust-lang/rust/issues/86442)
|
||||||
|
// inversion of https://github.com/rust-lang/rust/blob/dca3f1b786efd27be3b325ed1e01e247aa589c3b/library/std/src/sys/unix/mod.rs#L138-L185
|
||||||
|
// TODO(@AaronO): revisit as `io_error_more` lands in rust stable
|
||||||
|
use std::io::ErrorKind;
|
||||||
|
match err.kind() {
|
||||||
|
// ErrorKind::ArgumentListTooLong => "E2BIG",
|
||||||
|
ErrorKind::AddrInUse => "EADDRINUSE",
|
||||||
|
ErrorKind::AddrNotAvailable => "EADDRNOTAVAIL",
|
||||||
|
// ErrorKind::ResourceBusy => "EBUSY",
|
||||||
|
ErrorKind::ConnectionAborted => "ECONNABORTED",
|
||||||
|
ErrorKind::ConnectionRefused => "ECONNREFUSED",
|
||||||
|
ErrorKind::ConnectionReset => "ECONNRESET",
|
||||||
|
// ErrorKind::Deadlock => "EDEADLK",
|
||||||
|
// ErrorKind::FilesystemQuotaExceeded => "EDQUOT",
|
||||||
|
ErrorKind::AlreadyExists => "EEXIST",
|
||||||
|
// ErrorKind::FileTooLarge => "EFBIG",
|
||||||
|
// ErrorKind::HostUnreachable => "EHOSTUNREACH",
|
||||||
|
ErrorKind::Interrupted => "EINTR",
|
||||||
|
ErrorKind::InvalidInput => "EINVAL",
|
||||||
|
// ErrorKind::IsADirectory => "EISDIR",
|
||||||
|
// ErrorKind::FilesystemLoop => "ELOOP",
|
||||||
|
ErrorKind::NotFound => "ENOENT",
|
||||||
|
ErrorKind::OutOfMemory => "ENOMEM",
|
||||||
|
// ErrorKind::StorageFull => "ENOSPC",
|
||||||
|
ErrorKind::Unsupported => "ENOSYS",
|
||||||
|
// ErrorKind::TooManyLinks => "EMLINK",
|
||||||
|
// ErrorKind::FilenameTooLong => "ENAMETOOLONG",
|
||||||
|
// ErrorKind::NetworkDown => "ENETDOWN",
|
||||||
|
// ErrorKind::NetworkUnreachable => "ENETUNREACH",
|
||||||
|
ErrorKind::NotConnected => "ENOTCONN",
|
||||||
|
// ErrorKind::NotADirectory => "ENOTDIR",
|
||||||
|
// ErrorKind::DirectoryNotEmpty => "ENOTEMPTY",
|
||||||
|
ErrorKind::BrokenPipe => "EPIPE",
|
||||||
|
// ErrorKind::ReadOnlyFilesystem => "EROFS",
|
||||||
|
// ErrorKind::NotSeekable => "ESPIPE",
|
||||||
|
// ErrorKind::StaleNetworkFileHandle => "ESTALE",
|
||||||
|
ErrorKind::TimedOut => "ETIMEDOUT",
|
||||||
|
// ErrorKind::ExecutableFileBusy => "ETXTBSY",
|
||||||
|
// ErrorKind::CrossesDevices => "EXDEV",
|
||||||
|
ErrorKind::PermissionDenied => "EACCES", // NOTE: Collides with EPERM ...
|
||||||
|
_ => "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Maps OS errno codes to string names
|
||||||
|
/// derived from libuv: https://github.com/libuv/libuv/blob/26b2e5dbb6301756644d6e4cf6ca9c49c00513d3/include/uv/errno.h
|
||||||
|
/// generated with tools/codegen_error_codes.js
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn get_os_error_code(errno: i32) -> &'static str {
|
||||||
|
match errno {
|
||||||
|
libc::E2BIG => "E2BIG",
|
||||||
|
libc::EACCES => "EACCES",
|
||||||
|
libc::EADDRINUSE => "EADDRINUSE",
|
||||||
|
libc::EADDRNOTAVAIL => "EADDRNOTAVAIL",
|
||||||
|
libc::EAFNOSUPPORT => "EAFNOSUPPORT",
|
||||||
|
libc::EAGAIN => "EAGAIN",
|
||||||
|
libc::EALREADY => "EALREADY",
|
||||||
|
libc::EBADF => "EBADF",
|
||||||
|
libc::EBUSY => "EBUSY",
|
||||||
|
libc::ECANCELED => "ECANCELED",
|
||||||
|
libc::ECONNABORTED => "ECONNABORTED",
|
||||||
|
libc::ECONNREFUSED => "ECONNREFUSED",
|
||||||
|
libc::ECONNRESET => "ECONNRESET",
|
||||||
|
libc::EEXIST => "EEXIST",
|
||||||
|
libc::EFAULT => "EFAULT",
|
||||||
|
libc::EHOSTUNREACH => "EHOSTUNREACH",
|
||||||
|
libc::EINVAL => "EINVAL",
|
||||||
|
libc::EIO => "EIO",
|
||||||
|
libc::EISCONN => "EISCONN",
|
||||||
|
libc::EISDIR => "EISDIR",
|
||||||
|
libc::ELOOP => "ELOOP",
|
||||||
|
libc::EMFILE => "EMFILE",
|
||||||
|
libc::EMSGSIZE => "EMSGSIZE",
|
||||||
|
libc::ENAMETOOLONG => "ENAMETOOLONG",
|
||||||
|
libc::ENETUNREACH => "ENETUNREACH",
|
||||||
|
libc::ENOBUFS => "ENOBUFS",
|
||||||
|
libc::ENOENT => "ENOENT",
|
||||||
|
libc::ENOMEM => "ENOMEM",
|
||||||
|
libc::ENOSPC => "ENOSPC",
|
||||||
|
libc::ENOTCONN => "ENOTCONN",
|
||||||
|
libc::ENOTEMPTY => "ENOTEMPTY",
|
||||||
|
libc::ENOTSOCK => "ENOTSOCK",
|
||||||
|
libc::ENOTSUP => "ENOTSUP",
|
||||||
|
libc::EPERM => "EPERM",
|
||||||
|
libc::EPIPE => "EPIPE",
|
||||||
|
libc::EPROTONOSUPPORT => "EPROTONOSUPPORT",
|
||||||
|
libc::EROFS => "EROFS",
|
||||||
|
libc::ETIMEDOUT => "ETIMEDOUT",
|
||||||
|
libc::EXDEV => "EXDEV",
|
||||||
|
libc::ESOCKTNOSUPPORT => "ESOCKTNOSUPPORT",
|
||||||
|
_ => "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn get_os_error_code(errno: i32) -> &'static str {
|
||||||
|
match errno {
|
||||||
|
998 => "EACCES", // ERROR_NOACCESS
|
||||||
|
10013 => "EACCES", // WSAEACCES
|
||||||
|
1920 => "EACCES", // ERROR_CANT_ACCESS_FILE
|
||||||
|
1227 => "EADDRINUSE", // ERROR_ADDRESS_ALREADY_ASSOCIATED
|
||||||
|
10048 => "EADDRINUSE", // WSAEADDRINUSE
|
||||||
|
10049 => "EADDRNOTAVAIL", // WSAEADDRNOTAVAIL
|
||||||
|
10047 => "EAFNOSUPPORT", // WSAEAFNOSUPPORT
|
||||||
|
10035 => "EAGAIN", // WSAEWOULDBLOCK
|
||||||
|
10037 => "EALREADY", // WSAEALREADY
|
||||||
|
1004 => "EBADF", // ERROR_INVALID_FLAGS
|
||||||
|
6 => "EBADF", // ERROR_INVALID_HANDLE
|
||||||
|
33 => "EBUSY", // ERROR_LOCK_VIOLATION
|
||||||
|
231 => "EBUSY", // ERROR_PIPE_BUSY
|
||||||
|
32 => "EBUSY", // ERROR_SHARING_VIOLATION
|
||||||
|
995 => "ECANCELED", // ERROR_OPERATION_ABORTED
|
||||||
|
10004 => "ECANCELED", // WSAEINTR
|
||||||
|
1236 => "ECONNABORTED", // ERROR_CONNECTION_ABORTED
|
||||||
|
10053 => "ECONNABORTED", // WSAECONNABORTED
|
||||||
|
1225 => "ECONNREFUSED", // ERROR_CONNECTION_REFUSED
|
||||||
|
10061 => "ECONNREFUSED", // WSAECONNREFUSED
|
||||||
|
64 => "ECONNRESET", // ERROR_NETNAME_DELETED
|
||||||
|
10054 => "ECONNRESET", // WSAECONNRESET
|
||||||
|
183 => "EEXIST", // ERROR_ALREADY_EXISTS
|
||||||
|
80 => "EEXIST", // ERROR_FILE_EXISTS
|
||||||
|
111 => "EFAULT", // ERROR_BUFFER_OVERFLOW
|
||||||
|
10014 => "EFAULT", // WSAEFAULT
|
||||||
|
1232 => "EHOSTUNREACH", // ERROR_HOST_UNREACHABLE
|
||||||
|
10065 => "EHOSTUNREACH", // WSAEHOSTUNREACH
|
||||||
|
122 => "EINVAL", // ERROR_INSUFFICIENT_BUFFER
|
||||||
|
13 => "EINVAL", // ERROR_INVALID_DATA
|
||||||
|
87 => "EINVAL", // ERROR_INVALID_PARAMETER
|
||||||
|
1464 => "EINVAL", // ERROR_SYMLINK_NOT_SUPPORTED
|
||||||
|
10022 => "EINVAL", // WSAEINVAL
|
||||||
|
10046 => "EINVAL", // WSAEPFNOSUPPORT
|
||||||
|
1102 => "EIO", // ERROR_BEGINNING_OF_MEDIA
|
||||||
|
1111 => "EIO", // ERROR_BUS_RESET
|
||||||
|
23 => "EIO", // ERROR_CRC
|
||||||
|
1166 => "EIO", // ERROR_DEVICE_DOOR_OPEN
|
||||||
|
1165 => "EIO", // ERROR_DEVICE_REQUIRES_CLEANING
|
||||||
|
1393 => "EIO", // ERROR_DISK_CORRUPT
|
||||||
|
1129 => "EIO", // ERROR_EOM_OVERFLOW
|
||||||
|
1101 => "EIO", // ERROR_FILEMARK_DETECTED
|
||||||
|
31 => "EIO", // ERROR_GEN_FAILURE
|
||||||
|
1106 => "EIO", // ERROR_INVALID_BLOCK_LENGTH
|
||||||
|
1117 => "EIO", // ERROR_IO_DEVICE
|
||||||
|
1104 => "EIO", // ERROR_NO_DATA_DETECTED
|
||||||
|
205 => "EIO", // ERROR_NO_SIGNAL_SENT
|
||||||
|
110 => "EIO", // ERROR_OPEN_FAILED
|
||||||
|
1103 => "EIO", // ERROR_SETMARK_DETECTED
|
||||||
|
156 => "EIO", // ERROR_SIGNAL_REFUSED
|
||||||
|
10056 => "EISCONN", // WSAEISCONN
|
||||||
|
1921 => "ELOOP", // ERROR_CANT_RESOLVE_FILENAME
|
||||||
|
4 => "EMFILE", // ERROR_TOO_MANY_OPEN_FILES
|
||||||
|
10024 => "EMFILE", // WSAEMFILE
|
||||||
|
10040 => "EMSGSIZE", // WSAEMSGSIZE
|
||||||
|
206 => "ENAMETOOLONG", // ERROR_FILENAME_EXCED_RANGE
|
||||||
|
1231 => "ENETUNREACH", // ERROR_NETWORK_UNREACHABLE
|
||||||
|
10051 => "ENETUNREACH", // WSAENETUNREACH
|
||||||
|
10055 => "ENOBUFS", // WSAENOBUFS
|
||||||
|
161 => "ENOENT", // ERROR_BAD_PATHNAME
|
||||||
|
267 => "ENOENT", // ERROR_DIRECTORY
|
||||||
|
203 => "ENOENT", // ERROR_ENVVAR_NOT_FOUND
|
||||||
|
2 => "ENOENT", // ERROR_FILE_NOT_FOUND
|
||||||
|
123 => "ENOENT", // ERROR_INVALID_NAME
|
||||||
|
15 => "ENOENT", // ERROR_INVALID_DRIVE
|
||||||
|
4392 => "ENOENT", // ERROR_INVALID_REPARSE_DATA
|
||||||
|
126 => "ENOENT", // ERROR_MOD_NOT_FOUND
|
||||||
|
3 => "ENOENT", // ERROR_PATH_NOT_FOUND
|
||||||
|
11001 => "ENOENT", // WSAHOST_NOT_FOUND
|
||||||
|
11004 => "ENOENT", // WSANO_DATA
|
||||||
|
8 => "ENOMEM", // ERROR_NOT_ENOUGH_MEMORY
|
||||||
|
14 => "ENOMEM", // ERROR_OUTOFMEMORY
|
||||||
|
82 => "ENOSPC", // ERROR_CANNOT_MAKE
|
||||||
|
112 => "ENOSPC", // ERROR_DISK_FULL
|
||||||
|
277 => "ENOSPC", // ERROR_EA_TABLE_FULL
|
||||||
|
1100 => "ENOSPC", // ERROR_END_OF_MEDIA
|
||||||
|
39 => "ENOSPC", // ERROR_HANDLE_DISK_FULL
|
||||||
|
2250 => "ENOTCONN", // ERROR_NOT_CONNECTED
|
||||||
|
10057 => "ENOTCONN", // WSAENOTCONN
|
||||||
|
145 => "ENOTEMPTY", // ERROR_DIR_NOT_EMPTY
|
||||||
|
10038 => "ENOTSOCK", // WSAENOTSOCK
|
||||||
|
50 => "ENOTSUP", // ERROR_NOT_SUPPORTED
|
||||||
|
5 => "EPERM", // ERROR_ACCESS_DENIED
|
||||||
|
1314 => "EPERM", // ERROR_PRIVILEGE_NOT_HELD
|
||||||
|
230 => "EPIPE", // ERROR_BAD_PIPE
|
||||||
|
232 => "EPIPE", // ERROR_NO_DATA
|
||||||
|
233 => "EPIPE", // ERROR_PIPE_NOT_CONNECTED
|
||||||
|
10058 => "EPIPE", // WSAESHUTDOWN
|
||||||
|
10043 => "EPROTONOSUPPORT", // WSAEPROTONOSUPPORT
|
||||||
|
19 => "EROFS", // ERROR_WRITE_PROTECT
|
||||||
|
121 => "ETIMEDOUT", // ERROR_SEM_TIMEOUT
|
||||||
|
10060 => "ETIMEDOUT", // WSAETIMEDOUT
|
||||||
|
17 => "EXDEV", // ERROR_NOT_SAME_DEVICE
|
||||||
|
1 => "EISDIR", // ERROR_INVALID_FUNCTION
|
||||||
|
208 => "E2BIG", // ERROR_META_EXPANSION_TOO_LONG
|
||||||
|
10044 => "ESOCKTNOSUPPORT", // WSAESOCKTNOSUPPORT
|
||||||
|
_ => "",
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ mod async_cancel;
|
||||||
mod async_cell;
|
mod async_cell;
|
||||||
mod bindings;
|
mod bindings;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
mod error_codes;
|
||||||
mod extensions;
|
mod extensions;
|
||||||
mod flags;
|
mod flags;
|
||||||
mod gotham_state;
|
mod gotham_state;
|
||||||
|
|
|
@ -140,6 +140,7 @@ pub struct OpError {
|
||||||
#[serde(rename = "$err_class_name")]
|
#[serde(rename = "$err_class_name")]
|
||||||
class_name: &'static str,
|
class_name: &'static str,
|
||||||
message: String,
|
message: String,
|
||||||
|
code: Option<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serialize_op_result<R: Serialize + 'static>(
|
pub fn serialize_op_result<R: Serialize + 'static>(
|
||||||
|
@ -151,6 +152,7 @@ pub fn serialize_op_result<R: Serialize + 'static>(
|
||||||
Err(err) => OpResult::Err(OpError {
|
Err(err) => OpResult::Err(OpError {
|
||||||
class_name: (state.borrow().get_error_class_fn)(&err),
|
class_name: (state.borrow().get_error_class_fn)(&err),
|
||||||
message: err.to_string(),
|
message: err.to_string(),
|
||||||
|
code: crate::error_codes::get_error_code(&err),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,7 @@ fs3 = "0.5.0"
|
||||||
http = "0.2.4"
|
http = "0.2.4"
|
||||||
hyper = { version = "0.14.12", features = ["server", "stream", "http1", "http2", "runtime"] }
|
hyper = { version = "0.14.12", features = ["server", "stream", "http1", "http2", "runtime"] }
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
libc = "0.2.101"
|
libc = "0.2.106"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
notify = "=5.0.0-pre.12"
|
notify = "=5.0.0-pre.12"
|
||||||
regex = "1.5.4"
|
regex = "1.5.4"
|
||||||
|
|
Loading…
Add table
Reference in a new issue