summaryrefslogtreecommitdiff
path: root/runtime
diff options
context:
space:
mode:
Diffstat (limited to 'runtime')
-rw-r--r--runtime/Cargo.toml1
-rw-r--r--runtime/errors.rs145
-rw-r--r--runtime/ops/fs_events.rs34
-rw-r--r--runtime/ops/http.rs32
-rw-r--r--runtime/ops/mod.rs1
-rw-r--r--runtime/ops/os/mod.rs115
-rw-r--r--runtime/ops/process.rs150
-rw-r--r--runtime/ops/runtime.rs6
-rw-r--r--runtime/ops/signal.rs71
-rw-r--r--runtime/ops/tty.rs51
-rw-r--r--runtime/ops/utils.rs12
-rw-r--r--runtime/ops/web_worker.rs11
-rw-r--r--runtime/ops/web_worker/sync_fetch.rs90
-rw-r--r--runtime/ops/worker_host.rs58
-rw-r--r--runtime/web_worker.rs14
15 files changed, 557 insertions, 234 deletions
diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml
index dcb6b1ade..14580a8a6 100644
--- a/runtime/Cargo.toml
+++ b/runtime/Cargo.toml
@@ -121,6 +121,7 @@ serde.workspace = true
signal-hook = "0.3.17"
signal-hook-registry = "1.4.0"
tempfile.workspace = true
+thiserror.workspace = true
tokio.workspace = true
tokio-metrics.workspace = true
twox-hash.workspace = true
diff --git a/runtime/errors.rs b/runtime/errors.rs
index 9fc84407a..869f97968 100644
--- a/runtime/errors.rs
+++ b/runtime/errors.rs
@@ -9,6 +9,14 @@
//! Diagnostics are compile-time type errors, whereas JsErrors are runtime
//! exceptions.
+use crate::ops::fs_events::FsEventsError;
+use crate::ops::http::HttpStartError;
+use crate::ops::os::OsError;
+use crate::ops::process::ProcessError;
+use crate::ops::signal::SignalError;
+use crate::ops::tty::TtyError;
+use crate::ops::web_worker::SyncFetchError;
+use crate::ops::worker_host::CreateWorkerError;
use deno_broadcast_channel::BroadcastChannelError;
use deno_cache::CacheError;
use deno_canvas::CanvasError;
@@ -49,6 +57,7 @@ use deno_web::WebError;
use deno_websocket::HandshakeError;
use deno_websocket::WebsocketError;
use deno_webstorage::WebStorageError;
+use rustyline::error::ReadlineError;
use std::env;
use std::error::Error;
use std::io;
@@ -806,6 +815,102 @@ fn get_net_map_error(error: &deno_net::io::MapError) -> &'static str {
}
}
+fn get_create_worker_error(error: &CreateWorkerError) -> &'static str {
+ match error {
+ CreateWorkerError::ClassicWorkers => "DOMExceptionNotSupportedError",
+ CreateWorkerError::Permission(e) => {
+ get_error_class_name(e).unwrap_or("Error")
+ }
+ CreateWorkerError::ModuleResolution(e) => {
+ get_module_resolution_error_class(e)
+ }
+ CreateWorkerError::Io(e) => get_io_error_class(e),
+ CreateWorkerError::MessagePort(e) => get_web_message_port_error_class(e),
+ }
+}
+
+fn get_tty_error(error: &TtyError) -> &'static str {
+ match error {
+ TtyError::Resource(e) | TtyError::Other(e) => {
+ get_error_class_name(e).unwrap_or("Error")
+ }
+ TtyError::Io(e) => get_io_error_class(e),
+ #[cfg(unix)]
+ TtyError::Nix(e) => get_nix_error_class(e),
+ }
+}
+
+fn get_readline_error(error: &ReadlineError) -> &'static str {
+ match error {
+ ReadlineError::Io(e) => get_io_error_class(e),
+ ReadlineError::Eof => "Error",
+ ReadlineError::Interrupted => "Error",
+ #[cfg(unix)]
+ ReadlineError::Errno(e) => get_nix_error_class(e),
+ ReadlineError::WindowResized => "Error",
+ #[cfg(windows)]
+ ReadlineError::Decode(_) => "Error",
+ #[cfg(windows)]
+ ReadlineError::SystemError(_) => "Error",
+ _ => "Error",
+ }
+}
+
+fn get_signal_error(error: &SignalError) -> &'static str {
+ match error {
+ SignalError::InvalidSignalStr(_) => "TypeError",
+ SignalError::InvalidSignalInt(_) => "TypeError",
+ SignalError::SignalNotAllowed(_) => "TypeError",
+ SignalError::Io(e) => get_io_error_class(e),
+ }
+}
+
+fn get_fs_events_error(error: &FsEventsError) -> &'static str {
+ match error {
+ FsEventsError::Resource(e) | FsEventsError::Permission(e) => {
+ get_error_class_name(e).unwrap_or("Error")
+ }
+ FsEventsError::Notify(e) => get_notify_error_class(e),
+ FsEventsError::Canceled(e) => {
+ let io_err: io::Error = e.to_owned().into();
+ get_io_error_class(&io_err)
+ }
+ }
+}
+
+fn get_http_start_error(error: &HttpStartError) -> &'static str {
+ match error {
+ HttpStartError::TcpStreamInUse => "Busy",
+ HttpStartError::TlsStreamInUse => "Busy",
+ HttpStartError::UnixSocketInUse => "Busy",
+ HttpStartError::ReuniteTcp(_) => "Error",
+ #[cfg(unix)]
+ HttpStartError::ReuniteUnix(_) => "Error",
+ HttpStartError::Io(e) => get_io_error_class(e),
+ HttpStartError::Other(e) => get_error_class_name(e).unwrap_or("Error"),
+ }
+}
+
+fn get_process_error(error: &ProcessError) -> &'static str {
+ match error {
+ ProcessError::SpawnFailed { error, .. } => get_process_error(error),
+ ProcessError::FailedResolvingCwd(e) | ProcessError::Io(e) => {
+ get_io_error_class(e)
+ }
+ ProcessError::Permission(e) | ProcessError::Resource(e) => {
+ get_error_class_name(e).unwrap_or("Error")
+ }
+ ProcessError::BorrowMut(_) => "Error",
+ ProcessError::Which(_) => "Error",
+ ProcessError::ChildProcessAlreadyTerminated => "TypeError",
+ ProcessError::Signal(e) => get_signal_error(e),
+ ProcessError::MissingCmd => "Error",
+ ProcessError::InvalidPid => "TypeError",
+ #[cfg(unix)]
+ ProcessError::Nix(e) => get_nix_error_class(e),
+ }
+}
+
fn get_http_error(error: &HttpError) -> &'static str {
match error {
HttpError::Canceled(e) => {
@@ -859,11 +964,51 @@ fn get_websocket_upgrade_error(error: &WebSocketUpgradeError) -> &'static str {
}
}
+fn get_os_error(error: &OsError) -> &'static str {
+ match error {
+ OsError::Permission(e) => get_error_class_name(e).unwrap_or("Error"),
+ OsError::InvalidUtf8(_) => "InvalidData",
+ OsError::EnvEmptyKey => "TypeError",
+ OsError::EnvInvalidKey(_) => "TypeError",
+ OsError::EnvInvalidValue(_) => "TypeError",
+ OsError::Io(e) => get_io_error_class(e),
+ OsError::Var(e) => get_env_var_error_class(e),
+ }
+}
+
+fn get_sync_fetch_error(error: &SyncFetchError) -> &'static str {
+ match error {
+ SyncFetchError::BlobUrlsNotSupportedInContext => "TypeError",
+ SyncFetchError::Io(e) => get_io_error_class(e),
+ SyncFetchError::InvalidScriptUrl => "TypeError",
+ SyncFetchError::InvalidStatusCode(_) => "TypeError",
+ SyncFetchError::ClassicScriptSchemeUnsupportedInWorkers(_) => "TypeError",
+ SyncFetchError::InvalidUri(_) => "Error",
+ SyncFetchError::InvalidMimeType(_) => "DOMExceptionNetworkError",
+ SyncFetchError::MissingMimeType => "DOMExceptionNetworkError",
+ SyncFetchError::Fetch(e) => get_fetch_error(e),
+ SyncFetchError::Join(_) => "Error",
+ SyncFetchError::Other(e) => get_error_class_name(e).unwrap_or("Error"),
+ }
+}
+
pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> {
deno_core::error::get_custom_error_class(e)
.or_else(|| e.downcast_ref::<NApiError>().map(get_napi_error_class))
.or_else(|| e.downcast_ref::<WebError>().map(get_web_error_class))
.or_else(|| {
+ e.downcast_ref::<CreateWorkerError>()
+ .map(get_create_worker_error)
+ })
+ .or_else(|| e.downcast_ref::<TtyError>().map(get_tty_error))
+ .or_else(|| e.downcast_ref::<ReadlineError>().map(get_readline_error))
+ .or_else(|| e.downcast_ref::<SignalError>().map(get_signal_error))
+ .or_else(|| e.downcast_ref::<FsEventsError>().map(get_fs_events_error))
+ .or_else(|| e.downcast_ref::<HttpStartError>().map(get_http_start_error))
+ .or_else(|| e.downcast_ref::<ProcessError>().map(get_process_error))
+ .or_else(|| e.downcast_ref::<OsError>().map(get_os_error))
+ .or_else(|| e.downcast_ref::<SyncFetchError>().map(get_sync_fetch_error))
+ .or_else(|| {
e.downcast_ref::<CompressionError>()
.map(get_web_compression_error_class)
})
diff --git a/runtime/ops/fs_events.rs b/runtime/ops/fs_events.rs
index d88a32d91..89a0606db 100644
--- a/runtime/ops/fs_events.rs
+++ b/runtime/ops/fs_events.rs
@@ -1,6 +1,5 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-use deno_core::error::AnyError;
use deno_core::parking_lot::Mutex;
use deno_core::AsyncRefCell;
use deno_core::CancelFuture;
@@ -37,7 +36,7 @@ deno_core::extension!(
struct FsEventsResource {
#[allow(unused)]
watcher: RecommendedWatcher,
- receiver: AsyncRefCell<mpsc::Receiver<Result<FsEvent, AnyError>>>,
+ receiver: AsyncRefCell<mpsc::Receiver<Result<FsEvent, NotifyError>>>,
cancel: CancelHandle,
}
@@ -93,6 +92,18 @@ impl From<NotifyEvent> for FsEvent {
}
}
+#[derive(Debug, thiserror::Error)]
+pub enum FsEventsError {
+ #[error(transparent)]
+ Resource(deno_core::error::AnyError),
+ #[error(transparent)]
+ Permission(deno_core::error::AnyError),
+ #[error(transparent)]
+ Notify(#[from] NotifyError),
+ #[error(transparent)]
+ Canceled(#[from] deno_core::Canceled),
+}
+
#[derive(Deserialize)]
pub struct OpenArgs {
recursive: bool,
@@ -104,12 +115,12 @@ pub struct OpenArgs {
fn op_fs_events_open(
state: &mut OpState,
#[serde] args: OpenArgs,
-) -> Result<ResourceId, AnyError> {
- let (sender, receiver) = mpsc::channel::<Result<FsEvent, AnyError>>(16);
+) -> Result<ResourceId, FsEventsError> {
+ let (sender, receiver) = mpsc::channel::<Result<FsEvent, NotifyError>>(16);
let sender = Mutex::new(sender);
let mut watcher: RecommendedWatcher = Watcher::new(
move |res: Result<NotifyEvent, NotifyError>| {
- let res2 = res.map(FsEvent::from).map_err(AnyError::from);
+ let res2 = res.map(FsEvent::from);
let sender = sender.lock();
// Ignore result, if send failed it means that watcher was already closed,
// but not all messages have been flushed.
@@ -125,7 +136,8 @@ fn op_fs_events_open(
for path in &args.paths {
let path = state
.borrow_mut::<PermissionsContainer>()
- .check_read(path, "Deno.watchFs()")?;
+ .check_read(path, "Deno.watchFs()")
+ .map_err(FsEventsError::Permission)?;
watcher.watch(&path, recursive_mode)?;
}
let resource = FsEventsResource {
@@ -142,14 +154,18 @@ fn op_fs_events_open(
async fn op_fs_events_poll(
state: Rc<RefCell<OpState>>,
#[smi] rid: ResourceId,
-) -> Result<Option<FsEvent>, AnyError> {
- let resource = state.borrow().resource_table.get::<FsEventsResource>(rid)?;
+) -> Result<Option<FsEvent>, FsEventsError> {
+ let resource = state
+ .borrow()
+ .resource_table
+ .get::<FsEventsResource>(rid)
+ .map_err(FsEventsError::Resource)?;
let mut receiver = RcRef::map(&resource, |r| &r.receiver).borrow_mut().await;
let cancel = RcRef::map(resource, |r| &r.cancel);
let maybe_result = receiver.recv().or_cancel(cancel).await?;
match maybe_result {
Some(Ok(value)) => Ok(Some(value)),
- Some(Err(err)) => Err(err),
+ Some(Err(err)) => Err(FsEventsError::Notify(err)),
None => Ok(None),
}
}
diff --git a/runtime/ops/http.rs b/runtime/ops/http.rs
index cbabbe22c..6e3157668 100644
--- a/runtime/ops/http.rs
+++ b/runtime/ops/http.rs
@@ -2,9 +2,6 @@
use std::rc::Rc;
-use deno_core::error::bad_resource_id;
-use deno_core::error::custom_error;
-use deno_core::error::AnyError;
use deno_core::op2;
use deno_core::OpState;
use deno_core::ResourceId;
@@ -16,12 +13,31 @@ pub const UNSTABLE_FEATURE_NAME: &str = "http";
deno_core::extension!(deno_http_runtime, ops = [op_http_start],);
+#[derive(Debug, thiserror::Error)]
+pub enum HttpStartError {
+ #[error("TCP stream is currently in use")]
+ TcpStreamInUse,
+ #[error("TLS stream is currently in use")]
+ TlsStreamInUse,
+ #[error("Unix socket is currently in use")]
+ UnixSocketInUse,
+ #[error(transparent)]
+ ReuniteTcp(#[from] tokio::net::tcp::ReuniteError),
+ #[cfg(unix)]
+ #[error(transparent)]
+ ReuniteUnix(#[from] tokio::net::unix::ReuniteError),
+ #[error("{0}")]
+ Io(#[from] std::io::Error),
+ #[error(transparent)]
+ Other(deno_core::error::AnyError),
+}
+
#[op2(fast)]
#[smi]
fn op_http_start(
state: &mut OpState,
#[smi] tcp_stream_rid: ResourceId,
-) -> Result<ResourceId, AnyError> {
+) -> Result<ResourceId, HttpStartError> {
if let Ok(resource_rc) = state
.resource_table
.take::<TcpStreamResource>(tcp_stream_rid)
@@ -30,7 +46,7 @@ fn op_http_start(
// process of starting a HTTP server on top of this TCP connection, so we just return a Busy error.
// See also: https://github.com/denoland/deno/pull/16242
let resource = Rc::try_unwrap(resource_rc)
- .map_err(|_| custom_error("Busy", "TCP stream is currently in use"))?;
+ .map_err(|_| HttpStartError::TcpStreamInUse)?;
let (read_half, write_half) = resource.into_inner();
let tcp_stream = read_half.reunite(write_half)?;
let addr = tcp_stream.local_addr()?;
@@ -45,7 +61,7 @@ fn op_http_start(
// process of starting a HTTP server on top of this TLS connection, so we just return a Busy error.
// See also: https://github.com/denoland/deno/pull/16242
let resource = Rc::try_unwrap(resource_rc)
- .map_err(|_| custom_error("Busy", "TLS stream is currently in use"))?;
+ .map_err(|_| HttpStartError::TlsStreamInUse)?;
let (read_half, write_half) = resource.into_inner();
let tls_stream = read_half.unsplit(write_half);
let addr = tls_stream.local_addr()?;
@@ -61,7 +77,7 @@ fn op_http_start(
// process of starting a HTTP server on top of this UNIX socket, so we just return a Busy error.
// See also: https://github.com/denoland/deno/pull/16242
let resource = Rc::try_unwrap(resource_rc)
- .map_err(|_| custom_error("Busy", "Unix socket is currently in use"))?;
+ .map_err(|_| HttpStartError::UnixSocketInUse)?;
let (read_half, write_half) = resource.into_inner();
let unix_stream = read_half.reunite(write_half)?;
let addr = unix_stream.local_addr()?;
@@ -73,5 +89,5 @@ fn op_http_start(
));
}
- Err(bad_resource_id())
+ Err(HttpStartError::Other(deno_core::error::bad_resource_id()))
}
diff --git a/runtime/ops/mod.rs b/runtime/ops/mod.rs
index feed5052b..67065b901 100644
--- a/runtime/ops/mod.rs
+++ b/runtime/ops/mod.rs
@@ -9,7 +9,6 @@ pub mod process;
pub mod runtime;
pub mod signal;
pub mod tty;
-mod utils;
pub mod web_worker;
pub mod worker_host;
diff --git a/runtime/ops/os/mod.rs b/runtime/ops/os/mod.rs
index bd9260e97..24b0389e1 100644
--- a/runtime/ops/os/mod.rs
+++ b/runtime/ops/os/mod.rs
@@ -1,9 +1,6 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-use super::utils::into_string;
use crate::worker::ExitCode;
-use deno_core::error::type_error;
-use deno_core::error::AnyError;
use deno_core::op2;
use deno_core::v8;
use deno_core::OpState;
@@ -73,17 +70,39 @@ deno_core::extension!(
},
);
+#[derive(Debug, thiserror::Error)]
+pub enum OsError {
+ #[error(transparent)]
+ Permission(deno_core::error::AnyError),
+ #[error("File name or path {0:?} is not valid UTF-8")]
+ InvalidUtf8(std::ffi::OsString),
+ #[error("Key is an empty string.")]
+ EnvEmptyKey,
+ #[error("Key contains invalid characters: {0:?}")]
+ EnvInvalidKey(String),
+ #[error("Value contains invalid characters: {0:?}")]
+ EnvInvalidValue(String),
+ #[error(transparent)]
+ Var(#[from] env::VarError),
+ #[error("{0}")]
+ Io(#[from] std::io::Error),
+}
+
#[op2]
#[string]
-fn op_exec_path(state: &mut OpState) -> Result<String, AnyError> {
+fn op_exec_path(state: &mut OpState) -> Result<String, OsError> {
let current_exe = env::current_exe().unwrap();
state
.borrow_mut::<PermissionsContainer>()
- .check_read_blind(&current_exe, "exec_path", "Deno.execPath()")?;
+ .check_read_blind(&current_exe, "exec_path", "Deno.execPath()")
+ .map_err(OsError::Permission)?;
// normalize path so it doesn't include '.' or '..' components
let path = normalize_path(current_exe);
- into_string(path.into_os_string())
+ path
+ .into_os_string()
+ .into_string()
+ .map_err(OsError::InvalidUtf8)
}
#[op2(fast)]
@@ -91,20 +110,19 @@ fn op_set_env(
state: &mut OpState,
#[string] key: &str,
#[string] value: &str,
-) -> Result<(), AnyError> {
- state.borrow_mut::<PermissionsContainer>().check_env(key)?;
+) -> Result<(), OsError> {
+ state
+ .borrow_mut::<PermissionsContainer>()
+ .check_env(key)
+ .map_err(OsError::Permission)?;
if key.is_empty() {
- return Err(type_error("Key is an empty string."));
+ return Err(OsError::EnvEmptyKey);
}
if key.contains(&['=', '\0'] as &[char]) {
- return Err(type_error(format!(
- "Key contains invalid characters: {key:?}"
- )));
+ return Err(OsError::EnvInvalidKey(key.to_string()));
}
if value.contains('\0') {
- return Err(type_error(format!(
- "Value contains invalid characters: {value:?}"
- )));
+ return Err(OsError::EnvInvalidValue(value.to_string()));
}
env::set_var(key, value);
Ok(())
@@ -112,7 +130,9 @@ fn op_set_env(
#[op2]
#[serde]
-fn op_env(state: &mut OpState) -> Result<HashMap<String, String>, AnyError> {
+fn op_env(
+ state: &mut OpState,
+) -> Result<HashMap<String, String>, deno_core::error::AnyError> {
state.borrow_mut::<PermissionsContainer>().check_env_all()?;
Ok(env::vars().collect())
}
@@ -122,21 +142,22 @@ fn op_env(state: &mut OpState) -> Result<HashMap<String, String>, AnyError> {
fn op_get_env(
state: &mut OpState,
#[string] key: String,
-) -> Result<Option<String>, AnyError> {
+) -> Result<Option<String>, OsError> {
let skip_permission_check = NODE_ENV_VAR_ALLOWLIST.contains(&key);
if !skip_permission_check {
- state.borrow_mut::<PermissionsContainer>().check_env(&key)?;
+ state
+ .borrow_mut::<PermissionsContainer>()
+ .check_env(&key)
+ .map_err(OsError::Permission)?;
}
if key.is_empty() {
- return Err(type_error("Key is an empty string."));
+ return Err(OsError::EnvEmptyKey);
}
if key.contains(&['=', '\0'] as &[char]) {
- return Err(type_error(format!(
- "Key contains invalid characters: {key:?}"
- )));
+ return Err(OsError::EnvInvalidKey(key.to_string()));
}
let r = match env::var(key) {
@@ -150,10 +171,13 @@ fn op_get_env(
fn op_delete_env(
state: &mut OpState,
#[string] key: String,
-) -> Result<(), AnyError> {
- state.borrow_mut::<PermissionsContainer>().check_env(&key)?;
+) -> Result<(), OsError> {
+ state
+ .borrow_mut::<PermissionsContainer>()
+ .check_env(&key)
+ .map_err(OsError::Permission)?;
if key.is_empty() || key.contains(&['=', '\0'] as &[char]) {
- return Err(type_error("Key contains invalid characters."));
+ return Err(OsError::EnvInvalidKey(key.to_string()));
}
env::remove_var(key);
Ok(())
@@ -178,7 +202,9 @@ fn op_exit(state: &mut OpState) {
#[op2]
#[serde]
-fn op_loadavg(state: &mut OpState) -> Result<(f64, f64, f64), AnyError> {
+fn op_loadavg(
+ state: &mut OpState,
+) -> Result<(f64, f64, f64), deno_core::error::AnyError> {
state
.borrow_mut::<PermissionsContainer>()
.check_sys("loadavg", "Deno.loadavg()")?;
@@ -187,7 +213,9 @@ fn op_loadavg(state: &mut OpState) -> Result<(f64, f64, f64), AnyError> {
#[op2]
#[string]
-fn op_hostname(state: &mut OpState) -> Result<String, AnyError> {
+fn op_hostname(
+ state: &mut OpState,
+) -> Result<String, deno_core::error::AnyError> {
state
.borrow_mut::<PermissionsContainer>()
.check_sys("hostname", "Deno.hostname()")?;
@@ -196,7 +224,9 @@ fn op_hostname(state: &mut OpState) -> Result<String, AnyError> {
#[op2]
#[string]
-fn op_os_release(state: &mut OpState) -> Result<String, AnyError> {
+fn op_os_release(
+ state: &mut OpState,
+) -> Result<String, deno_core::error::AnyError> {
state
.borrow_mut::<PermissionsContainer>()
.check_sys("osRelease", "Deno.osRelease()")?;
@@ -207,10 +237,11 @@ fn op_os_release(state: &mut OpState) -> Result<String, AnyError> {
#[serde]
fn op_network_interfaces(
state: &mut OpState,
-) -> Result<Vec<NetworkInterface>, AnyError> {
+) -> Result<Vec<NetworkInterface>, OsError> {
state
.borrow_mut::<PermissionsContainer>()
- .check_sys("networkInterfaces", "Deno.networkInterfaces()")?;
+ .check_sys("networkInterfaces", "Deno.networkInterfaces()")
+ .map_err(OsError::Permission)?;
Ok(netif::up()?.map(NetworkInterface::from).collect())
}
@@ -259,7 +290,7 @@ impl From<netif::Interface> for NetworkInterface {
#[serde]
fn op_system_memory_info(
state: &mut OpState,
-) -> Result<Option<sys_info::MemInfo>, AnyError> {
+) -> Result<Option<sys_info::MemInfo>, deno_core::error::AnyError> {
state
.borrow_mut::<PermissionsContainer>()
.check_sys("systemMemoryInfo", "Deno.systemMemoryInfo()")?;
@@ -269,7 +300,9 @@ fn op_system_memory_info(
#[cfg(not(windows))]
#[op2]
#[smi]
-fn op_gid(state: &mut OpState) -> Result<Option<u32>, AnyError> {
+fn op_gid(
+ state: &mut OpState,
+) -> Result<Option<u32>, deno_core::error::AnyError> {
state
.borrow_mut::<PermissionsContainer>()
.check_sys("gid", "Deno.gid()")?;
@@ -283,7 +316,9 @@ fn op_gid(state: &mut OpState) -> Result<Option<u32>, AnyError> {
#[cfg(windows)]
#[op2]
#[smi]
-fn op_gid(state: &mut OpState) -> Result<Option<u32>, AnyError> {
+fn op_gid(
+ state: &mut OpState,
+) -> Result<Option<u32>, deno_core::error::AnyError> {
state
.borrow_mut::<PermissionsContainer>()
.check_sys("gid", "Deno.gid()")?;
@@ -293,7 +328,9 @@ fn op_gid(state: &mut OpState) -> Result<Option<u32>, AnyError> {
#[cfg(not(windows))]
#[op2]
#[smi]
-fn op_uid(state: &mut OpState) -> Result<Option<u32>, AnyError> {
+fn op_uid(
+ state: &mut OpState,
+) -> Result<Option<u32>, deno_core::error::AnyError> {
state
.borrow_mut::<PermissionsContainer>()
.check_sys("uid", "Deno.uid()")?;
@@ -307,7 +344,9 @@ fn op_uid(state: &mut OpState) -> Result<Option<u32>, AnyError> {
#[cfg(windows)]
#[op2]
#[smi]
-fn op_uid(state: &mut OpState) -> Result<Option<u32>, AnyError> {
+fn op_uid(
+ state: &mut OpState,
+) -> Result<Option<u32>, deno_core::error::AnyError> {
state
.borrow_mut::<PermissionsContainer>()
.check_sys("uid", "Deno.uid()")?;
@@ -485,7 +524,7 @@ fn rss() -> usize {
}
}
-fn os_uptime(state: &mut OpState) -> Result<u64, AnyError> {
+fn os_uptime(state: &mut OpState) -> Result<u64, deno_core::error::AnyError> {
state
.borrow_mut::<PermissionsContainer>()
.check_sys("osUptime", "Deno.osUptime()")?;
@@ -494,6 +533,8 @@ fn os_uptime(state: &mut OpState) -> Result<u64, AnyError> {
#[op2(fast)]
#[number]
-fn op_os_uptime(state: &mut OpState) -> Result<u64, AnyError> {
+fn op_os_uptime(
+ state: &mut OpState,
+) -> Result<u64, deno_core::error::AnyError> {
os_uptime(state)
}
diff --git a/runtime/ops/process.rs b/runtime/ops/process.rs
index f6555e932..28913f7c1 100644
--- a/runtime/ops/process.rs
+++ b/runtime/ops/process.rs
@@ -1,8 +1,5 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-use deno_core::anyhow::Context;
-use deno_core::error::type_error;
-use deno_core::error::AnyError;
use deno_core::op2;
use deno_core::serde_json;
use deno_core::AsyncMutFuture;
@@ -35,6 +32,7 @@ use tokio::process::Command;
#[cfg(windows)]
use std::os::windows::process::CommandExt;
+use crate::ops::signal::SignalError;
#[cfg(unix)]
use std::os::unix::prelude::ExitStatusExt;
#[cfg(unix)]
@@ -105,11 +103,12 @@ impl StdioOrRid {
pub fn as_stdio(
&self,
state: &mut OpState,
- ) -> Result<std::process::Stdio, AnyError> {
+ ) -> Result<std::process::Stdio, ProcessError> {
match &self {
StdioOrRid::Stdio(val) => Ok(val.as_stdio()),
StdioOrRid::Rid(rid) => {
FileResource::with_file(state, *rid, |file| Ok(file.as_stdio()?))
+ .map_err(ProcessError::Resource)
}
}
}
@@ -191,6 +190,39 @@ pub struct SpawnArgs {
needs_npm_process_state: bool,
}
+#[derive(Debug, thiserror::Error)]
+pub enum ProcessError {
+ #[error("Failed to spawn '{command}': {error}")]
+ SpawnFailed {
+ command: String,
+ #[source]
+ error: Box<ProcessError>,
+ },
+ #[error("{0}")]
+ Io(#[from] std::io::Error),
+ #[cfg(unix)]
+ #[error(transparent)]
+ Nix(nix::Error),
+ #[error("failed resolving cwd: {0}")]
+ FailedResolvingCwd(#[source] std::io::Error),
+ #[error(transparent)]
+ Permission(deno_core::error::AnyError),
+ #[error(transparent)]
+ Resource(deno_core::error::AnyError),
+ #[error(transparent)]
+ BorrowMut(std::cell::BorrowMutError),
+ #[error(transparent)]
+ Which(which::Error),
+ #[error("Child process has already terminated.")]
+ ChildProcessAlreadyTerminated,
+ #[error("Invalid pid")]
+ InvalidPid,
+ #[error(transparent)]
+ Signal(#[from] SignalError),
+ #[error("Missing cmd")]
+ MissingCmd, // only for Deno.run
+}
+
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ChildStdio {
@@ -208,7 +240,7 @@ pub struct ChildStatus {
}
impl TryFrom<ExitStatus> for ChildStatus {
- type Error = AnyError;
+ type Error = SignalError;
fn try_from(status: ExitStatus) -> Result<Self, Self::Error> {
let code = status.code();
@@ -259,7 +291,7 @@ type CreateCommand = (
pub fn npm_process_state_tempfile(
contents: &[u8],
-) -> Result<deno_io::RawIoHandle, AnyError> {
+) -> Result<deno_io::RawIoHandle, std::io::Error> {
let mut temp_file = tempfile::tempfile()?;
temp_file.write_all(contents)?;
let handle = temp_file.into_raw_io_handle();
@@ -301,7 +333,7 @@ fn create_command(
state: &mut OpState,
mut args: SpawnArgs,
api_name: &str,
-) -> Result<CreateCommand, AnyError> {
+) -> Result<CreateCommand, ProcessError> {
let maybe_npm_process_state = if args.needs_npm_process_state {
let provider = state.borrow::<NpmProcessStateProviderRc>();
let process_state = provider.get_npm_process_state();
@@ -505,7 +537,7 @@ fn spawn_child(
ipc_pipe_rid: Option<ResourceId>,
extra_pipe_rids: Vec<Option<ResourceId>>,
detached: bool,
-) -> Result<Child, AnyError> {
+) -> Result<Child, ProcessError> {
let mut command = tokio::process::Command::from(command);
// TODO(@crowlkats): allow detaching processes.
// currently deno will orphan a process when exiting with an error or Deno.exit()
@@ -554,10 +586,10 @@ fn spawn_child(
}
}
- return Err(AnyError::from(err).context(format!(
- "Failed to spawn '{}'",
- command.get_program().to_string_lossy()
- )));
+ return Err(ProcessError::SpawnFailed {
+ command: command.get_program().to_string_lossy().to_string(),
+ error: Box::new(err.into()),
+ });
}
};
@@ -600,11 +632,19 @@ fn compute_run_cmd_and_check_permissions(
arg_clear_env: bool,
state: &mut OpState,
api_name: &str,
-) -> Result<(PathBuf, RunEnv), AnyError> {
- let run_env = compute_run_env(arg_cwd, arg_envs, arg_clear_env)
- .with_context(|| format!("Failed to spawn '{}'", arg_cmd))?;
- let cmd = resolve_cmd(arg_cmd, &run_env)
- .with_context(|| format!("Failed to spawn '{}'", arg_cmd))?;
+) -> Result<(PathBuf, RunEnv), ProcessError> {
+ let run_env =
+ compute_run_env(arg_cwd, arg_envs, arg_clear_env).map_err(|e| {
+ ProcessError::SpawnFailed {
+ command: arg_cmd.to_string(),
+ error: Box::new(e),
+ }
+ })?;
+ let cmd =
+ resolve_cmd(arg_cmd, &run_env).map_err(|e| ProcessError::SpawnFailed {
+ command: arg_cmd.to_string(),
+ error: Box::new(e),
+ })?;
check_run_permission(
state,
&RunQueryDescriptor::Path {
@@ -613,7 +653,8 @@ fn compute_run_cmd_and_check_permissions(
},
&run_env,
api_name,
- )?;
+ )
+ .map_err(ProcessError::Permission)?;
Ok((cmd, run_env))
}
@@ -631,9 +672,10 @@ fn compute_run_env(
arg_cwd: Option<&str>,
arg_envs: &[(String, String)],
arg_clear_env: bool,
-) -> Result<RunEnv, AnyError> {
+) -> Result<RunEnv, ProcessError> {
#[allow(clippy::disallowed_methods)]
- let cwd = std::env::current_dir().context("failed resolving cwd")?;
+ let cwd =
+ std::env::current_dir().map_err(ProcessError::FailedResolvingCwd)?;
let cwd = arg_cwd
.map(|cwd_arg| resolve_path(cwd_arg, &cwd))
.unwrap_or(cwd);
@@ -670,7 +712,7 @@ fn compute_run_env(
Ok(RunEnv { envs, cwd })
}
-fn resolve_cmd(cmd: &str, env: &RunEnv) -> Result<PathBuf, AnyError> {
+fn resolve_cmd(cmd: &str, env: &RunEnv) -> Result<PathBuf, ProcessError> {
let is_path = cmd.contains('/');
#[cfg(windows)]
let is_path = is_path || cmd.contains('\\') || Path::new(&cmd).is_absolute();
@@ -683,7 +725,7 @@ fn resolve_cmd(cmd: &str, env: &RunEnv) -> Result<PathBuf, AnyError> {
Err(which::Error::CannotFindBinaryPath) => {
Err(std::io::Error::from(std::io::ErrorKind::NotFound).into())
}
- Err(err) => Err(err.into()),
+ Err(err) => Err(ProcessError::Which(err)),
}
}
}
@@ -697,7 +739,7 @@ fn check_run_permission(
cmd: &RunQueryDescriptor,
run_env: &RunEnv,
api_name: &str,
-) -> Result<(), AnyError> {
+) -> Result<(), deno_core::error::AnyError> {
let permissions = state.borrow_mut::<PermissionsContainer>();
if !permissions.query_run_all(api_name) {
// error the same on all platforms
@@ -754,7 +796,7 @@ fn op_spawn_child(
state: &mut OpState,
#[serde] args: SpawnArgs,
#[string] api_name: String,
-) -> Result<Child, AnyError> {
+) -> Result<Child, ProcessError> {
let detached = args.detached;
let (command, pipe_rid, extra_pipe_rids, handles_to_close) =
create_command(state, args, &api_name)?;
@@ -771,16 +813,23 @@ fn op_spawn_child(
async fn op_spawn_wait(
state: Rc<RefCell<OpState>>,
#[smi] rid: ResourceId,
-) -> Result<ChildStatus, AnyError> {
+) -> Result<ChildStatus, ProcessError> {
let resource = state
.borrow_mut()
.resource_table
- .get::<ChildResource>(rid)?;
- let result = resource.0.try_borrow_mut()?.wait().await?.try_into();
+ .get::<ChildResource>(rid)
+ .map_err(ProcessError::Resource)?;
+ let result = resource
+ .0
+ .try_borrow_mut()
+ .map_err(ProcessError::BorrowMut)?
+ .wait()
+ .await?
+ .try_into()?;
if let Ok(resource) = state.borrow_mut().resource_table.take_any(rid) {
resource.close();
}
- result
+ Ok(result)
}
#[op2]
@@ -788,16 +837,14 @@ async fn op_spawn_wait(
fn op_spawn_sync(
state: &mut OpState,
#[serde] args: SpawnArgs,
-) -> Result<SpawnOutput, AnyError> {
+) -> Result<SpawnOutput, ProcessError> {
let stdout = matches!(args.stdio.stdout, StdioOrRid::Stdio(Stdio::Piped));
let stderr = matches!(args.stdio.stderr, StdioOrRid::Stdio(Stdio::Piped));
let (mut command, _, _, _) =
create_command(state, args, "Deno.Command().outputSync()")?;
- let output = command.output().with_context(|| {
- format!(
- "Failed to spawn '{}'",
- command.get_program().to_string_lossy()
- )
+ let output = command.output().map_err(|e| ProcessError::SpawnFailed {
+ command: command.get_program().to_string_lossy().to_string(),
+ error: Box::new(e.into()),
})?;
Ok(SpawnOutput {
@@ -820,17 +867,15 @@ fn op_spawn_kill(
state: &mut OpState,
#[smi] rid: ResourceId,
#[string] signal: String,
-) -> Result<(), AnyError> {
+) -> Result<(), ProcessError> {
if let Ok(child_resource) = state.resource_table.get::<ChildResource>(rid) {
deprecated::kill(child_resource.1 as i32, &signal)?;
return Ok(());
}
- Err(type_error("Child process has already terminated."))
+ Err(ProcessError::ChildProcessAlreadyTerminated)
}
mod deprecated {
- use deno_core::anyhow;
-
use super::*;
#[derive(Deserialize)]
@@ -876,9 +921,9 @@ mod deprecated {
pub fn op_run(
state: &mut OpState,
#[serde] run_args: RunArgs,
- ) -> Result<RunInfo, AnyError> {
+ ) -> Result<RunInfo, ProcessError> {
let args = run_args.cmd;
- let cmd = args.first().ok_or_else(|| anyhow::anyhow!("Missing cmd"))?;
+ let cmd = args.first().ok_or(ProcessError::MissingCmd)?;
let (cmd, run_env) = compute_run_cmd_and_check_permissions(
cmd,
run_args.cwd.as_deref(),
@@ -990,11 +1035,12 @@ mod deprecated {
pub async fn op_run_status(
state: Rc<RefCell<OpState>>,
#[smi] rid: ResourceId,
- ) -> Result<ProcessStatus, AnyError> {
+ ) -> Result<ProcessStatus, ProcessError> {
let resource = state
.borrow_mut()
.resource_table
- .get::<ChildResource>(rid)?;
+ .get::<ChildResource>(rid)
+ .map_err(ProcessError::Resource)?;
let mut child = resource.borrow_mut().await;
let run_status = child.wait().await?;
let code = run_status.code();
@@ -1017,17 +1063,17 @@ mod deprecated {
}
#[cfg(unix)]
- pub fn kill(pid: i32, signal: &str) -> Result<(), AnyError> {
+ pub fn kill(pid: i32, signal: &str) -> Result<(), ProcessError> {
let signo = super::super::signal::signal_str_to_int(signal)?;
use nix::sys::signal::kill as unix_kill;
use nix::sys::signal::Signal;
use nix::unistd::Pid;
- let sig = Signal::try_from(signo)?;
- unix_kill(Pid::from_raw(pid), Option::Some(sig)).map_err(AnyError::from)
+ let sig = Signal::try_from(signo).map_err(ProcessError::Nix)?;
+ unix_kill(Pid::from_raw(pid), Some(sig)).map_err(ProcessError::Nix)
}
#[cfg(not(unix))]
- pub fn kill(pid: i32, signal: &str) -> Result<(), AnyError> {
+ pub fn kill(pid: i32, signal: &str) -> Result<(), ProcessError> {
use std::io::Error;
use std::io::ErrorKind::NotFound;
use winapi::shared::minwindef::DWORD;
@@ -1041,9 +1087,9 @@ mod deprecated {
use winapi::um::winnt::PROCESS_TERMINATE;
if !matches!(signal, "SIGKILL" | "SIGTERM") {
- Err(type_error(format!("Invalid signal: {signal}")))
+ Err(SignalError::InvalidSignalStr(signal.to_string()).into())
} else if pid <= 0 {
- Err(type_error("Invalid pid"))
+ Err(ProcessError::InvalidPid)
} else {
let handle =
// SAFETY: winapi call
@@ -1077,11 +1123,11 @@ mod deprecated {
#[smi] pid: i32,
#[string] signal: String,
#[string] api_name: String,
- ) -> Result<(), AnyError> {
+ ) -> Result<(), ProcessError> {
state
.borrow_mut::<PermissionsContainer>()
- .check_run_all(&api_name)?;
- kill(pid, &signal)?;
- Ok(())
+ .check_run_all(&api_name)
+ .map_err(ProcessError::Permission)?;
+ kill(pid, &signal)
}
}
diff --git a/runtime/ops/runtime.rs b/runtime/ops/runtime.rs
index 419274ebd..8d54783fc 100644
--- a/runtime/ops/runtime.rs
+++ b/runtime/ops/runtime.rs
@@ -1,6 +1,5 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-use deno_core::error::AnyError;
use deno_core::op2;
use deno_core::ModuleSpecifier;
use deno_core::OpState;
@@ -16,10 +15,9 @@ deno_core::extension!(
#[op2]
#[string]
-fn op_main_module(state: &mut OpState) -> Result<String, AnyError> {
+fn op_main_module(state: &mut OpState) -> String {
let main_url = state.borrow::<ModuleSpecifier>();
- let main_path = main_url.to_string();
- Ok(main_path)
+ main_url.to_string()
}
/// This is an op instead of being done at initialization time because
diff --git a/runtime/ops/signal.rs b/runtime/ops/signal.rs
index 3bf43d6e1..e1e4ab68b 100644
--- a/runtime/ops/signal.rs
+++ b/runtime/ops/signal.rs
@@ -1,6 +1,4 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-use deno_core::error::type_error;
-use deno_core::error::AnyError;
use deno_core::op2;
use deno_core::AsyncRefCell;
use deno_core::CancelFuture;
@@ -46,6 +44,42 @@ deno_core::extension!(
}
);
+#[derive(Debug, thiserror::Error)]
+pub enum SignalError {
+ #[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "openbsd",
+ target_os = "openbsd",
+ target_os = "macos",
+ target_os = "solaris",
+ target_os = "illumos"
+ ))]
+ #[error("Invalid signal: {0}")]
+ InvalidSignalStr(String),
+ #[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "openbsd",
+ target_os = "openbsd",
+ target_os = "macos",
+ target_os = "solaris",
+ target_os = "illumos"
+ ))]
+ #[error("Invalid signal: {0}")]
+ InvalidSignalInt(libc::c_int),
+ #[cfg(target_os = "windows")]
+ #[error("Windows only supports ctrl-c (SIGINT) and ctrl-break (SIGBREAK), but got {0}")]
+ InvalidSignalStr(String),
+ #[cfg(target_os = "windows")]
+ #[error("Windows only supports ctrl-c (SIGINT) and ctrl-break (SIGBREAK), but got {0}")]
+ InvalidSignalInt(libc::c_int),
+ #[error("Binding to signal '{0}' is not allowed")]
+ SignalNotAllowed(String),
+ #[error("{0}")]
+ Io(#[from] std::io::Error),
+}
+
#[cfg(unix)]
#[derive(Default)]
struct SignalState {
@@ -153,18 +187,18 @@ macro_rules! first_literal {
};
}
macro_rules! signal_dict {
- ($error_msg:expr, $(($number:literal, $($name:literal)|+)),*) => {
- pub fn signal_str_to_int(s: &str) -> Result<libc::c_int, AnyError> {
+ ($(($number:literal, $($name:literal)|+)),*) => {
+ pub fn signal_str_to_int(s: &str) -> Result<libc::c_int, SignalError> {
match s {
$($($name)|* => Ok($number),)*
- _ => Err(type_error($error_msg(s))),
+ _ => Err(SignalError::InvalidSignalStr(s.to_string())),
}
}
- pub fn signal_int_to_str(s: libc::c_int) -> Result<&'static str, AnyError> {
+ pub fn signal_int_to_str(s: libc::c_int) -> Result<&'static str, SignalError> {
match s {
$($number => Ok(first_literal!($($name),+)),)*
- _ => Err(type_error($error_msg(s))),
+ _ => Err(SignalError::InvalidSignalInt(s)),
}
}
}
@@ -172,7 +206,6 @@ macro_rules! signal_dict {
#[cfg(target_os = "freebsd")]
signal_dict!(
- |s| { format!("Invalid signal : {}", s) },
(1, "SIGHUP"),
(2, "SIGINT"),
(3, "SIGQUIT"),
@@ -210,7 +243,6 @@ signal_dict!(
#[cfg(target_os = "openbsd")]
signal_dict!(
- |s| { format!("Invalid signal : {}", s) },
(1, "SIGHUP"),
(2, "SIGINT"),
(3, "SIGQUIT"),
@@ -246,7 +278,6 @@ signal_dict!(
#[cfg(any(target_os = "android", target_os = "linux"))]
signal_dict!(
- |s| { format!("Invalid signal : {s}") },
(1, "SIGHUP"),
(2, "SIGINT"),
(3, "SIGQUIT"),
@@ -282,7 +313,6 @@ signal_dict!(
#[cfg(target_os = "macos")]
signal_dict!(
- |s| { format!("Invalid signal : {s}") },
(1, "SIGHUP"),
(2, "SIGINT"),
(3, "SIGQUIT"),
@@ -318,7 +348,6 @@ signal_dict!(
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
signal_dict!(
- |s| { format!("Invalid signal : {s}") },
(1, "SIGHUP"),
(2, "SIGINT"),
(3, "SIGQUIT"),
@@ -362,11 +391,7 @@ signal_dict!(
);
#[cfg(target_os = "windows")]
-signal_dict!(
- |_| { "Windows only supports ctrl-c (SIGINT) and ctrl-break (SIGBREAK)." },
- (2, "SIGINT"),
- (21, "SIGBREAK")
-);
+signal_dict!((2, "SIGINT"), (21, "SIGBREAK"));
#[cfg(unix)]
#[op2(fast)]
@@ -374,12 +399,10 @@ signal_dict!(
fn op_signal_bind(
state: &mut OpState,
#[string] sig: &str,
-) -> Result<ResourceId, AnyError> {
+) -> Result<ResourceId, SignalError> {
let signo = signal_str_to_int(sig)?;
if signal_hook_registry::FORBIDDEN.contains(&signo) {
- return Err(type_error(format!(
- "Binding to signal '{sig}' is not allowed",
- )));
+ return Err(SignalError::SignalNotAllowed(sig.to_string()));
}
let signal = AsyncRefCell::new(signal(SignalKind::from_raw(signo))?);
@@ -413,7 +436,7 @@ fn op_signal_bind(
fn op_signal_bind(
state: &mut OpState,
#[string] sig: &str,
-) -> Result<ResourceId, AnyError> {
+) -> Result<ResourceId, SignalError> {
let signo = signal_str_to_int(sig)?;
let resource = SignalStreamResource {
signal: AsyncRefCell::new(match signo {
@@ -437,7 +460,7 @@ fn op_signal_bind(
async fn op_signal_poll(
state: Rc<RefCell<OpState>>,
#[smi] rid: ResourceId,
-) -> Result<bool, AnyError> {
+) -> Result<bool, deno_core::error::AnyError> {
let resource = state
.borrow_mut()
.resource_table
@@ -456,7 +479,7 @@ async fn op_signal_poll(
pub fn op_signal_unbind(
state: &mut OpState,
#[smi] rid: ResourceId,
-) -> Result<(), AnyError> {
+) -> Result<(), deno_core::error::AnyError> {
let resource = state.resource_table.take::<SignalStreamResource>(rid)?;
#[cfg(unix)]
diff --git a/runtime/ops/tty.rs b/runtime/ops/tty.rs
index 77e1330b5..7849185fa 100644
--- a/runtime/ops/tty.rs
+++ b/runtime/ops/tty.rs
@@ -2,7 +2,6 @@
use std::io::Error;
-use deno_core::error::AnyError;
use deno_core::op2;
use deno_core::OpState;
use rustyline::config::Configurer;
@@ -64,6 +63,19 @@ deno_core::extension!(
},
);
+#[derive(Debug, thiserror::Error)]
+pub enum TtyError {
+ #[error(transparent)]
+ Resource(deno_core::error::AnyError),
+ #[error("{0}")]
+ Io(#[from] std::io::Error),
+ #[cfg(unix)]
+ #[error(transparent)]
+ Nix(nix::Error),
+ #[error(transparent)]
+ Other(deno_core::error::AnyError),
+}
+
// ref: <https://learn.microsoft.com/en-us/windows/console/setconsolemode>
#[cfg(windows)]
const COOKED_MODE: DWORD =
@@ -90,8 +102,11 @@ fn op_set_raw(
rid: u32,
is_raw: bool,
cbreak: bool,
-) -> Result<(), AnyError> {
- let handle_or_fd = state.resource_table.get_fd(rid)?;
+) -> Result<(), TtyError> {
+ let handle_or_fd = state
+ .resource_table
+ .get_fd(rid)
+ .map_err(TtyError::Resource)?;
// From https://github.com/kkawakam/rustyline/blob/master/src/tty/windows.rs
// and https://github.com/kkawakam/rustyline/blob/master/src/tty/unix.rs
@@ -107,7 +122,7 @@ fn op_set_raw(
let handle = handle_or_fd;
if cbreak {
- return Err(deno_core::error::not_supported());
+ return Err(TtyError::Other(deno_core::error::not_supported()));
}
let mut original_mode: DWORD = 0;
@@ -115,7 +130,7 @@ fn op_set_raw(
if unsafe { consoleapi::GetConsoleMode(handle, &mut original_mode) }
== FALSE
{
- return Err(Error::last_os_error().into());
+ return Err(TtyError::Io(Error::last_os_error()));
}
let new_mode = if is_raw {
@@ -185,7 +200,7 @@ fn op_set_raw(
winapi::um::wincon::WriteConsoleInputW(handle, &record, 1, &mut 0)
} == FALSE
{
- return Err(Error::last_os_error().into());
+ return Err(TtyError::Io(Error::last_os_error()));
}
/* Wait for read thread to acknowledge the cancellation to ensure that nothing
@@ -199,7 +214,7 @@ fn op_set_raw(
// SAFETY: winapi call
if unsafe { consoleapi::SetConsoleMode(handle, new_mode) } == FALSE {
- return Err(Error::last_os_error().into());
+ return Err(TtyError::Io(Error::last_os_error()));
}
Ok(())
@@ -252,7 +267,8 @@ fn op_set_raw(
Some(mode) => mode,
None => {
// Save original mode.
- let original_mode = termios::tcgetattr(raw_fd)?;
+ let original_mode =
+ termios::tcgetattr(raw_fd).map_err(TtyError::Nix)?;
tty_mode_store.set(rid, original_mode.clone());
original_mode
}
@@ -274,11 +290,13 @@ fn op_set_raw(
}
raw.control_chars[termios::SpecialCharacterIndices::VMIN as usize] = 1;
raw.control_chars[termios::SpecialCharacterIndices::VTIME as usize] = 0;
- termios::tcsetattr(raw_fd, termios::SetArg::TCSADRAIN, &raw)?;
+ termios::tcsetattr(raw_fd, termios::SetArg::TCSADRAIN, &raw)
+ .map_err(TtyError::Nix)?;
} else {
// Try restore saved mode.
if let Some(mode) = tty_mode_store.take(rid) {
- termios::tcsetattr(raw_fd, termios::SetArg::TCSADRAIN, &mode)?;
+ termios::tcsetattr(raw_fd, termios::SetArg::TCSADRAIN, &mode)
+ .map_err(TtyError::Nix)?;
}
}
@@ -290,13 +308,16 @@ fn op_set_raw(
fn op_console_size(
state: &mut OpState,
#[buffer] result: &mut [u32],
-) -> Result<(), AnyError> {
+) -> Result<(), TtyError> {
fn check_console_size(
state: &mut OpState,
result: &mut [u32],
rid: u32,
- ) -> Result<(), AnyError> {
- let fd = state.resource_table.get_fd(rid)?;
+ ) -> Result<(), TtyError> {
+ let fd = state
+ .resource_table
+ .get_fd(rid)
+ .map_err(TtyError::Resource)?;
let size = console_size_from_fd(fd)?;
result[0] = size.cols;
result[1] = size.rows;
@@ -419,7 +440,7 @@ mod tests {
pub fn op_read_line_prompt(
#[string] prompt_text: &str,
#[string] default_value: &str,
-) -> Result<Option<String>, AnyError> {
+) -> Result<Option<String>, ReadlineError> {
let mut editor = Editor::<(), rustyline::history::DefaultHistory>::new()
.expect("Failed to create editor.");
@@ -439,6 +460,6 @@ pub fn op_read_line_prompt(
Ok(None)
}
Err(ReadlineError::Eof) => Ok(None),
- Err(err) => Err(err.into()),
+ Err(err) => Err(err),
}
}
diff --git a/runtime/ops/utils.rs b/runtime/ops/utils.rs
deleted file mode 100644
index d5ce61c1f..000000000
--- a/runtime/ops/utils.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-
-use deno_core::error::custom_error;
-use deno_core::error::AnyError;
-
-/// A utility function to map OsStrings to Strings
-pub fn into_string(s: std::ffi::OsString) -> Result<String, AnyError> {
- s.into_string().map_err(|s| {
- let message = format!("File name or path {s:?} is not valid UTF-8");
- custom_error("InvalidData", message)
- })
-}
diff --git a/runtime/ops/web_worker.rs b/runtime/ops/web_worker.rs
index e28bf2192..d0c3eea66 100644
--- a/runtime/ops/web_worker.rs
+++ b/runtime/ops/web_worker.rs
@@ -4,15 +4,16 @@ mod sync_fetch;
use crate::web_worker::WebWorkerInternalHandle;
use crate::web_worker::WebWorkerType;
-use deno_core::error::AnyError;
use deno_core::op2;
use deno_core::CancelFuture;
use deno_core::OpState;
use deno_web::JsMessageData;
+use deno_web::MessagePortError;
use std::cell::RefCell;
use std::rc::Rc;
use self::sync_fetch::op_worker_sync_fetch;
+pub use sync_fetch::SyncFetchError;
deno_core::extension!(
deno_web_worker,
@@ -30,17 +31,16 @@ deno_core::extension!(
fn op_worker_post_message(
state: &mut OpState,
#[serde] data: JsMessageData,
-) -> Result<(), AnyError> {
+) -> Result<(), MessagePortError> {
let handle = state.borrow::<WebWorkerInternalHandle>().clone();
- handle.port.send(state, data)?;
- Ok(())
+ handle.port.send(state, data)
}
#[op2(async(lazy), fast)]
#[serde]
async fn op_worker_recv_message(
state: Rc<RefCell<OpState>>,
-) -> Result<Option<JsMessageData>, AnyError> {
+) -> Result<Option<JsMessageData>, MessagePortError> {
let handle = {
let state = state.borrow();
state.borrow::<WebWorkerInternalHandle>().clone()
@@ -50,7 +50,6 @@ async fn op_worker_recv_message(
.recv(state.clone())
.or_cancel(handle.cancel)
.await?
- .map_err(|e| e.into())
}
#[op2(fast)]
diff --git a/runtime/ops/web_worker/sync_fetch.rs b/runtime/ops/web_worker/sync_fetch.rs
index bd55a5fc8..d1f133d3d 100644
--- a/runtime/ops/web_worker/sync_fetch.rs
+++ b/runtime/ops/web_worker/sync_fetch.rs
@@ -4,14 +4,12 @@ use std::sync::Arc;
use crate::web_worker::WebWorkerInternalHandle;
use crate::web_worker::WebWorkerType;
-use deno_core::error::custom_error;
-use deno_core::error::type_error;
-use deno_core::error::AnyError;
use deno_core::futures::StreamExt;
use deno_core::op2;
use deno_core::url::Url;
use deno_core::OpState;
use deno_fetch::data_url::DataUrl;
+use deno_fetch::FetchError;
use deno_web::BlobStore;
use http_body_util::BodyExt;
use hyper::body::Bytes;
@@ -27,6 +25,32 @@ fn mime_type_essence(mime_type: &str) -> String {
essence.trim().to_ascii_lowercase()
}
+#[derive(Debug, thiserror::Error)]
+pub enum SyncFetchError {
+ #[error("Blob URLs are not supported in this context.")]
+ BlobUrlsNotSupportedInContext,
+ #[error("{0}")]
+ Io(#[from] std::io::Error),
+ #[error("Invalid script URL")]
+ InvalidScriptUrl,
+ #[error("http status error: {0}")]
+ InvalidStatusCode(http::StatusCode),
+ #[error("Classic scripts with scheme {0}: are not supported in workers")]
+ ClassicScriptSchemeUnsupportedInWorkers(String),
+ #[error("{0}")]
+ InvalidUri(#[from] http::uri::InvalidUri),
+ #[error("Invalid MIME type {0:?}.")]
+ InvalidMimeType(String),
+ #[error("Missing MIME type.")]
+ MissingMimeType,
+ #[error(transparent)]
+ Fetch(#[from] FetchError),
+ #[error(transparent)]
+ Join(#[from] tokio::task::JoinError),
+ #[error(transparent)]
+ Other(deno_core::error::AnyError),
+}
+
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SyncFetchScript {
@@ -40,21 +64,22 @@ pub fn op_worker_sync_fetch(
state: &mut OpState,
#[serde] scripts: Vec<String>,
loose_mime_checks: bool,
-) -> Result<Vec<SyncFetchScript>, AnyError> {
+) -> Result<Vec<SyncFetchScript>, SyncFetchError> {
let handle = state.borrow::<WebWorkerInternalHandle>().clone();
assert_eq!(handle.worker_type, WebWorkerType::Classic);
// it's not safe to share a client across tokio runtimes, so create a fresh one
// https://github.com/seanmonstar/reqwest/issues/1148#issuecomment-910868788
let options = state.borrow::<deno_fetch::Options>().clone();
- let client = deno_fetch::create_client_from_options(&options)?;
+ let client = deno_fetch::create_client_from_options(&options)
+ .map_err(FetchError::ClientCreate)?;
// TODO(andreubotella) It's not good to throw an exception related to blob
// URLs when none of the script URLs use the blob scheme.
// Also, in which contexts are blob URLs not supported?
let blob_store = state
.try_borrow::<Arc<BlobStore>>()
- .ok_or_else(|| type_error("Blob URLs are not supported in this context."))?
+ .ok_or(SyncFetchError::BlobUrlsNotSupportedInContext)?
.clone();
// TODO(andreubotella): make the below thread into a resource that can be
@@ -74,7 +99,7 @@ pub fn op_worker_sync_fetch(
let blob_store = blob_store.clone();
deno_core::unsync::spawn(async move {
let script_url = Url::parse(&script)
- .map_err(|_| type_error("Invalid script URL"))?;
+ .map_err(|_| SyncFetchError::InvalidScriptUrl)?;
let mut loose_mime_checks = loose_mime_checks;
let (body, mime_type, res_url) = match script_url.scheme() {
@@ -86,15 +111,13 @@ pub fn op_worker_sync_fetch(
);
*req.uri_mut() = script_url.as_str().parse()?;
- let resp = client.send(req).await?;
+ let resp =
+ client.send(req).await.map_err(FetchError::ClientSend)?;
if resp.status().is_client_error()
|| resp.status().is_server_error()
{
- return Err(type_error(format!(
- "http status error: {}",
- resp.status()
- )));
+ return Err(SyncFetchError::InvalidStatusCode(resp.status()));
}
// TODO(andreubotella) Properly run fetch's "extract a MIME type".
@@ -107,30 +130,32 @@ pub fn op_worker_sync_fetch(
// Always check the MIME type with HTTP(S).
loose_mime_checks = false;
- let body = resp.collect().await?.to_bytes();
+ let body = resp
+ .collect()
+ .await
+ .map_err(SyncFetchError::Other)?
+ .to_bytes();
(body, mime_type, script)
}
"data" => {
- let data_url = DataUrl::process(&script)
- .map_err(|e| type_error(format!("{e:?}")))?;
+ let data_url =
+ DataUrl::process(&script).map_err(FetchError::DataUrl)?;
let mime_type = {
let mime = data_url.mime_type();
format!("{}/{}", mime.type_, mime.subtype)
};
- let (body, _) = data_url
- .decode_to_vec()
- .map_err(|e| type_error(format!("{e:?}")))?;
+ let (body, _) =
+ data_url.decode_to_vec().map_err(FetchError::Base64)?;
(Bytes::from(body), Some(mime_type), script)
}
"blob" => {
- let blob =
- blob_store.get_object_url(script_url).ok_or_else(|| {
- type_error("Blob for the given URL not found.")
- })?;
+ let blob = blob_store
+ .get_object_url(script_url)
+ .ok_or(FetchError::BlobNotFound)?;
let mime_type = mime_type_essence(&blob.media_type);
@@ -139,10 +164,11 @@ pub fn op_worker_sync_fetch(
(Bytes::from(body), Some(mime_type), script)
}
_ => {
- return Err(type_error(format!(
- "Classic scripts with scheme {}: are not supported in workers.",
- script_url.scheme()
- )))
+ return Err(
+ SyncFetchError::ClassicScriptSchemeUnsupportedInWorkers(
+ script_url.scheme().to_string(),
+ ),
+ )
}
};
@@ -151,17 +177,11 @@ pub fn op_worker_sync_fetch(
match mime_type.as_deref() {
Some("application/javascript" | "text/javascript") => {}
Some(mime_type) => {
- return Err(custom_error(
- "DOMExceptionNetworkError",
- format!("Invalid MIME type {mime_type:?}."),
- ))
- }
- None => {
- return Err(custom_error(
- "DOMExceptionNetworkError",
- "Missing MIME type.",
+ return Err(SyncFetchError::InvalidMimeType(
+ mime_type.to_string(),
))
}
+ None => return Err(SyncFetchError::MissingMimeType),
}
}
diff --git a/runtime/ops/worker_host.rs b/runtime/ops/worker_host.rs
index d85541d51..82cc94924 100644
--- a/runtime/ops/worker_host.rs
+++ b/runtime/ops/worker_host.rs
@@ -10,8 +10,6 @@ use crate::web_worker::WorkerControlEvent;
use crate::web_worker::WorkerId;
use crate::web_worker::WorkerMetadata;
use crate::worker::FormatJsErrorFn;
-use deno_core::error::custom_error;
-use deno_core::error::AnyError;
use deno_core::op2;
use deno_core::serde::Deserialize;
use deno_core::CancelFuture;
@@ -22,6 +20,7 @@ use deno_permissions::ChildPermissionsArg;
use deno_permissions::PermissionsContainer;
use deno_web::deserialize_js_transferables;
use deno_web::JsMessageData;
+use deno_web::MessagePortError;
use log::debug;
use std::cell::RefCell;
use std::collections::HashMap;
@@ -119,6 +118,20 @@ pub struct CreateWorkerArgs {
close_on_idle: bool,
}
+#[derive(Debug, thiserror::Error)]
+pub enum CreateWorkerError {
+ #[error("Classic workers are not supported.")]
+ ClassicWorkers,
+ #[error(transparent)]
+ Permission(deno_core::error::AnyError),
+ #[error(transparent)]
+ ModuleResolution(#[from] deno_core::ModuleResolutionError),
+ #[error(transparent)]
+ MessagePort(#[from] MessagePortError),
+ #[error("{0}")]
+ Io(#[from] std::io::Error),
+}
+
/// Create worker as the host
#[op2]
#[serde]
@@ -126,7 +139,7 @@ fn op_create_worker(
state: &mut OpState,
#[serde] args: CreateWorkerArgs,
#[serde] maybe_worker_metadata: Option<JsMessageData>,
-) -> Result<WorkerId, AnyError> {
+) -> Result<WorkerId, CreateWorkerError> {
let specifier = args.specifier.clone();
let maybe_source_code = if args.has_source_code {
Some(args.source_code.clone())
@@ -137,10 +150,7 @@ fn op_create_worker(
let worker_type = args.worker_type;
if let WebWorkerType::Classic = worker_type {
if let TestingFeaturesEnabled(false) = state.borrow() {
- return Err(custom_error(
- "DOMExceptionNotSupportedError",
- "Classic workers are not supported.",
- ));
+ return Err(CreateWorkerError::ClassicWorkers);
}
}
@@ -154,7 +164,9 @@ fn op_create_worker(
let parent_permissions = state.borrow_mut::<PermissionsContainer>();
let worker_permissions = if let Some(child_permissions_arg) = args.permissions
{
- parent_permissions.create_child_permissions(child_permissions_arg)?
+ parent_permissions
+ .create_child_permissions(child_permissions_arg)
+ .map_err(CreateWorkerError::Permission)?
} else {
parent_permissions.clone()
};
@@ -166,9 +178,8 @@ fn op_create_worker(
let module_specifier = deno_core::resolve_url(&specifier)?;
let worker_name = args_name.unwrap_or_default();
- let (handle_sender, handle_receiver) = std::sync::mpsc::sync_channel::<
- Result<SendableWebWorkerHandle, AnyError>,
- >(1);
+ let (handle_sender, handle_receiver) =
+ std::sync::mpsc::sync_channel::<SendableWebWorkerHandle>(1);
// Setup new thread
let thread_builder = std::thread::Builder::new().name(format!("{worker_id}"));
@@ -202,7 +213,7 @@ fn op_create_worker(
});
// Send thread safe handle from newly created worker to host thread
- handle_sender.send(Ok(external_handle)).unwrap();
+ handle_sender.send(external_handle).unwrap();
drop(handle_sender);
// At this point the only method of communication with host
@@ -218,7 +229,7 @@ fn op_create_worker(
})?;
// Receive WebWorkerHandle from newly created worker
- let worker_handle = handle_receiver.recv().unwrap()?;
+ let worker_handle = handle_receiver.recv().unwrap();
let worker_thread = WorkerThread {
worker_handle: worker_handle.into(),
@@ -291,7 +302,7 @@ fn close_channel(
async fn op_host_recv_ctrl(
state: Rc<RefCell<OpState>>,
#[serde] id: WorkerId,
-) -> Result<WorkerControlEvent, AnyError> {
+) -> WorkerControlEvent {
let (worker_handle, cancel_handle) = {
let state = state.borrow();
let workers_table = state.borrow::<WorkersTable>();
@@ -300,7 +311,7 @@ async fn op_host_recv_ctrl(
(handle.worker_handle.clone(), handle.cancel_handle.clone())
} else {
// If handle was not found it means worker has already shutdown
- return Ok(WorkerControlEvent::Close);
+ return WorkerControlEvent::Close;
}
};
@@ -309,22 +320,21 @@ async fn op_host_recv_ctrl(
.or_cancel(cancel_handle)
.await;
match maybe_event {
- Ok(Ok(Some(event))) => {
+ Ok(Some(event)) => {
// Terminal error means that worker should be removed from worker table.
if let WorkerControlEvent::TerminalError(_) = &event {
close_channel(state, id, WorkerChannel::Ctrl);
}
- Ok(event)
+ event
}
- Ok(Ok(None)) => {
+ Ok(None) => {
// If there was no event from worker it means it has already been closed.
close_channel(state, id, WorkerChannel::Ctrl);
- Ok(WorkerControlEvent::Close)
+ WorkerControlEvent::Close
}
- Ok(Err(err)) => Err(err),
Err(_) => {
// The worker was terminated.
- Ok(WorkerControlEvent::Close)
+ WorkerControlEvent::Close
}
}
}
@@ -334,7 +344,7 @@ async fn op_host_recv_ctrl(
async fn op_host_recv_message(
state: Rc<RefCell<OpState>>,
#[serde] id: WorkerId,
-) -> Result<Option<JsMessageData>, AnyError> {
+) -> Result<Option<JsMessageData>, MessagePortError> {
let (worker_handle, cancel_handle) = {
let s = state.borrow();
let workers_table = s.borrow::<WorkersTable>();
@@ -359,7 +369,7 @@ async fn op_host_recv_message(
}
Ok(ret)
}
- Ok(Err(err)) => Err(err.into()),
+ Ok(Err(err)) => Err(err),
Err(_) => {
// The worker was terminated.
Ok(None)
@@ -373,7 +383,7 @@ fn op_host_post_message(
state: &mut OpState,
#[serde] id: WorkerId,
#[serde] data: JsMessageData,
-) -> Result<(), AnyError> {
+) -> Result<(), MessagePortError> {
if let Some(worker_thread) = state.borrow::<WorkersTable>().get(&id) {
debug!("post message to worker {}", id);
let worker_handle = worker_thread.worker_handle.clone();
diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs
index f560ce17e..04cd3305e 100644
--- a/runtime/web_worker.rs
+++ b/runtime/web_worker.rs
@@ -166,7 +166,10 @@ pub struct WebWorkerInternalHandle {
impl WebWorkerInternalHandle {
/// Post WorkerEvent to parent as a worker
- pub fn post_event(&self, event: WorkerControlEvent) -> Result<(), AnyError> {
+ pub fn post_event(
+ &self,
+ event: WorkerControlEvent,
+ ) -> Result<(), mpsc::TrySendError<WorkerControlEvent>> {
let mut sender = self.sender.clone();
// If the channel is closed,
// the worker must have terminated but the termination message has not yet been received.
@@ -176,8 +179,7 @@ impl WebWorkerInternalHandle {
self.has_terminated.store(true, Ordering::SeqCst);
return Ok(());
}
- sender.try_send(event)?;
- Ok(())
+ sender.try_send(event)
}
/// Check if this worker is terminated or being terminated
@@ -263,11 +265,9 @@ impl WebWorkerHandle {
/// Get the WorkerEvent with lock
/// Return error if more than one listener tries to get event
#[allow(clippy::await_holding_refcell_ref)] // TODO(ry) remove!
- pub async fn get_control_event(
- &self,
- ) -> Result<Option<WorkerControlEvent>, AnyError> {
+ pub async fn get_control_event(&self) -> Option<WorkerControlEvent> {
let mut receiver = self.receiver.borrow_mut();
- Ok(receiver.next().await)
+ receiver.next().await
}
/// Terminate the worker