Merge pull request #2550 from bytecodealliance/pch/wiggle_trapping
wiggle: introduce Trap enum
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -2909,7 +2909,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "witx"
|
name = "witx"
|
||||||
version = "0.8.7"
|
version = "0.8.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"diff",
|
"diff",
|
||||||
|
|||||||
Submodule crates/wasi-common/WASI updated: 71f0425147...8deb71ddd0
@@ -17,6 +17,10 @@ pub enum Error {
|
|||||||
#[error("GetRandom: {0}")]
|
#[error("GetRandom: {0}")]
|
||||||
GetRandom(#[from] getrandom::Error),
|
GetRandom(#[from] getrandom::Error),
|
||||||
|
|
||||||
|
/// Some corners of the WASI standard are unsupported.
|
||||||
|
#[error("Unsupported: {0}")]
|
||||||
|
Unsupported(&'static str),
|
||||||
|
|
||||||
/// The host OS may return an io error that doesn't match one of the
|
/// The host OS may return an io error that doesn't match one of the
|
||||||
/// wasi errno variants we expect. We do not expose the details of this
|
/// wasi errno variants we expect. We do not expose the details of this
|
||||||
/// error to the user.
|
/// error to the user.
|
||||||
|
|||||||
@@ -714,14 +714,17 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
|
|||||||
Ok(nevents)
|
Ok(nevents)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn proc_exit(&self, _rval: types::Exitcode) -> std::result::Result<(), ()> {
|
fn proc_exit(&self, status: types::Exitcode) -> wiggle::Trap {
|
||||||
// proc_exit is special in that it's expected to unwind the stack, which
|
// Check that the status is within WASI's range.
|
||||||
// typically requires runtime-specific logic.
|
if status < 126 {
|
||||||
unimplemented!("runtimes are expected to override this implementation")
|
wiggle::Trap::I32Exit(status as i32)
|
||||||
|
} else {
|
||||||
|
wiggle::Trap::String("exit with invalid exit status outside of [0..126)".to_owned())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn proc_raise(&self, _sig: types::Signal) -> Result<()> {
|
fn proc_raise(&self, _sig: types::Signal) -> Result<()> {
|
||||||
unimplemented!("proc_raise")
|
Err(Error::Unsupported("proc_raise"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sched_yield(&self) -> Result<()> {
|
fn sched_yield(&self) -> Result<()> {
|
||||||
@@ -741,7 +744,7 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
|
|||||||
_ri_data: &types::IovecArray<'_>,
|
_ri_data: &types::IovecArray<'_>,
|
||||||
_ri_flags: types::Riflags,
|
_ri_flags: types::Riflags,
|
||||||
) -> Result<(types::Size, types::Roflags)> {
|
) -> Result<(types::Size, types::Roflags)> {
|
||||||
unimplemented!("sock_recv")
|
Err(Error::Unsupported("sock_recv"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sock_send(
|
fn sock_send(
|
||||||
@@ -750,11 +753,11 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
|
|||||||
_si_data: &types::CiovecArray<'_>,
|
_si_data: &types::CiovecArray<'_>,
|
||||||
_si_flags: types::Siflags,
|
_si_flags: types::Siflags,
|
||||||
) -> Result<types::Size> {
|
) -> Result<types::Size> {
|
||||||
unimplemented!("sock_send")
|
Err(Error::Unsupported("sock_send"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sock_shutdown(&self, _fd: types::Fd, _how: types::Sdflags) -> Result<()> {
|
fn sock_shutdown(&self, _fd: types::Fd, _how: types::Sdflags) -> Result<()> {
|
||||||
unimplemented!("sock_shutdown")
|
Err(Error::Unsupported("sock_shutdown"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -801,7 +804,7 @@ impl WasiCtx {
|
|||||||
Err(error) => {
|
Err(error) => {
|
||||||
events.push(types::Event {
|
events.push(types::Event {
|
||||||
userdata: subscription.userdata,
|
userdata: subscription.userdata,
|
||||||
error: error.into(),
|
error: error.try_into().expect("non-trapping error"),
|
||||||
type_: types::Eventtype::FdRead,
|
type_: types::Eventtype::FdRead,
|
||||||
fd_readwrite: types::EventFdReadwrite {
|
fd_readwrite: types::EventFdReadwrite {
|
||||||
nbytes: 0,
|
nbytes: 0,
|
||||||
@@ -827,7 +830,7 @@ impl WasiCtx {
|
|||||||
Err(error) => {
|
Err(error) => {
|
||||||
events.push(types::Event {
|
events.push(types::Event {
|
||||||
userdata: subscription.userdata,
|
userdata: subscription.userdata,
|
||||||
error: error.into(),
|
error: error.try_into().expect("non-trapping error"),
|
||||||
type_: types::Eventtype::FdWrite,
|
type_: types::Eventtype::FdWrite,
|
||||||
fd_readwrite: types::EventFdReadwrite {
|
fd_readwrite: types::EventFdReadwrite {
|
||||||
nbytes: 0,
|
nbytes: 0,
|
||||||
|
|||||||
@@ -23,15 +23,16 @@ impl types::GuestErrorConversion for WasiCtx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl types::UserErrorConversion for WasiCtx {
|
impl types::UserErrorConversion for WasiCtx {
|
||||||
fn errno_from_error(&self, e: Error) -> Result<Errno, String> {
|
fn errno_from_error(&self, e: Error) -> Result<Errno, wiggle::Trap> {
|
||||||
tracing::debug!("Error: {:?}", e);
|
tracing::debug!("Error: {:?}", e);
|
||||||
Ok(e.into())
|
e.try_into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Error> for Errno {
|
impl TryFrom<Error> for Errno {
|
||||||
fn from(e: Error) -> Errno {
|
type Error = wiggle::Trap;
|
||||||
types_new::Errno::from(e).into()
|
fn try_from(e: Error) -> Result<Errno, wiggle::Trap> {
|
||||||
|
Ok(types_new::Errno::try_from(e)?.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -346,7 +347,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
|
|||||||
Ok(nevents)
|
Ok(nevents)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn proc_exit(&self, rval: Exitcode) -> Result<(), ()> {
|
fn proc_exit(&self, rval: Exitcode) -> wiggle::Trap {
|
||||||
WasiSnapshotPreview1::proc_exit(self, rval)
|
WasiSnapshotPreview1::proc_exit(self, rval)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ fn handle_fd_event(
|
|||||||
let output_event = if revents.contains(PollFlags::POLLNVAL) {
|
let output_event = if revents.contains(PollFlags::POLLNVAL) {
|
||||||
Event {
|
Event {
|
||||||
userdata: fd_event.userdata,
|
userdata: fd_event.userdata,
|
||||||
error: Error::Badf.into(),
|
error: Error::Badf.try_into().unwrap(),
|
||||||
type_: fd_event.r#type,
|
type_: fd_event.r#type,
|
||||||
fd_readwrite: EventFdReadwrite {
|
fd_readwrite: EventFdReadwrite {
|
||||||
nbytes: 0,
|
nbytes: 0,
|
||||||
@@ -127,7 +127,7 @@ fn handle_fd_event(
|
|||||||
} else if revents.contains(PollFlags::POLLERR) {
|
} else if revents.contains(PollFlags::POLLERR) {
|
||||||
Event {
|
Event {
|
||||||
userdata: fd_event.userdata,
|
userdata: fd_event.userdata,
|
||||||
error: Error::Io.into(),
|
error: Error::Io.try_into().unwrap(),
|
||||||
type_: fd_event.r#type,
|
type_: fd_event.r#type,
|
||||||
fd_readwrite: EventFdReadwrite {
|
fd_readwrite: EventFdReadwrite {
|
||||||
nbytes: 0,
|
nbytes: 0,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use crate::sys::stdio::{Stderr, Stdin, Stdout};
|
|||||||
use crate::sys::AsFile;
|
use crate::sys::AsFile;
|
||||||
use crate::{Error, Result};
|
use crate::{Error, Result};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use std::convert::TryInto;
|
use std::convert::{TryFrom, TryInto};
|
||||||
use std::sync::mpsc::{self, Receiver, RecvTimeoutError, Sender, TryRecvError};
|
use std::sync::mpsc::{self, Receiver, RecvTimeoutError, Sender, TryRecvError};
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
@@ -81,7 +81,9 @@ impl StdinPoll {
|
|||||||
// Linux returns `POLLIN` in both cases, and we imitate this behavior.
|
// Linux returns `POLLIN` in both cases, and we imitate this behavior.
|
||||||
let resp = match std::io::stdin().lock().fill_buf() {
|
let resp = match std::io::stdin().lock().fill_buf() {
|
||||||
Ok(_) => PollState::Ready,
|
Ok(_) => PollState::Ready,
|
||||||
Err(e) => PollState::Error(Errno::from(Error::from(e))),
|
Err(e) => {
|
||||||
|
PollState::Error(Errno::try_from(Error::from(e)).expect("non-trapping error"))
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Notify the requestor about data in stdin. They may have already timed out,
|
// Notify the requestor about data in stdin. They may have already timed out,
|
||||||
@@ -158,7 +160,9 @@ fn handle_rw_event(event: FdEventData, out_events: &mut Vec<Event>) {
|
|||||||
.as_file()
|
.as_file()
|
||||||
.and_then(|f| f.metadata())
|
.and_then(|f| f.metadata())
|
||||||
.map(|m| m.len())
|
.map(|m| m.len())
|
||||||
.map_err(|ioerror| Errno::from(Error::from(ioerror)))
|
.map_err(|ioerror| {
|
||||||
|
Errno::try_from(Error::from(ioerror)).expect("non-trapping error")
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
// The spec is unclear what nbytes should actually be for __WASI_EVENTTYPE_FD_WRITE and
|
// The spec is unclear what nbytes should actually be for __WASI_EVENTTYPE_FD_WRITE and
|
||||||
// the implementation on Unix just returns 0 here, so it's probably fine
|
// the implementation on Unix just returns 0 here, so it's probably fine
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use crate::{Error, WasiCtx};
|
use crate::{Error, WasiCtx};
|
||||||
|
use std::convert::{TryFrom, TryInto};
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
wiggle::from_witx!({
|
wiggle::from_witx!({
|
||||||
@@ -23,47 +24,51 @@ impl types::GuestErrorConversion for WasiCtx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl types::UserErrorConversion for WasiCtx {
|
impl types::UserErrorConversion for WasiCtx {
|
||||||
fn errno_from_error(&self, e: Error) -> Result<Errno, String> {
|
fn errno_from_error(&self, e: Error) -> Result<Errno, wiggle::Trap> {
|
||||||
debug!("Error: {:?}", e);
|
debug!("Error: {:?}", e);
|
||||||
Ok(e.into())
|
e.try_into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Error> for Errno {
|
impl TryFrom<Error> for Errno {
|
||||||
fn from(e: Error) -> Errno {
|
type Error = wiggle::Trap;
|
||||||
|
fn try_from(e: Error) -> Result<Errno, wiggle::Trap> {
|
||||||
match e {
|
match e {
|
||||||
Error::Guest(e) => e.into(),
|
Error::Guest(e) => Ok(e.into()),
|
||||||
Error::TryFromInt(_) => Errno::Overflow,
|
Error::TryFromInt(_) => Ok(Errno::Overflow),
|
||||||
Error::Utf8(_) => Errno::Ilseq,
|
Error::Utf8(_) => Ok(Errno::Ilseq),
|
||||||
Error::UnexpectedIo(_) => Errno::Io,
|
Error::UnexpectedIo(_) => Ok(Errno::Io),
|
||||||
Error::GetRandom(_) => Errno::Io,
|
Error::GetRandom(_) => Ok(Errno::Io),
|
||||||
Error::TooBig => Errno::TooBig,
|
Error::TooBig => Ok(Errno::TooBig),
|
||||||
Error::Acces => Errno::Acces,
|
Error::Acces => Ok(Errno::Acces),
|
||||||
Error::Badf => Errno::Badf,
|
Error::Badf => Ok(Errno::Badf),
|
||||||
Error::Busy => Errno::Busy,
|
Error::Busy => Ok(Errno::Busy),
|
||||||
Error::Exist => Errno::Exist,
|
Error::Exist => Ok(Errno::Exist),
|
||||||
Error::Fault => Errno::Fault,
|
Error::Fault => Ok(Errno::Fault),
|
||||||
Error::Fbig => Errno::Fbig,
|
Error::Fbig => Ok(Errno::Fbig),
|
||||||
Error::Ilseq => Errno::Ilseq,
|
Error::Ilseq => Ok(Errno::Ilseq),
|
||||||
Error::Inval => Errno::Inval,
|
Error::Inval => Ok(Errno::Inval),
|
||||||
Error::Io => Errno::Io,
|
Error::Io => Ok(Errno::Io),
|
||||||
Error::Isdir => Errno::Isdir,
|
Error::Isdir => Ok(Errno::Isdir),
|
||||||
Error::Loop => Errno::Loop,
|
Error::Loop => Ok(Errno::Loop),
|
||||||
Error::Mfile => Errno::Mfile,
|
Error::Mfile => Ok(Errno::Mfile),
|
||||||
Error::Mlink => Errno::Mlink,
|
Error::Mlink => Ok(Errno::Mlink),
|
||||||
Error::Nametoolong => Errno::Nametoolong,
|
Error::Nametoolong => Ok(Errno::Nametoolong),
|
||||||
Error::Nfile => Errno::Nfile,
|
Error::Nfile => Ok(Errno::Nfile),
|
||||||
Error::Noent => Errno::Noent,
|
Error::Noent => Ok(Errno::Noent),
|
||||||
Error::Nomem => Errno::Nomem,
|
Error::Nomem => Ok(Errno::Nomem),
|
||||||
Error::Nospc => Errno::Nospc,
|
Error::Nospc => Ok(Errno::Nospc),
|
||||||
Error::Notdir => Errno::Notdir,
|
Error::Notdir => Ok(Errno::Notdir),
|
||||||
Error::Notempty => Errno::Notempty,
|
Error::Notempty => Ok(Errno::Notempty),
|
||||||
Error::Notsup => Errno::Notsup,
|
Error::Notsup => Ok(Errno::Notsup),
|
||||||
Error::Overflow => Errno::Overflow,
|
Error::Overflow => Ok(Errno::Overflow),
|
||||||
Error::Pipe => Errno::Pipe,
|
Error::Pipe => Ok(Errno::Pipe),
|
||||||
Error::Perm => Errno::Perm,
|
Error::Perm => Ok(Errno::Perm),
|
||||||
Error::Spipe => Errno::Spipe,
|
Error::Spipe => Ok(Errno::Spipe),
|
||||||
Error::Notcapable => Errno::Notcapable,
|
Error::Notcapable => Ok(Errno::Notcapable),
|
||||||
|
Error::Unsupported(feature) => {
|
||||||
|
Err(wiggle::Trap::String(format!("unsupported: {}", feature)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,8 +85,8 @@ impl From<wiggle::GuestError> for Errno {
|
|||||||
PtrBorrowed { .. } => Self::Fault,
|
PtrBorrowed { .. } => Self::Fault,
|
||||||
InvalidUtf8 { .. } => Self::Ilseq,
|
InvalidUtf8 { .. } => Self::Ilseq,
|
||||||
TryFromIntError { .. } => Self::Overflow,
|
TryFromIntError { .. } => Self::Overflow,
|
||||||
InFunc { .. } => Self::Inval,
|
InFunc { err, .. } => Errno::from(*err),
|
||||||
InDataField { .. } => Self::Inval,
|
InDataField { err, .. } => Errno::from(*err),
|
||||||
SliceLengthsDiffer { .. } => Self::Fault,
|
SliceLengthsDiffer { .. } => Self::Fault,
|
||||||
BorrowCheckerOutOfHandles { .. } => Self::Fault,
|
BorrowCheckerOutOfHandles { .. } => Self::Fault,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ impl types::GuestErrorConversion for WasiNnCtx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> types::UserErrorConversion for WasiNnCtx {
|
impl<'a> types::UserErrorConversion for WasiNnCtx {
|
||||||
fn errno_from_wasi_nn_error(&self, e: WasiNnError) -> Result<Errno, String> {
|
fn errno_from_wasi_nn_error(&self, e: WasiNnError) -> Result<Errno, wiggle::Trap> {
|
||||||
eprintln!("Host error: {:?}", e);
|
eprintln!("Host error: {:?}", e);
|
||||||
match e {
|
match e {
|
||||||
WasiNnError::OpenvinoError(_) => unimplemented!(),
|
WasiNnError::OpenvinoError(_) => unimplemented!(),
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
use wasmtime::Trap;
|
|
||||||
|
|
||||||
pub mod old;
|
pub mod old;
|
||||||
|
|
||||||
pub use wasi_common::virtfs;
|
pub use wasi_common::virtfs;
|
||||||
@@ -26,12 +24,7 @@ modules. This structure exports all that various fields of the wasi instance
|
|||||||
as fields which can be used to implement your own instantiation logic, if
|
as fields which can be used to implement your own instantiation logic, if
|
||||||
necessary. Additionally [`Wasi::get_export`] can be used to do name-based
|
necessary. Additionally [`Wasi::get_export`] can be used to do name-based
|
||||||
resolution.",
|
resolution.",
|
||||||
// Don't use the wiggle generated code to implement proc_exit, we need
|
}
|
||||||
// to hook directly into the runtime there:
|
|
||||||
function_override: {
|
|
||||||
proc_exit => wasi_proc_exit
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -41,17 +34,3 @@ pub fn is_wasi_module(name: &str) -> bool {
|
|||||||
// trick.
|
// trick.
|
||||||
name.starts_with("wasi")
|
name.starts_with("wasi")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implement the WASI `proc_exit` function. This function is implemented here
|
|
||||||
/// instead of in wasi-common so that we can use the runtime to perform an
|
|
||||||
/// unwind rather than exiting the host process.
|
|
||||||
fn wasi_proc_exit(status: i32) -> Result<(), Trap> {
|
|
||||||
// Check that the status is within WASI's range.
|
|
||||||
if status >= 0 && status < 126 {
|
|
||||||
Err(Trap::i32_exit(status))
|
|
||||||
} else {
|
|
||||||
Err(Trap::new(
|
|
||||||
"exit with invalid exit status outside of [0..126)",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
pub use wasi_common::virtfs;
|
pub use wasi_common::virtfs;
|
||||||
pub use wasi_common::{WasiCtx, WasiCtxBuilder};
|
pub use wasi_common::{WasiCtx, WasiCtxBuilder};
|
||||||
|
|
||||||
use crate::wasi_proc_exit;
|
|
||||||
|
|
||||||
// Defines a `struct Wasi` with member fields and appropriate APIs for dealing
|
// Defines a `struct Wasi` with member fields and appropriate APIs for dealing
|
||||||
// with all the various WASI exports.
|
// with all the various WASI exports.
|
||||||
wasmtime_wiggle::wasmtime_integration!({
|
wasmtime_wiggle::wasmtime_integration!({
|
||||||
@@ -25,12 +23,7 @@ modules. This structure exports all that various fields of the wasi instance
|
|||||||
as fields which can be used to implement your own instantiation logic, if
|
as fields which can be used to implement your own instantiation logic, if
|
||||||
necessary. Additionally [`Wasi::get_export`] can be used to do name-based
|
necessary. Additionally [`Wasi::get_export`] can be used to do name-based
|
||||||
resolution.",
|
resolution.",
|
||||||
// Don't use the wiggle generated code to implement proc_exit, we need
|
}
|
||||||
// to hook directly into the runtime there:
|
|
||||||
function_override: {
|
|
||||||
proc_exit => wasi_proc_exit
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -5,3 +5,14 @@ invoked as a Rust procedural macro.
|
|||||||
|
|
||||||
Wiggle is not specialized to any particular WebAssembly runtime. It is usable
|
Wiggle is not specialized to any particular WebAssembly runtime. It is usable
|
||||||
in at least Wasmtime and Lucet.
|
in at least Wasmtime and Lucet.
|
||||||
|
|
||||||
|
## Learning more
|
||||||
|
|
||||||
|
Read the docs on [docs.rs](https://docs.rs/wiggle/).
|
||||||
|
|
||||||
|
There are child crates for [integrating with Wasmtime](https://github.com/bytecodealliance/wasmtime/tree/main/crates/wiggle/wasmtime) and [Lucet](https://github.com/bytecodealliance/lucet/tree/main/lucet-wiggle).
|
||||||
|
|
||||||
|
The [wasi-common crate](https://github.com/bytecodealliance/wasmtime/tree/main/crates/wasi-common) is implemented using Wiggle and the [wasmtime-wasi
|
||||||
|
crate](https://github.com/bytecodealliance/wasmtime/tree/main/crates/wasi) integrates wasi-common with the Wasmtime engine.
|
||||||
|
|
||||||
|
Andrew Brown wrote a great [blog post](https://bytecodealliance.org/articles/implementing-wasi-nn-in-wasmtime) on using Wiggle with Wasmtime.
|
||||||
|
|||||||
@@ -36,12 +36,6 @@ pub fn define_func(
|
|||||||
witx::CoreParamSignifies::Value(atom) => names.atom_type(atom),
|
witx::CoreParamSignifies::Value(atom) => names.atom_type(atom),
|
||||||
_ => unreachable!("ret should always be passed by value"),
|
_ => unreachable!("ret should always be passed by value"),
|
||||||
}
|
}
|
||||||
} else if func.noreturn {
|
|
||||||
// Ideally we would return `quote!(!)` here, but, we'd have to change
|
|
||||||
// the error handling logic in all the marshalling code to never return,
|
|
||||||
// and instead provide some other way to bail to the context...
|
|
||||||
// noreturn func
|
|
||||||
unimplemented!("noreturn funcs not supported yet!")
|
|
||||||
} else {
|
} else {
|
||||||
quote!(())
|
quote!(())
|
||||||
};
|
};
|
||||||
@@ -174,25 +168,42 @@ pub fn define_func(
|
|||||||
let mod_name = &module.name.as_str();
|
let mod_name = &module.name.as_str();
|
||||||
let func_name = &func.name.as_str();
|
let func_name = &func.name.as_str();
|
||||||
|
|
||||||
quote!(pub fn #ident(#abi_args) -> Result<#abi_ret, String> {
|
if func.noreturn {
|
||||||
let _span = #rt::tracing::span!(
|
quote!(pub fn #ident(#abi_args) -> Result<#abi_ret, wiggle::Trap> {
|
||||||
#rt::tracing::Level::TRACE,
|
let _span = #rt::tracing::span!(
|
||||||
"wiggle abi",
|
#rt::tracing::Level::TRACE,
|
||||||
module = #mod_name,
|
"wiggle abi",
|
||||||
function = #func_name
|
module = #mod_name,
|
||||||
);
|
function = #func_name
|
||||||
let _enter = _span.enter();
|
);
|
||||||
|
let _enter = _span.enter();
|
||||||
|
|
||||||
#(#marshal_args)*
|
#(#marshal_args)*
|
||||||
#(#marshal_rets_pre)*
|
#log_marshalled_args
|
||||||
#log_marshalled_args
|
let trap = #trait_name::#ident(ctx, #(#trait_args),*);
|
||||||
let #trait_bindings = match #trait_name::#ident(ctx, #(#trait_args),*) {
|
Err(trap)
|
||||||
Ok(#trait_bindings) => { #trait_rets },
|
})
|
||||||
Err(e) => { #ret_err },
|
} else {
|
||||||
};
|
quote!(pub fn #ident(#abi_args) -> Result<#abi_ret, wiggle::Trap> {
|
||||||
#(#marshal_rets_post)*
|
let _span = #rt::tracing::span!(
|
||||||
#success
|
#rt::tracing::Level::TRACE,
|
||||||
})
|
"wiggle abi",
|
||||||
|
module = #mod_name,
|
||||||
|
function = #func_name
|
||||||
|
);
|
||||||
|
let _enter = _span.enter();
|
||||||
|
|
||||||
|
#(#marshal_args)*
|
||||||
|
#(#marshal_rets_pre)*
|
||||||
|
#log_marshalled_args
|
||||||
|
let #trait_bindings = match #trait_name::#ident(ctx, #(#trait_args),*) {
|
||||||
|
Ok(#trait_bindings) => { #trait_rets },
|
||||||
|
Err(e) => { #ret_err },
|
||||||
|
};
|
||||||
|
#(#marshal_rets_post)*
|
||||||
|
#success
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn marshal_arg(
|
fn marshal_arg(
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ pub fn generate(doc: &witx::Document, names: &Names, errs: &ErrorTransform) -> T
|
|||||||
let abi_typename = names.type_ref(&errtype.abi_type(), anon_lifetime());
|
let abi_typename = names.type_ref(&errtype.abi_type(), anon_lifetime());
|
||||||
let user_typename = errtype.typename();
|
let user_typename = errtype.typename();
|
||||||
let methodname = names.user_error_conversion_method(&errtype);
|
let methodname = names.user_error_conversion_method(&errtype);
|
||||||
quote!(fn #methodname(&self, e: super::#user_typename) -> Result<#abi_typename, String>;)
|
quote!(fn #methodname(&self, e: super::#user_typename) -> Result<#abi_typename, wiggle::Trap>;)
|
||||||
});
|
});
|
||||||
let user_error_conversion = quote! {
|
let user_error_conversion = quote! {
|
||||||
pub trait UserErrorConversion {
|
pub trait UserErrorConversion {
|
||||||
|
|||||||
@@ -48,28 +48,34 @@ pub fn define_module_trait(names: &Names, m: &Module, errxform: &ErrorTransform)
|
|||||||
};
|
};
|
||||||
quote!(#arg_name: #arg_type)
|
quote!(#arg_name: #arg_type)
|
||||||
});
|
});
|
||||||
let rets = f
|
|
||||||
.results
|
let result = if !f.noreturn {
|
||||||
.iter()
|
let rets = f
|
||||||
.skip(1)
|
.results
|
||||||
.map(|ret| names.type_ref(&ret.tref, lifetime.clone()));
|
.iter()
|
||||||
let err = f
|
.skip(1)
|
||||||
.results
|
.map(|ret| names.type_ref(&ret.tref, lifetime.clone()));
|
||||||
.get(0)
|
let err = f
|
||||||
.map(|err_result| {
|
.results
|
||||||
if let Some(custom_err) = errxform.for_abi_error(&err_result.tref) {
|
.get(0)
|
||||||
let tn = custom_err.typename();
|
.map(|err_result| {
|
||||||
quote!(super::#tn)
|
if let Some(custom_err) = errxform.for_abi_error(&err_result.tref) {
|
||||||
} else {
|
let tn = custom_err.typename();
|
||||||
names.type_ref(&err_result.tref, lifetime.clone())
|
quote!(super::#tn)
|
||||||
}
|
} else {
|
||||||
})
|
names.type_ref(&err_result.tref, lifetime.clone())
|
||||||
.unwrap_or(quote!(()));
|
}
|
||||||
|
})
|
||||||
|
.unwrap_or(quote!(()));
|
||||||
|
quote!( Result<(#(#rets),*), #err> )
|
||||||
|
} else {
|
||||||
|
quote!(wiggle::Trap)
|
||||||
|
};
|
||||||
|
|
||||||
if is_anonymous {
|
if is_anonymous {
|
||||||
quote!(fn #funcname(&self, #(#args),*) -> Result<(#(#rets),*), #err>;)
|
quote!(fn #funcname(&self, #(#args),*) -> #result; )
|
||||||
} else {
|
} else {
|
||||||
quote!(fn #funcname<#lifetime>(&self, #(#args),*) -> Result<(#(#rets),*), #err>;)
|
quote!(fn #funcname<#lifetime>(&self, #(#args),*) -> #result;)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
quote! {
|
quote! {
|
||||||
|
|||||||
@@ -928,3 +928,16 @@ impl Pointee for str {
|
|||||||
<[u8]>::debug(pointer, f)
|
<[u8]>::debug(pointer, f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A runtime-independent way for Wiggle to terminate WebAssembly execution.
|
||||||
|
/// Functions that are marked `(@witx noreturn)` will always return a Trap.
|
||||||
|
/// Other functions that want to Trap can do so via their `UserErrorConversion`
|
||||||
|
/// trait, which transforms the user's own error type into a `Result<abierror, Trap>`.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum Trap {
|
||||||
|
/// A Trap which indicates an i32 (posix-style) exit code. Runtimes may have a
|
||||||
|
/// special way of dealing with this for WASI embeddings and otherwise.
|
||||||
|
I32Exit(i32),
|
||||||
|
/// Any other Trap is just an unstructured String, for reporting and debugging.
|
||||||
|
String(String),
|
||||||
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ impl_errno!(types::Errno, types::GuestErrorConversion);
|
|||||||
/// When the `errors` mapping in witx is non-empty, we need to impl the
|
/// When the `errors` mapping in witx is non-empty, we need to impl the
|
||||||
/// types::UserErrorConversion trait that wiggle generates from that mapping.
|
/// types::UserErrorConversion trait that wiggle generates from that mapping.
|
||||||
impl<'a> types::UserErrorConversion for WasiCtx<'a> {
|
impl<'a> types::UserErrorConversion for WasiCtx<'a> {
|
||||||
fn errno_from_rich_error(&self, e: RichError) -> Result<types::Errno, String> {
|
fn errno_from_rich_error(&self, e: RichError) -> Result<types::Errno, wiggle::Trap> {
|
||||||
wiggle::tracing::debug!(
|
wiggle::tracing::debug!(
|
||||||
rich_error = wiggle::tracing::field::debug(&e),
|
rich_error = wiggle::tracing::field::debug(&e),
|
||||||
"error conversion"
|
"error conversion"
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ mod convert_just_errno {
|
|||||||
/// When the `errors` mapping in witx is non-empty, we need to impl the
|
/// When the `errors` mapping in witx is non-empty, we need to impl the
|
||||||
/// types::UserErrorConversion trait that wiggle generates from that mapping.
|
/// types::UserErrorConversion trait that wiggle generates from that mapping.
|
||||||
impl<'a> types::UserErrorConversion for WasiCtx<'a> {
|
impl<'a> types::UserErrorConversion for WasiCtx<'a> {
|
||||||
fn errno_from_rich_error(&self, e: RichError) -> Result<types::Errno, String> {
|
fn errno_from_rich_error(&self, e: RichError) -> Result<types::Errno, wiggle::Trap> {
|
||||||
// WasiCtx can collect a Vec<String> log so we can test this. We're
|
// WasiCtx can collect a Vec<String> log so we can test this. We're
|
||||||
// logging the Display impl that `thiserror::Error` provides us.
|
// logging the Display impl that `thiserror::Error` provides us.
|
||||||
self.log.borrow_mut().push(e.to_string());
|
self.log.borrow_mut().push(e.to_string());
|
||||||
@@ -115,8 +115,9 @@ mod convert_multiple_error_types {
|
|||||||
TooMuchCoffee(usize),
|
TooMuchCoffee(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Just like the other error, except that we have a second errno type:
|
// Just like the prior test, except that we have a second errno type. This should mean there
|
||||||
// trivial function.
|
// are two functions in UserErrorConversion.
|
||||||
|
// Additionally, test that the function "baz" marked noreturn always returns a wiggle::Trap.
|
||||||
wiggle::from_witx!({
|
wiggle::from_witx!({
|
||||||
witx_literal: "
|
witx_literal: "
|
||||||
(typename $errno (enum u8 $ok $invalid_arg $picket_line))
|
(typename $errno (enum u8 $ok $invalid_arg $picket_line))
|
||||||
@@ -127,7 +128,10 @@ mod convert_multiple_error_types {
|
|||||||
(result $err $errno))
|
(result $err $errno))
|
||||||
(@interface func (export \"bar\")
|
(@interface func (export \"bar\")
|
||||||
(param $drink u32)
|
(param $drink u32)
|
||||||
(result $err $errno2)))
|
(result $err $errno2))
|
||||||
|
(@interface func (export \"baz\")
|
||||||
|
(param $drink u32)
|
||||||
|
(@witx noreturn)))
|
||||||
",
|
",
|
||||||
ctx: WasiCtx,
|
ctx: WasiCtx,
|
||||||
errors: { errno => RichError, errno2 => AnotherRichError },
|
errors: { errno => RichError, errno2 => AnotherRichError },
|
||||||
@@ -159,13 +163,13 @@ mod convert_multiple_error_types {
|
|||||||
// each member of the `errors` mapping.
|
// each member of the `errors` mapping.
|
||||||
// Bodies elided.
|
// Bodies elided.
|
||||||
impl<'a> types::UserErrorConversion for WasiCtx<'a> {
|
impl<'a> types::UserErrorConversion for WasiCtx<'a> {
|
||||||
fn errno_from_rich_error(&self, _e: RichError) -> Result<types::Errno, String> {
|
fn errno_from_rich_error(&self, _e: RichError) -> Result<types::Errno, wiggle::Trap> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
fn errno2_from_another_rich_error(
|
fn errno2_from_another_rich_error(
|
||||||
&self,
|
&self,
|
||||||
_e: AnotherRichError,
|
_e: AnotherRichError,
|
||||||
) -> Result<types::Errno2, String> {
|
) -> Result<types::Errno2, wiggle::Trap> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -178,5 +182,8 @@ mod convert_multiple_error_types {
|
|||||||
fn bar(&self, _: u32) -> Result<(), AnotherRichError> {
|
fn bar(&self, _: u32) -> Result<(), AnotherRichError> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
fn baz(&self, _: u32) -> wiggle::Trap {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -199,8 +199,9 @@ fn generate_func(
|
|||||||
#(#arg_names),*
|
#(#arg_names),*
|
||||||
);
|
);
|
||||||
match result {
|
match result {
|
||||||
Ok(r) => {return Ok(r.into());},
|
Ok(r) => Ok(r.into()),
|
||||||
Err(err) => { return Err(wasmtime::Trap::new(err)); },
|
Err(wasmtime_wiggle::Trap::String(err)) => Err(wasmtime::Trap::new(err)),
|
||||||
|
Err(wasmtime_wiggle::Trap::I32Exit(err)) => Err(wasmtime::Trap::i32_exit(err)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
(module
|
(module
|
||||||
(import "wasi_unstable" "proc_exit"
|
(import "wasi_unstable" "proc_exit"
|
||||||
(func $__wasi_proc_exit (param i32)))
|
(func $__wasi_proc_exit (param i32)))
|
||||||
|
(memory (export "memory") 0)
|
||||||
(func $_start
|
(func $_start
|
||||||
(call $__wasi_proc_exit (i32.const 125))
|
(call $__wasi_proc_exit (i32.const 125))
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
(module
|
(module
|
||||||
(import "wasi_snapshot_preview1" "proc_exit"
|
(import "wasi_snapshot_preview1" "proc_exit"
|
||||||
(func $__wasi_proc_exit (param i32)))
|
(func $__wasi_proc_exit (param i32)))
|
||||||
|
(memory (export "memory") 0)
|
||||||
(func $_start
|
(func $_start
|
||||||
(call $__wasi_proc_exit (i32.const 125))
|
(call $__wasi_proc_exit (i32.const 125))
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
(module
|
(module
|
||||||
(import "wasi_unstable" "proc_exit"
|
(import "wasi_unstable" "proc_exit"
|
||||||
(func $__wasi_proc_exit (param i32)))
|
(func $__wasi_proc_exit (param i32)))
|
||||||
|
(memory (export "memory") 0)
|
||||||
(func $_start
|
(func $_start
|
||||||
(call $__wasi_proc_exit (i32.const 126))
|
(call $__wasi_proc_exit (i32.const 126))
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
(module
|
(module
|
||||||
(import "wasi_snapshot_preview1" "proc_exit"
|
(import "wasi_snapshot_preview1" "proc_exit"
|
||||||
(func $__wasi_proc_exit (param i32)))
|
(func $__wasi_proc_exit (param i32)))
|
||||||
|
(memory (export "memory") 0)
|
||||||
(func $_start
|
(func $_start
|
||||||
(call $__wasi_proc_exit (i32.const 126))
|
(call $__wasi_proc_exit (i32.const 126))
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
(module
|
(module
|
||||||
(import "wasi_unstable" "proc_exit"
|
(import "wasi_unstable" "proc_exit"
|
||||||
(func $__wasi_proc_exit (param i32)))
|
(func $__wasi_proc_exit (param i32)))
|
||||||
|
(memory (export "memory") 0)
|
||||||
(func $_start
|
(func $_start
|
||||||
(call $__wasi_proc_exit (i32.const 2))
|
(call $__wasi_proc_exit (i32.const 2))
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
(module
|
(module
|
||||||
(import "wasi_snapshot_preview1" "proc_exit"
|
(import "wasi_snapshot_preview1" "proc_exit"
|
||||||
(func $__wasi_proc_exit (param i32)))
|
(func $__wasi_proc_exit (param i32)))
|
||||||
|
(memory (export "memory") 0)
|
||||||
(func $_start
|
(func $_start
|
||||||
(call $__wasi_proc_exit (i32.const 2))
|
(call $__wasi_proc_exit (i32.const 2))
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
(type (func (param i32)))
|
(type (func (param i32)))
|
||||||
|
|
||||||
(import "wasi_snapshot_preview1" "proc_exit" (func (type 0)))
|
(import "wasi_snapshot_preview1" "proc_exit" (func (type 0)))
|
||||||
|
(memory (export "memory") 0)
|
||||||
|
|
||||||
(func $exit (param i32)
|
(func $exit (param i32)
|
||||||
local.get 0
|
local.get 0
|
||||||
|
|||||||
Reference in New Issue
Block a user