From 69a719500a06b2f2d2660e145abd2e6255c2c037 Mon Sep 17 00:00:00 2001 From: OLUWAMUYIWA Date: Fri, 4 Nov 2022 17:47:59 +0100 Subject: [PATCH 1/6] implemented wasm-wasi based on wasmtimes impl --- Cargo.toml | 2 +- crates/core/src/host_error.rs | 30 +++++ crates/core/src/lib.rs | 2 +- crates/wasi/Cargo.toml | 21 ++++ crates/wasi/src/lib.rs | 22 ++++ crates/wasi/src/snapshots/mod.rs | 1 + crates/wasi/src/snapshots/preview_1.rs | 151 +++++++++++++++++++++++++ crates/wasi/tests/mod.rs | 1 + crates/wasi/tests/wasi_wat.rs | 91 +++++++++++++++ crates/wasi/tests/wat/hello_world.wat | 28 +++++ 10 files changed, 347 insertions(+), 2 deletions(-) create mode 100644 crates/wasi/Cargo.toml create mode 100644 crates/wasi/src/lib.rs create mode 100644 crates/wasi/src/snapshots/mod.rs create mode 100644 crates/wasi/src/snapshots/preview_1.rs create mode 100644 crates/wasi/tests/mod.rs create mode 100644 crates/wasi/tests/wasi_wat.rs create mode 100644 crates/wasi/tests/wat/hello_world.wat diff --git a/Cargo.toml b/Cargo.toml index 6efe8d2e9f..4ff4a18f4c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["crates/arena", "crates/cli", "crates/core", "crates/wasmi"] +members = ["crates/arena", "crates/cli", "crates/core", "crates/wasmi", "crates/wasi"] exclude = [] resolver = "2" diff --git a/crates/core/src/host_error.rs b/crates/core/src/host_error.rs index 0bf9bd1289..3f34398a2b 100644 --- a/crates/core/src/host_error.rs +++ b/crates/core/src/host_error.rs @@ -60,3 +60,33 @@ use downcast_rs::{impl_downcast, DowncastSync}; /// ``` pub trait HostError: 'static + Display + Debug + DowncastSync {} impl_downcast!(HostError); + +#[derive(Debug)] +pub enum HostErrType { + WithReason(String), + I32Exit(i32), +} + +impl Display for HostErrType { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + HostErrType::WithReason(r) => f.debug_tuple("HostErrorWithReason").field(r).finish(), + HostErrType::I32Exit(i) => f + .debug_tuple("HostErrorI32Exit") + .field(&format!("{i}")) + .finish(), + } + } +} + +impl HostErrType { + pub fn new_with_reason(reason: String) -> Self { + Self::WithReason(reason) + } + + pub fn new_132_exit(i: i32) -> Self { + Self::I32Exit(i) + } +} + +impl HostError for HostErrType {} diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 64a573cd1e..0b02e7c823 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -25,7 +25,7 @@ extern crate alloc; extern crate std as alloc; pub use self::{ - host_error::HostError, + host_error::{HostErrType, HostError}, nan_preserving_float::{F32, F64}, trap::{Trap, TrapCode}, units::Pages, diff --git a/crates/wasi/Cargo.toml b/crates/wasi/Cargo.toml new file mode 100644 index 0000000000..e7079a0eb9 --- /dev/null +++ b/crates/wasi/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "wasmi_wasi" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +wasi-common = "2.0.1" +wasmtime-wasi = "2.0.1" +wiggle = "2.0.0" +wasmi = { version = "0.19.0", path = "../wasmi" } +wasmi_core = { version = "0.4.0", path = "../core"} + +[dev-dependencies] +wat = "1.0.50" + +[features] +default = ["std"] +std = [] \ No newline at end of file diff --git a/crates/wasi/src/lib.rs b/crates/wasi/src/lib.rs new file mode 100644 index 0000000000..976d79a733 --- /dev/null +++ b/crates/wasi/src/lib.rs @@ -0,0 +1,22 @@ +pub mod snapshots; + +pub use snapshots::preview_1::define_wasi; +pub use wasi_common::{Error, WasiDir, WasiFile}; +pub use wasmi::Linker; +pub use wasmtime_wasi::{ + clocks, + dir::Dir, + file::{filetype_from, get_fd_flags, File}, + net, + sched, + stdio, + WasiCtx, + WasiCtxBuilder, +}; + +#[allow(dead_code)] +/// WasiCtxBuilder exists in case we wish to have our own versions of `WasiCtx`'s `rng` and `sched` +/// and possibly add our own methods as needed. It's private for now because I've found no use for it yet +struct WasmiWasiCtxBuilder(WasiCtx); + +impl WasmiWasiCtxBuilder {} diff --git a/crates/wasi/src/snapshots/mod.rs b/crates/wasi/src/snapshots/mod.rs new file mode 100644 index 0000000000..1508cc068d --- /dev/null +++ b/crates/wasi/src/snapshots/mod.rs @@ -0,0 +1 @@ +pub mod preview_1; diff --git a/crates/wasi/src/snapshots/preview_1.rs b/crates/wasi/src/snapshots/preview_1.rs new file mode 100644 index 0000000000..5ba082f6f3 --- /dev/null +++ b/crates/wasi/src/snapshots/preview_1.rs @@ -0,0 +1,151 @@ +#[allow(unused_imports)] +use wasi_common::snapshots::preview_1::*; +use wasi_common::Error; +use wasmi::{core::Trap, AsContextMut, Caller, Extern, Func, Linker}; +use wasmi_core::HostErrType; + +/// Adapted from wasmtime's `wiggle` crate. To reuse this would have required +/// an implementation of `From` +fn run_in_dummy_executor(future: F) -> Result { + use std::{ + pin::Pin, + task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, + }; + + let mut f = Pin::from(Box::new(future)); + let waker = dummy_waker(); + let mut cx = Context::from_waker(&waker); + match f.as_mut().poll(&mut cx) { + Poll::Ready(val) => return Ok(val), + Poll::Pending => + return Err(Trap::from(HostErrType::new_with_reason("Cannot wait on pending future: must enable wiggle \"async\" future and execute on an async Store".to_string()))) + } + + fn dummy_waker() -> Waker { + return unsafe { Waker::from_raw(clone(5 as *const _)) }; + + unsafe fn clone(ptr: *const ()) -> RawWaker { + assert_eq!(ptr as usize, 5); + const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop); + RawWaker::new(ptr, &VTABLE) + } + + unsafe fn wake(ptr: *const ()) { + assert_eq!(ptr as usize, 5); + } + + unsafe fn wake_by_ref(ptr: *const ()) { + assert_eq!(ptr as usize, 5); + } + + unsafe fn drop(ptr: *const ()) { + assert_eq!(ptr as usize, 5); + } + } +} + +/// Creates the function item `add_wasi_snapshot_preview1_to_wasmi_linker` which when called adds all +/// `wasi preview_1` functions to the linker +macro_rules! impl_add_to_linker_for_funcs { + ($($fname:ident ($( $arg:ident : $typ:ty ),* $(,)? ) => $ret:tt),+ $(,)?) => { + fn add_wasi_snapshot_preview1_to_wasmi_linker<'a, T, U>( + linker: &mut Linker, + mut store_ctx: impl AsContextMut, + wasi_ctx: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static) + -> Result<(), Error> + where U: wasi_common::snapshots::preview_1::wasi_snapshot_preview1::WasiSnapshotPreview1 + + wasi_common::snapshots::preview_1::wasi_snapshot_preview1::UserErrorConversion + { + let mut store = store_ctx.as_context_mut(); + $(linker.define( + "wasi_snapshot_preview1", + stringify!($fname), + Func::wrap(&mut store, move|mut caller: Caller<'_, T>, $($arg : $typ,)*| { + let result = async { + let mem = match caller.get_export("memory") { + Some(Extern::Memory(m)) => m, + _ => return Err(Trap::from(HostErrType::new_with_reason("missing required memory export".to_string()))), + }; + let(mem, ctx) = mem.data_and_store_mut(&mut caller); + let ctx = wasi_ctx(ctx); + let mem = wiggle::wasmtime::WasmtimeGuestMemory::new(mem); + + match wasi_common::snapshots::preview_1::wasi_snapshot_preview1::$fname(ctx, &mem, $($arg,)*).await { + Ok(r) => Ok(<$ret>::from(r)), + Err(wiggle::Trap::String(err)) => Err(Trap::from(HostErrType::new_with_reason(err))), + Err(wiggle::Trap::I32Exit(i)) => Err(Trap::from(HostErrType::new_132_exit(i))), + + } + }; + run_in_dummy_executor(result)? + }) + )?; + + )+ + Ok(()) + } +} +} + +impl_add_to_linker_for_funcs!( + args_get (arg0: i32, arg1: i32) => i32, + args_sizes_get(arg0: i32, arg1: i32) => i32, + environ_get (arg0: i32, arg1: i32) => i32, + environ_sizes_get (arg0: i32, arg1: i32) => i32, + clock_res_get (arg0: i32, arg1: i32) => i32, + clock_time_get (arg0 : i32, arg1 : i64, arg2 : i32) => i32, + fd_advise(arg0: i32, arg1: i64, arg2: i64, arg3: i32) => i32, + fd_allocate(arg0: i32, arg1: i64, arg2: i64) => i32, + fd_close(arg0: i32,) => i32, + fd_datasync(arg0: i32,) => i32, + fd_fdstat_get(arg0: i32, arg1: i32) => i32, + fd_fdstat_set_flags(arg0: i32, arg1: i32) => i32, + fd_fdstat_set_rights(arg0: i32, arg1: i64, arg2: i64) => i32, + fd_filestat_get(arg0: i32, arg1: i32) => i32, + fd_filestat_set_size(arg0: i32, arg1: i64) => i32, + fd_filestat_set_times(arg0: i32, arg1: i64, arg2: i64, arg3: i32) => i32, + fd_pread(arg0: i32, arg1: i32, arg2: i32, arg3: i64, arg4: i32) => i32, + fd_prestat_get(arg0: i32, arg1: i32) => i32, + fd_prestat_dir_name(arg0: i32, arg1: i32, arg2: i32) => i32, + fd_pwrite(arg0: i32, arg1: i32, arg2: i32, arg3: i64, arg4: i32) => i32, + + fd_read(arg0: i32, arg1: i32, arg2: i32, arg3: i32) => i32, + fd_readdir(arg0: i32, arg1: i32, arg2: i32, arg3: i64, arg4: i32) => i32, + fd_renumber(arg0: i32, arg1: i32) => i32, + fd_seek(arg0: i32, arg1: i64, arg2: i32, arg3: i32) => i32, + fd_sync(arg0: i32) => i32, + fd_tell(arg0: i32, arg1: i32) => i32, + fd_write(arg0: i32, arg1: i32, arg2: i32, arg3: i32) => i32, + path_create_directory(arg0: i32, arg1: i32, arg2: i32) => i32, + path_filestat_get(arg0: i32, arg1: i32, arg2: i32, arg3: i32, arg4: i32) => i32, + path_filestat_set_times(arg0: i32, arg1: i32, arg2: i32, arg3: i32, arg4: i64, arg5: i64, arg6: i32) => i32, + path_link(arg0: i32, arg1: i32, arg2: i32, arg3: i32, arg4: i32, arg5: i32, arg6: i32) => i32, + path_open(arg0: i32, arg1: i32, arg2: i32, arg3: i32, arg4: i32, arg5: i64, arg6: i64, arg7: i32, arg8: i32) => i32, + path_readlink(arg0: i32, rg1: i32, rg2: i32, rg3: i32, rg4: i32, rg5: i32) => i32, + path_remove_directory(arg0: i32, arg1: i32, arg2: i32) => i32, + path_rename(arg0: i32, arg1: i32, arg2: i32, arg3: i32, arg4: i32, arg5: i32) => i32, + path_symlink(arg0: i32, arg1: i32, arg2: i32, arg3: i32, arg4: i32) => i32, + path_unlink_file(arg0: i32, arg1: i32, arg2: i32) => i32, + poll_oneoff(arg0: i32, arg1: i32, arg2: i32, arg3: i32) => i32, + proc_raise(arg0: i32) => i32, + proc_exit(arg0: i32) => (), + sched_yield() => i32, + random_get(arg0: i32, arg1: i32) => i32, + sock_accept(arg0: i32, arg1: i32, arg2: i32) => i32, + sock_recv(arg0: i32, arg1: i32, arg2: i32, arg3: i32, arg4: i32, arg5: i32) => i32, + sock_send(arg0: i32,arg1: i32,arg2: i32,arg3: i32,arg4: i32) => i32, + sock_shutdown(arg0: i32, arg1: i32) => i32, + +); + +pub fn define_wasi( + linker: &mut Linker, + store_ctx: impl AsContextMut, + wasi_ctx: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, +) -> Result<(), Error> +where + U: wasi_common::snapshots::preview_1::wasi_snapshot_preview1::WasiSnapshotPreview1 + + wasi_common::snapshots::preview_1::wasi_snapshot_preview1::UserErrorConversion, +{ + add_wasi_snapshot_preview1_to_wasmi_linker(linker, store_ctx, wasi_ctx) +} diff --git a/crates/wasi/tests/mod.rs b/crates/wasi/tests/mod.rs new file mode 100644 index 0000000000..8e5f198eed --- /dev/null +++ b/crates/wasi/tests/mod.rs @@ -0,0 +1 @@ +mod wasi_wat; diff --git a/crates/wasi/tests/wasi_wat.rs b/crates/wasi/tests/wasi_wat.rs new file mode 100644 index 0000000000..779159db49 --- /dev/null +++ b/crates/wasi/tests/wasi_wat.rs @@ -0,0 +1,91 @@ +use std::{collections::HashMap, fs}; + +use wasmi::{Config, Engine, Extern, Instance, Linker, Module, Store}; +use wasmi_core::Value; +use wasmi_wasi::{define_wasi, WasiCtx}; +use wasmtime_wasi::WasiCtxBuilder; + +// #[derive(Debug)] +// pub struct TestContext { +// /// The `wasmi` engine used for executing functions used during the test. +// engine: Engine, +// /// The linker for linking together Wasm test modules. +// linker: Linker<()>, +// /// The store to hold all runtime data during the test. +// store: Store<()>, +// /// The list of all encountered Wasm modules belonging to the test. +// modules: Vec, +// /// The list of all instantiated modules. +// instances: HashMap, +// /// Intermediate results buffer that can be reused for calling Wasm functions. +// results: Vec, +// /// The descriptor of the test. +// file: String, +// } + +// impl TestContext { +// pub(crate) fn new(config: &Config, path: &str) -> Self { +// let engine = Engine::new(config); +// let mut linker = Linker::default(); +// let mut store = Store::new(&engine, ()); + +// Self { +// engine, +// linker, +// store, +// modules: Vec::new(), +// instances: HashMap::new(), +// results: Vec::new(), +// file: read_wast(path), +// } +// } +// } + +fn read_wast(path: &str) -> String { + fs::read_to_string(path) + .unwrap_or_else(|error| panic!("{path}, failed to read `.wast` test file: {error}")) +} + +pub fn load_instance_from_wat(wat_bytes: &[u8]) -> (wasmi::Store, wasmi::Instance) { + let wasm = wat2wasm(wat_bytes); + let config = Config::default(); + let engine = wasmi::Engine::new(&config); + let module = wasmi::Module::new(&engine, &wasm[..]).unwrap(); + let mut linker = >::default(); + // add wasi to linker + let wasi = WasiCtxBuilder::new() + .inherit_stdio() + .inherit_args() + .unwrap() + .build(); + let mut store = wasmi::Store::new(&engine, wasi); + + define_wasi(&mut linker, &mut store, |ctx| ctx).unwrap(); + let instance = linker + .instantiate(&mut store, &module) + .unwrap() + .start(&mut store) + .unwrap(); + (store, instance) +} + +/// Converts the `.wat` encoded `bytes` into `.wasm` encoded bytes. +pub fn wat2wasm(bytes: &[u8]) -> Vec { + wat::parse_bytes(bytes).unwrap().into_owned() +} + +fn load() -> (Store, Instance) { + let bytes = include_bytes!("wat/hello_world.wat"); + load_instance_from_wat(bytes) +} + +#[test] +fn test_hello_world() { + let (mut store, instance) = load(); + let f = instance + .get_export(&store, "_start") + .and_then(Extern::into_func) + .unwrap(); + let mut result = []; + f.call(&mut store, &vec![], &mut result).unwrap(); +} diff --git a/crates/wasi/tests/wat/hello_world.wat b/crates/wasi/tests/wat/hello_world.wat new file mode 100644 index 0000000000..80d9e5798a --- /dev/null +++ b/crates/wasi/tests/wat/hello_world.wat @@ -0,0 +1,28 @@ +;; copied (and slightly adapted) from [wasmtime tutorial](https://github.com/bytecodealliance/wasmtime/blob/main/docs/WASI-tutorial.md) +(module + ;; Import the required fd_write WASI function which will write the given io vectors to stdout + ;; The function signature for fd_write is: + ;; (File Descriptor, *iovs, iovs_len, nwritten) -> Returns number of bytes written + (import "wasi_snapshot_preview1" "fd_write" (func $fd_write (param i32 i32 i32 i32) (result i32))) + + (memory 1) + (export "memory" (memory 0)) + + ;; Write 'hello world\n' to memory at an offset of 8 bytes + ;; Note the trailing newline which is required for the text to appear + (data (i32.const 8) "hello world\n") + + (func $main (export "_start") + ;; Creating a new io vector within linear memory + (i32.store (i32.const 0) (i32.const 8)) ;; iov.iov_base - This is a pointer to the start of the 'hello world\n' string + (i32.store (i32.const 4) (i32.const 12)) ;; iov.iov_len - The length of the 'hello world\n' string + + (call $fd_write + (i32.const 1) ;; file_descriptor - 1 for stdout + (i32.const 0) ;; *iovs - The pointer to the iov array, which is stored at memory location 0 + (i32.const 1) ;; iovs_len - We're printing 1 string stored in an iov - so one. + (i32.const 20) ;; nwritten - A place in memory to store the number of bytes written + ) + drop ;; Discard the number of bytes written from the top of the stack + ) +) \ No newline at end of file From 075ec8294038ecd23677a5d6456522dee7c16d38 Mon Sep 17 00:00:00 2001 From: OLUWAMUYIWA Date: Tue, 8 Nov 2022 09:30:34 +0100 Subject: [PATCH 2/6] made suggested changes --- crates/core/src/host_error.rs | 30 ----- crates/core/src/lib.rs | 2 +- crates/wasi/Cargo.toml | 6 +- crates/wasi/src/lib.rs | 14 +- crates/wasi/src/snapshots/preview_1.rs | 176 ++++++++++++------------- crates/wasi/tests/wasi_wat.rs | 48 +------ crates/wasi/tests/wat/hello_world.wat | 1 + 7 files changed, 97 insertions(+), 180 deletions(-) diff --git a/crates/core/src/host_error.rs b/crates/core/src/host_error.rs index f671f474ce..7f44e329f3 100644 --- a/crates/core/src/host_error.rs +++ b/crates/core/src/host_error.rs @@ -58,33 +58,3 @@ use downcast_rs::{impl_downcast, DowncastSync}; /// ``` pub trait HostError: 'static + Display + Debug + DowncastSync {} impl_downcast!(HostError); - -#[derive(Debug)] -pub enum HostErrType { - WithReason(String), - I32Exit(i32), -} - -impl Display for HostErrType { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - HostErrType::WithReason(r) => f.debug_tuple("HostErrorWithReason").field(r).finish(), - HostErrType::I32Exit(i) => f - .debug_tuple("HostErrorI32Exit") - .field(&format!("{i}")) - .finish(), - } - } -} - -impl HostErrType { - pub fn new_with_reason(reason: String) -> Self { - Self::WithReason(reason) - } - - pub fn new_132_exit(i: i32) -> Self { - Self::I32Exit(i) - } -} - -impl HostError for HostErrType {} diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 0b02e7c823..64a573cd1e 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -25,7 +25,7 @@ extern crate alloc; extern crate std as alloc; pub use self::{ - host_error::{HostErrType, HostError}, + host_error::HostError, nan_preserving_float::{F32, F64}, trap::{Trap, TrapCode}, units::Pages, diff --git a/crates/wasi/Cargo.toml b/crates/wasi/Cargo.toml index e7079a0eb9..f3d05fffa8 100644 --- a/crates/wasi/Cargo.toml +++ b/crates/wasi/Cargo.toml @@ -8,10 +8,10 @@ edition = "2021" [dependencies] wasi-common = "2.0.1" -wasmtime-wasi = "2.0.1" +wasi-cap-std-sync = "2.0.1" wiggle = "2.0.0" -wasmi = { version = "0.19.0", path = "../wasmi" } -wasmi_core = { version = "0.4.0", path = "../core"} +wasmi = { version = "0.20.0", path = "../wasmi" } +wasmi_core = { version = "0.5.0", path = "../core"} [dev-dependencies] wat = "1.0.50" diff --git a/crates/wasi/src/lib.rs b/crates/wasi/src/lib.rs index 976d79a733..8d7f0c5d98 100644 --- a/crates/wasi/src/lib.rs +++ b/crates/wasi/src/lib.rs @@ -1,22 +1,14 @@ pub mod snapshots; pub use snapshots::preview_1::define_wasi; -pub use wasi_common::{Error, WasiDir, WasiFile}; -pub use wasmi::Linker; -pub use wasmtime_wasi::{ +pub use wasi_cap_std_sync::{ clocks, dir::Dir, file::{filetype_from, get_fd_flags, File}, net, sched, stdio, - WasiCtx, WasiCtxBuilder, }; - -#[allow(dead_code)] -/// WasiCtxBuilder exists in case we wish to have our own versions of `WasiCtx`'s `rng` and `sched` -/// and possibly add our own methods as needed. It's private for now because I've found no use for it yet -struct WasmiWasiCtxBuilder(WasiCtx); - -impl WasmiWasiCtxBuilder {} +pub use wasi_common::{Error, WasiCtx, WasiDir, WasiFile}; +pub use wasmi::Linker; diff --git a/crates/wasi/src/snapshots/preview_1.rs b/crates/wasi/src/snapshots/preview_1.rs index 5ba082f6f3..83fa34cdeb 100644 --- a/crates/wasi/src/snapshots/preview_1.rs +++ b/crates/wasi/src/snapshots/preview_1.rs @@ -1,54 +1,43 @@ +use std::{ + pin::Pin, + task::{Context, RawWaker, RawWakerVTable, Waker}, +}; + #[allow(unused_imports)] use wasi_common::snapshots::preview_1::*; use wasi_common::Error; use wasmi::{core::Trap, AsContextMut, Caller, Extern, Func, Linker}; -use wasmi_core::HostErrType; - -/// Adapted from wasmtime's `wiggle` crate. To reuse this would have required -/// an implementation of `From` -fn run_in_dummy_executor(future: F) -> Result { - use std::{ - pin::Pin, - task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, - }; - let mut f = Pin::from(Box::new(future)); - let waker = dummy_waker(); - let mut cx = Context::from_waker(&waker); - match f.as_mut().poll(&mut cx) { - Poll::Ready(val) => return Ok(val), - Poll::Pending => - return Err(Trap::from(HostErrType::new_with_reason("Cannot wait on pending future: must enable wiggle \"async\" future and execute on an async Store".to_string()))) +// Creates a dummy `RawWaker`. We can only create Wakers from `RawWaker`s +fn dummy_raw_waker() -> RawWaker { + fn no_op(_: *const ()) {} + //returns a new RawWaker by calling dummy_raw_waker again + fn clone(_: *const ()) -> RawWaker { + dummy_raw_waker() } + // RawWakerVTable specifies the functions that should be called when the RawWaker is cloned, woken, or dropped. + let vtable = &RawWakerVTable::new(clone, no_op, no_op, no_op); - fn dummy_waker() -> Waker { - return unsafe { Waker::from_raw(clone(5 as *const _)) }; - - unsafe fn clone(ptr: *const ()) -> RawWaker { - assert_eq!(ptr as usize, 5); - const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop); - RawWaker::new(ptr, &VTABLE) - } - - unsafe fn wake(ptr: *const ()) { - assert_eq!(ptr as usize, 5); - } - - unsafe fn wake_by_ref(ptr: *const ()) { - assert_eq!(ptr as usize, 5); - } + RawWaker::new(0 as *const (), vtable) +} - unsafe fn drop(ptr: *const ()) { - assert_eq!(ptr as usize, 5); - } +// Creates a dummy waker which does *nothing*, as the future itsef polls to ready at first poll +// A waker is needed to do any polling at all, as it is the primary constituent of the `Context` for polling +fn run_in_dummy_executor(f: F) -> Result { + let mut f = Pin::from(Box::new(f)); + let waker = unsafe { Waker::from_raw(dummy_raw_waker()) }; + let mut cx = Context::from_waker(&waker); + match f.as_mut().poll(&mut cx) { + std::task::Poll::Ready(val) => return Ok(val), + std::task::Poll::Pending => Err(Trap::new("Cannot wait on pending future")), } } -/// Creates the function item `add_wasi_snapshot_preview1_to_wasmi_linker` which when called adds all -/// `wasi preview_1` functions to the linker +// Creates the function item `add_wasi_snapshot_preview1_to_wasmi_linker` which when called adds all +// `wasi preview_1` functions to the linker macro_rules! impl_add_to_linker_for_funcs { - ($($fname:ident ($( $arg:ident : $typ:ty ),* $(,)? ) => $ret:tt),+ $(,)?) => { - fn add_wasi_snapshot_preview1_to_wasmi_linker<'a, T, U>( + ($($fname:ident ($( $arg:ident : $typ:ty ),* $(,)? ) -> $ret:tt),+ $(,)?) => { + fn add_wasi_snapshot_to_wasmi_linker<'a, T, U>( linker: &mut Linker, mut store_ctx: impl AsContextMut, wasi_ctx: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static) @@ -64,7 +53,7 @@ macro_rules! impl_add_to_linker_for_funcs { let result = async { let mem = match caller.get_export("memory") { Some(Extern::Memory(m)) => m, - _ => return Err(Trap::from(HostErrType::new_with_reason("missing required memory export".to_string()))), + _ => return Err(Trap::new("missing required memory export".to_string())), }; let(mem, ctx) = mem.data_and_store_mut(&mut caller); let ctx = wasi_ctx(ctx); @@ -72,8 +61,8 @@ macro_rules! impl_add_to_linker_for_funcs { match wasi_common::snapshots::preview_1::wasi_snapshot_preview1::$fname(ctx, &mem, $($arg,)*).await { Ok(r) => Ok(<$ret>::from(r)), - Err(wiggle::Trap::String(err)) => Err(Trap::from(HostErrType::new_with_reason(err))), - Err(wiggle::Trap::I32Exit(i)) => Err(Trap::from(HostErrType::new_132_exit(i))), + Err(wiggle::Trap::String(err)) => Err(Trap::new(err)), + Err(wiggle::Trap::I32Exit(i)) => Err(Trap::i32_exit(i)), } }; @@ -88,56 +77,65 @@ macro_rules! impl_add_to_linker_for_funcs { } impl_add_to_linker_for_funcs!( - args_get (arg0: i32, arg1: i32) => i32, - args_sizes_get(arg0: i32, arg1: i32) => i32, - environ_get (arg0: i32, arg1: i32) => i32, - environ_sizes_get (arg0: i32, arg1: i32) => i32, - clock_res_get (arg0: i32, arg1: i32) => i32, - clock_time_get (arg0 : i32, arg1 : i64, arg2 : i32) => i32, - fd_advise(arg0: i32, arg1: i64, arg2: i64, arg3: i32) => i32, - fd_allocate(arg0: i32, arg1: i64, arg2: i64) => i32, - fd_close(arg0: i32,) => i32, - fd_datasync(arg0: i32,) => i32, - fd_fdstat_get(arg0: i32, arg1: i32) => i32, - fd_fdstat_set_flags(arg0: i32, arg1: i32) => i32, - fd_fdstat_set_rights(arg0: i32, arg1: i64, arg2: i64) => i32, - fd_filestat_get(arg0: i32, arg1: i32) => i32, - fd_filestat_set_size(arg0: i32, arg1: i64) => i32, - fd_filestat_set_times(arg0: i32, arg1: i64, arg2: i64, arg3: i32) => i32, - fd_pread(arg0: i32, arg1: i32, arg2: i32, arg3: i64, arg4: i32) => i32, - fd_prestat_get(arg0: i32, arg1: i32) => i32, - fd_prestat_dir_name(arg0: i32, arg1: i32, arg2: i32) => i32, - fd_pwrite(arg0: i32, arg1: i32, arg2: i32, arg3: i64, arg4: i32) => i32, + args_get (arg0: i32, arg1: i32) -> i32, + args_sizes_get(arg0: i32, arg1: i32) -> i32, + environ_get (arg0: i32, arg1: i32) -> i32, + environ_sizes_get (arg0: i32, arg1: i32) -> i32, + clock_res_get (arg0: i32, arg1: i32) -> i32, + clock_time_get (arg0 : i32, arg1 : i64, arg2 : i32) -> i32, + fd_advise(arg0: i32, arg1: i64, arg2: i64, arg3: i32) -> i32, + fd_allocate(arg0: i32, arg1: i64, arg2: i64) -> i32, + fd_close(fd: i32,) -> i32, + fd_datasync(fd: i32,) -> i32, + fd_fdstat_get(arg0: i32, arg1: i32) -> i32, + fd_fdstat_set_flags(arg0: i32, arg1: i32) -> i32, + fd_fdstat_set_rights(arg0: i32, arg1: i64, arg2: i64) -> i32, + fd_filestat_get(arg0: i32, arg1: i32) -> i32, + fd_filestat_set_size(arg0: i32, arg1: i64) -> i32, + fd_filestat_set_times(arg0: i32, arg1: i64, arg2: i64, arg3: i32) -> i32, + fd_pread(arg0: i32, arg1: i32, arg2: i32, arg3: i64, arg4: i32) -> i32, + fd_prestat_get(arg0: i32, arg1: i32) -> i32, + fd_prestat_dir_name(arg0: i32, arg1: i32, arg2: i32) -> i32, + fd_pwrite(arg0: i32, arg1: i32, arg2: i32, arg3: i64, arg4: i32) -> i32, - fd_read(arg0: i32, arg1: i32, arg2: i32, arg3: i32) => i32, - fd_readdir(arg0: i32, arg1: i32, arg2: i32, arg3: i64, arg4: i32) => i32, - fd_renumber(arg0: i32, arg1: i32) => i32, - fd_seek(arg0: i32, arg1: i64, arg2: i32, arg3: i32) => i32, - fd_sync(arg0: i32) => i32, - fd_tell(arg0: i32, arg1: i32) => i32, - fd_write(arg0: i32, arg1: i32, arg2: i32, arg3: i32) => i32, - path_create_directory(arg0: i32, arg1: i32, arg2: i32) => i32, - path_filestat_get(arg0: i32, arg1: i32, arg2: i32, arg3: i32, arg4: i32) => i32, - path_filestat_set_times(arg0: i32, arg1: i32, arg2: i32, arg3: i32, arg4: i64, arg5: i64, arg6: i32) => i32, - path_link(arg0: i32, arg1: i32, arg2: i32, arg3: i32, arg4: i32, arg5: i32, arg6: i32) => i32, - path_open(arg0: i32, arg1: i32, arg2: i32, arg3: i32, arg4: i32, arg5: i64, arg6: i64, arg7: i32, arg8: i32) => i32, - path_readlink(arg0: i32, rg1: i32, rg2: i32, rg3: i32, rg4: i32, rg5: i32) => i32, - path_remove_directory(arg0: i32, arg1: i32, arg2: i32) => i32, - path_rename(arg0: i32, arg1: i32, arg2: i32, arg3: i32, arg4: i32, arg5: i32) => i32, - path_symlink(arg0: i32, arg1: i32, arg2: i32, arg3: i32, arg4: i32) => i32, - path_unlink_file(arg0: i32, arg1: i32, arg2: i32) => i32, - poll_oneoff(arg0: i32, arg1: i32, arg2: i32, arg3: i32) => i32, - proc_raise(arg0: i32) => i32, - proc_exit(arg0: i32) => (), - sched_yield() => i32, - random_get(arg0: i32, arg1: i32) => i32, - sock_accept(arg0: i32, arg1: i32, arg2: i32) => i32, - sock_recv(arg0: i32, arg1: i32, arg2: i32, arg3: i32, arg4: i32, arg5: i32) => i32, - sock_send(arg0: i32,arg1: i32,arg2: i32,arg3: i32,arg4: i32) => i32, - sock_shutdown(arg0: i32, arg1: i32) => i32, + fd_read(arg0: i32, arg1: i32, arg2: i32, arg3: i32) -> i32, + fd_readdir(arg0: i32, arg1: i32, arg2: i32, arg3: i64, arg4: i32) -> i32, + fd_renumber(arg0: i32, arg1: i32) -> i32, + fd_seek(arg0: i32, arg1: i64, arg2: i32, arg3: i32) -> i32, + fd_sync(arg0: i32) -> i32, + fd_tell(arg0: i32, arg1: i32) -> i32, + fd_write(arg0: i32, arg1: i32, arg2: i32, arg3: i32) -> i32, + path_create_directory(arg0: i32, arg1: i32, arg2: i32) -> i32, + path_filestat_get(arg0: i32, arg1: i32, arg2: i32, arg3: i32, arg4: i32) -> i32, + path_filestat_set_times(arg0: i32, arg1: i32, arg2: i32, arg3: i32, arg4: i64, arg5: i64, arg6: i32) -> i32, + path_link(arg0: i32, arg1: i32, arg2: i32, arg3: i32, arg4: i32, arg5: i32, arg6: i32) -> i32, + path_open(arg0: i32, arg1: i32, arg2: i32, arg3: i32, arg4: i32, arg5: i64, arg6: i64, arg7: i32, arg8: i32) -> i32, + path_readlink(arg0: i32, rg1: i32, rg2: i32, rg3: i32, rg4: i32, rg5: i32) -> i32, + path_remove_directory(arg0: i32, arg1: i32, arg2: i32) -> i32, + path_rename(arg0: i32, arg1: i32, arg2: i32, arg3: i32, arg4: i32, arg5: i32) -> i32, + path_symlink(arg0: i32, arg1: i32, arg2: i32, arg3: i32, arg4: i32) -> i32, + path_unlink_file(arg0: i32, arg1: i32, arg2: i32) -> i32, + poll_oneoff(arg0: i32, arg1: i32, arg2: i32, arg3: i32) -> i32, + proc_raise(arg0: i32) -> i32, + proc_exit(arg0: i32) -> (), + sched_yield() -> i32, + random_get(arg0: i32, arg1: i32) -> i32, + sock_accept(arg0: i32, arg1: i32, arg2: i32) -> i32, + sock_recv(arg0: i32, arg1: i32, arg2: i32, arg3: i32, arg4: i32, arg5: i32) -> i32, + sock_send(arg0: i32,arg1: i32,arg2: i32,arg3: i32,arg4: i32) -> i32, + sock_shutdown(arg0: i32, arg1: i32) -> i32, ); +/// Adds the entire `WASI API` to the [`Linker`] +/// Once linked, users can make use of all the low-level functionalities that `WASI` provides. +/// You could call them `syscall`s and you'd be correct, because they mirror what a non-os-dependent set of syscalls would look like +/// You now have access to resources such as files, directories, random number generators, and certain parts of the networking stack. +/// +/// # Note +/// +/// `WASI` is versioned in snapshots. It's still a WIP. Currently, this crate supports `preview_1` +/// Look [here](https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md) for more details. pub fn define_wasi( linker: &mut Linker, store_ctx: impl AsContextMut, @@ -147,5 +145,5 @@ where U: wasi_common::snapshots::preview_1::wasi_snapshot_preview1::WasiSnapshotPreview1 + wasi_common::snapshots::preview_1::wasi_snapshot_preview1::UserErrorConversion, { - add_wasi_snapshot_preview1_to_wasmi_linker(linker, store_ctx, wasi_ctx) + add_wasi_snapshot_to_wasmi_linker(linker, store_ctx, wasi_ctx) } diff --git a/crates/wasi/tests/wasi_wat.rs b/crates/wasi/tests/wasi_wat.rs index 779159db49..8d95d2962c 100644 --- a/crates/wasi/tests/wasi_wat.rs +++ b/crates/wasi/tests/wasi_wat.rs @@ -1,50 +1,6 @@ -use std::{collections::HashMap, fs}; - -use wasmi::{Config, Engine, Extern, Instance, Linker, Module, Store}; -use wasmi_core::Value; +use wasi_cap_std_sync::WasiCtxBuilder; +use wasmi::{Config, Extern, Instance, Store}; use wasmi_wasi::{define_wasi, WasiCtx}; -use wasmtime_wasi::WasiCtxBuilder; - -// #[derive(Debug)] -// pub struct TestContext { -// /// The `wasmi` engine used for executing functions used during the test. -// engine: Engine, -// /// The linker for linking together Wasm test modules. -// linker: Linker<()>, -// /// The store to hold all runtime data during the test. -// store: Store<()>, -// /// The list of all encountered Wasm modules belonging to the test. -// modules: Vec, -// /// The list of all instantiated modules. -// instances: HashMap, -// /// Intermediate results buffer that can be reused for calling Wasm functions. -// results: Vec, -// /// The descriptor of the test. -// file: String, -// } - -// impl TestContext { -// pub(crate) fn new(config: &Config, path: &str) -> Self { -// let engine = Engine::new(config); -// let mut linker = Linker::default(); -// let mut store = Store::new(&engine, ()); - -// Self { -// engine, -// linker, -// store, -// modules: Vec::new(), -// instances: HashMap::new(), -// results: Vec::new(), -// file: read_wast(path), -// } -// } -// } - -fn read_wast(path: &str) -> String { - fs::read_to_string(path) - .unwrap_or_else(|error| panic!("{path}, failed to read `.wast` test file: {error}")) -} pub fn load_instance_from_wat(wat_bytes: &[u8]) -> (wasmi::Store, wasmi::Instance) { let wasm = wat2wasm(wat_bytes); diff --git a/crates/wasi/tests/wat/hello_world.wat b/crates/wasi/tests/wat/hello_world.wat index 80d9e5798a..f28ee7d852 100644 --- a/crates/wasi/tests/wat/hello_world.wat +++ b/crates/wasi/tests/wat/hello_world.wat @@ -1,4 +1,5 @@ ;; copied (and slightly adapted) from [wasmtime tutorial](https://github.com/bytecodealliance/wasmtime/blob/main/docs/WASI-tutorial.md) + (module ;; Import the required fd_write WASI function which will write the given io vectors to stdout ;; The function signature for fd_write is: From 531b4aacc446d29fe7544efb9b16c64629bcd9fa Mon Sep 17 00:00:00 2001 From: OLUWAMUYIWA Date: Thu, 10 Nov 2022 22:07:26 +0100 Subject: [PATCH 3/6] renamed arguments to funcs in the impl_add_to_linker_for_funcs macro and added docs --- crates/wasi/src/snapshots/preview_1.rs | 512 +++++++++++++++++++++---- 1 file changed, 438 insertions(+), 74 deletions(-) diff --git a/crates/wasi/src/snapshots/preview_1.rs b/crates/wasi/src/snapshots/preview_1.rs index 83fa34cdeb..90fe45e441 100644 --- a/crates/wasi/src/snapshots/preview_1.rs +++ b/crates/wasi/src/snapshots/preview_1.rs @@ -18,7 +18,7 @@ fn dummy_raw_waker() -> RawWaker { // RawWakerVTable specifies the functions that should be called when the RawWaker is cloned, woken, or dropped. let vtable = &RawWakerVTable::new(clone, no_op, no_op, no_op); - RawWaker::new(0 as *const (), vtable) + RawWaker::new(std::ptr::null::<()>(), vtable) } // Creates a dummy waker which does *nothing*, as the future itsef polls to ready at first poll @@ -28,7 +28,7 @@ fn run_in_dummy_executor(f: F) -> Result return Ok(val), + std::task::Poll::Ready(val) => Ok(val), std::task::Poll::Pending => Err(Trap::new("Cannot wait on pending future")), } } @@ -36,7 +36,12 @@ fn run_in_dummy_executor(f: F) -> Result $ret:tt),+ $(,)?) => { + ( + $( + $( #[$docs:meta] )* + fn $fname:ident ($( $arg:ident : $typ:ty ),* $(,)? ) -> $ret:tt + );+ $(;)? + ) => { fn add_wasi_snapshot_to_wasmi_linker<'a, T, U>( linker: &mut Linker, mut store_ctx: impl AsContextMut, @@ -46,85 +51,444 @@ macro_rules! impl_add_to_linker_for_funcs { wasi_common::snapshots::preview_1::wasi_snapshot_preview1::UserErrorConversion { let mut store = store_ctx.as_context_mut(); - $(linker.define( - "wasi_snapshot_preview1", - stringify!($fname), - Func::wrap(&mut store, move|mut caller: Caller<'_, T>, $($arg : $typ,)*| { - let result = async { - let mem = match caller.get_export("memory") { - Some(Extern::Memory(m)) => m, - _ => return Err(Trap::new("missing required memory export".to_string())), + $( + $(#[$docs])* + linker.define( + "wasi_snapshot_preview1", + stringify!($fname), + Func::wrap(&mut store, move|mut caller: Caller<'_, T>, $($arg : $typ,)*| { + let result = async { + let mem = match caller.get_export("memory") { + Some(Extern::Memory(m)) => m, + _ => return Err(Trap::new("missing required memory export".to_string())), + }; + let(mem, ctx) = mem.data_and_store_mut(&mut caller); + let ctx = wasi_ctx(ctx); + let mem = wiggle::wasmtime::WasmtimeGuestMemory::new(mem); + + match wasi_common::snapshots::preview_1::wasi_snapshot_preview1::$fname(ctx, &mem, $($arg,)*).await { + Ok(r) => Ok(<$ret>::from(r)), + Err(wiggle::Trap::String(err)) => Err(Trap::new(err)), + Err(wiggle::Trap::I32Exit(i)) => Err(Trap::i32_exit(i)), + + } }; - let(mem, ctx) = mem.data_and_store_mut(&mut caller); - let ctx = wasi_ctx(ctx); - let mem = wiggle::wasmtime::WasmtimeGuestMemory::new(mem); - - match wasi_common::snapshots::preview_1::wasi_snapshot_preview1::$fname(ctx, &mem, $($arg,)*).await { - Ok(r) => Ok(<$ret>::from(r)), - Err(wiggle::Trap::String(err)) => Err(Trap::new(err)), - Err(wiggle::Trap::I32Exit(i)) => Err(Trap::i32_exit(i)), - - } - }; - run_in_dummy_executor(result)? - }) - )?; - - )+ + run_in_dummy_executor(result)? + }) + )?; + + )+ Ok(()) } } } impl_add_to_linker_for_funcs!( - args_get (arg0: i32, arg1: i32) -> i32, - args_sizes_get(arg0: i32, arg1: i32) -> i32, - environ_get (arg0: i32, arg1: i32) -> i32, - environ_sizes_get (arg0: i32, arg1: i32) -> i32, - clock_res_get (arg0: i32, arg1: i32) -> i32, - clock_time_get (arg0 : i32, arg1 : i64, arg2 : i32) -> i32, - fd_advise(arg0: i32, arg1: i64, arg2: i64, arg3: i32) -> i32, - fd_allocate(arg0: i32, arg1: i64, arg2: i64) -> i32, - fd_close(fd: i32,) -> i32, - fd_datasync(fd: i32,) -> i32, - fd_fdstat_get(arg0: i32, arg1: i32) -> i32, - fd_fdstat_set_flags(arg0: i32, arg1: i32) -> i32, - fd_fdstat_set_rights(arg0: i32, arg1: i64, arg2: i64) -> i32, - fd_filestat_get(arg0: i32, arg1: i32) -> i32, - fd_filestat_set_size(arg0: i32, arg1: i64) -> i32, - fd_filestat_set_times(arg0: i32, arg1: i64, arg2: i64, arg3: i32) -> i32, - fd_pread(arg0: i32, arg1: i32, arg2: i32, arg3: i64, arg4: i32) -> i32, - fd_prestat_get(arg0: i32, arg1: i32) -> i32, - fd_prestat_dir_name(arg0: i32, arg1: i32, arg2: i32) -> i32, - fd_pwrite(arg0: i32, arg1: i32, arg2: i32, arg3: i64, arg4: i32) -> i32, - - fd_read(arg0: i32, arg1: i32, arg2: i32, arg3: i32) -> i32, - fd_readdir(arg0: i32, arg1: i32, arg2: i32, arg3: i64, arg4: i32) -> i32, - fd_renumber(arg0: i32, arg1: i32) -> i32, - fd_seek(arg0: i32, arg1: i64, arg2: i32, arg3: i32) -> i32, - fd_sync(arg0: i32) -> i32, - fd_tell(arg0: i32, arg1: i32) -> i32, - fd_write(arg0: i32, arg1: i32, arg2: i32, arg3: i32) -> i32, - path_create_directory(arg0: i32, arg1: i32, arg2: i32) -> i32, - path_filestat_get(arg0: i32, arg1: i32, arg2: i32, arg3: i32, arg4: i32) -> i32, - path_filestat_set_times(arg0: i32, arg1: i32, arg2: i32, arg3: i32, arg4: i64, arg5: i64, arg6: i32) -> i32, - path_link(arg0: i32, arg1: i32, arg2: i32, arg3: i32, arg4: i32, arg5: i32, arg6: i32) -> i32, - path_open(arg0: i32, arg1: i32, arg2: i32, arg3: i32, arg4: i32, arg5: i64, arg6: i64, arg7: i32, arg8: i32) -> i32, - path_readlink(arg0: i32, rg1: i32, rg2: i32, rg3: i32, rg4: i32, rg5: i32) -> i32, - path_remove_directory(arg0: i32, arg1: i32, arg2: i32) -> i32, - path_rename(arg0: i32, arg1: i32, arg2: i32, arg3: i32, arg4: i32, arg5: i32) -> i32, - path_symlink(arg0: i32, arg1: i32, arg2: i32, arg3: i32, arg4: i32) -> i32, - path_unlink_file(arg0: i32, arg1: i32, arg2: i32) -> i32, - poll_oneoff(arg0: i32, arg1: i32, arg2: i32, arg3: i32) -> i32, - proc_raise(arg0: i32) -> i32, - proc_exit(arg0: i32) -> (), - sched_yield() -> i32, - random_get(arg0: i32, arg1: i32) -> i32, - sock_accept(arg0: i32, arg1: i32, arg2: i32) -> i32, - sock_recv(arg0: i32, arg1: i32, arg2: i32, arg3: i32, arg4: i32, arg5: i32) -> i32, - sock_send(arg0: i32,arg1: i32,arg2: i32,arg3: i32,arg4: i32) -> i32, - sock_shutdown(arg0: i32, arg1: i32) -> i32, + // Read command-line argument data. + // The size of the array should match that returned by `args_sizes_get`. Each argument is expected to be \0 terminated. + fn args_get(argv: i32, argv_buf: i32) -> i32; + + // Return command-line argument data sizes. + // Returns the number of arguments and the size of the argument string data, or an error + // `offset0` and `offset1` are offsets into memory where the two results are stored + fn args_sizes_get(offset0: i32, offset1: i32) -> i32; + + // Read environment variable data. The sizes of the buffers should match that returned by `environ_sizes_get`. + // Key/value pairs are expected to be joined with =s, and terminated with \0s. + fn environ_get(environ: i32, environ_buf: i32) -> i32; + + // Return environment variable data sizes. + // Returns the number of environment variable arguments and the size of the environment variable data. + // `offset0` and `offset1` are offsets into memory where the two results are stored + fn environ_sizes_get(offset0: i32, offset1: i32) -> i32; + + // Return the resolution of a clock. Implementations are required to provide a non-zero value for supported clocks. + // For unsupported clocks, return `errno::inval.` + // Note: This is similar to `clock_getres` in POSIX. + // param id is the `ClockID`, `offset0` is the offset into memory where the result is written + fn clock_res_get(id: i32, offset0: i32) -> i32; + + // Return the time value of a clock. Note: This is similar to `clock_gettime` in POSIX. + // Result is stored in `offset0` + fn clock_time_get(id: i32, precision: i64, offset0: i32) -> i32; + + // Provide file advisory information on a file descriptor. Note: This is similar to `posix_fadvise` in POSIX. + // Parameters: + // fd: file descriptor + // offset: The offset within the file to which the advisory applies + // len: The length of the region to which the advisory applies + // advice: the advice + fn fd_advise(fd: i32, offset: i64, len: i64, advice: i32) -> i32; + + // Force the allocation of space in a file. + // Note: This is similar to `posix_fallocate` in `POSIX`. + // Parameters: + // fd: file descriptor + // offset The offset at which to start the allocation. + // len: The length of the area that is allocated. + fn fd_allocate(fd: i32, offset: i64, len: i64) -> i32; + + // Close a file descriptor. Note: This is similar to `close` in POSIX. + // Parameters: + // fd: file descriptor + fn fd_close(fd: i32) -> i32; + + // Synchronize the data of a file to disk. Note: This is similar to `fdatasync` in POSIX. + // Parameters: + // fd: file descriptor + fn fd_datasync(fd: i32) -> i32; + + // Get the attributes of a file descriptor. + // Note: This returns similar flags to `fsync(fd, F_GETFL)` in POSIX, as well as additional fields. + // Parameters: + // fd: file descriptor + // offset0: ffset into memory where the result is written + fn fd_fdstat_get(fd: i32, offset0: i32) -> i32; + + // Adjust the flags associated with a file descriptor. + // Note: This is similar to `fcntl(fd, F_SETFL, flags)` in POSIX. + // Parameters: + // fd: file descriptor + // flags: the desired values of the file descriptor flags. + fn fd_fdstat_set_flags(fd: i32, flags: i32) -> i32; + + // Adjust the rights associated with a file descriptor. + // This can only be used to remove rights, and returns errno::notcapable if called in a way that would attempt to add rights + // Parameters: + // fd: file descriptor + // fs_rights_base: The desired rights of the file descriptor. + // fs_rights_inheriting: rughts + fn fd_fdstat_set_rights(fd: i32, fs_rights_base: i64, fs_rights_inheriting: i64) -> i32; + + // Returns the attributes of an open file. + // Parameters: + // fd: file descriptor + // offset0: the offset into memory where the result(The buffer where the file's attributes are stored.) is written. + fn fd_filestat_get(fd: i32, offset0: i32) -> i32; + + // Adjust the size of an open file. + // If this increases the file's size, the extra bytes are filled with zeros. + // Note: This is similar to `ftruncate` in POSIX. + // Parameters: + // fd: file descriptor + // size: The desired file size. + fn fd_filestat_set_size(fd: i32, size: i64) -> i32; + + // Adjust the timestamps of an open file or directory. + // Note: This is similar to `futimens` in POSIX. + // Parameters: + // fd: file descriptor + // atim: The desired values of the data access timestamp. + // mtim: The desired values of the data modification timestamp. + // fst_flags: A bitmask indicating which timestamps to adjust. + fn fd_filestat_set_times(fd: i32, atim: i64, mtim: i64, fst_flags: i32) -> i32; + + // Read from a file descriptor, without using and updating the file descriptor's offset. + // Note: This is similar to `preadv` in POSIX. + // Parameters: + // fd: file descriptor + // iov_buf, iov_buf_len: used to create iovec, which is the list of scatter/gather vectors in which to store data. + // offset: The offset within the file at which to read. + // offsset0: size of bytes read is written here + fn fd_pread(fd: i32, iov_buf: i32, iov_buf_len: i32, offset: i64, offset0: i32) -> i32; + + // Return a description of the given preopened file descriptor. + // Parameters: + // fd: file descriptor + // offset0: Result (The buffer where the description is stored) is written into this offset in memory + fn fd_prestat_get(fd: i32, offset0: i32) -> i32; + + // Return a description of the given preopened file descriptor. + // Parameters: + // fd: file descriptor + // path: A buffer into which to write the preopened directory name. + // path_len + fn fd_prestat_dir_name(fd: i32, path: i32, path_len: i32) -> i32; + + // Write to a file descriptor, without using and updating the file descriptor's offset. + // Note: This is similar to `pwritev` in POSIX. + // Parameters: + // fd: file descriptor + // ciov_buf, ciov_buf_len: used to create ciovec, which is the list of scatter/gather vectors from which to retrieve data. + // offset: The offset within the file at which to write. + // offsset0: size of bytes written is written here + fn fd_pwrite(fd: i32, ciov_buf: i32, ciov_buf_len: i32, offset: i64, offset0: i32) -> i32; + + // Read from a file descriptor. Note: This is similar to readv in POSIX. + // Parameters: + // fd: file descriptor + // iov_buf, iov_buf_len: used to create iovec, which is the list of scatter/gather vectors in which to store data. + // offset: The offset within the file at which to read. + // offsset0: size of bytes read is written here + fn fd_read(fd: i32, iov_buf: i32, iov_buf_len: i32, offset1: i32) -> i32; + + // Read directory entries from a directory. When successful, the contents of the output buffer consist of a sequence of directory entries. + // Each directory entry consists of a dirent object, followed by dirent::d_namlen bytes holding the name of the directory entry. + // This function fills the output buffer as much as possible, potentially truncating the last directory entry. + // This allows the caller to grow its read buffer size in case it's too small to fit a single large directory entry, or skip the oversized directory entry. + // Parameters: + // fd: file descriptor + // buf: The buffer where directory entries are stored + // buf_len + // cookie: The location within the directory to start reading + // offset0: The result, i.e. The number of bytes stored in the read buffer, is stored at this offset in memory + // If less than the size of the read buffer, the end of the directory has been reached. + fn fd_readdir(fd: i32, buf: i32, buf_len: i32, cookie: i64, offset0: i32) -> i32; + + // Atomically replace a file descriptor by renumbering another file descriptor. + // Due to the strong focus on thread safety, this environment does not provide a mechanism to duplicate or renumber a file descriptor to an arbitrary number, like dup2(). + // This would be prone to race conditions, as an actual file descriptor with the same number could be allocated by a different thread at the same time. + // This function provides a way to atomically renumber file descriptors, which would disappear if dup2() were to be removed entirely. + // Parameters: + // fd: file descriptor + // to: The file descriptor to overwrite. + fn fd_renumber(fd: i32, to: i32) -> i32; + + // Move the offset of a file descriptor. Note: This is similar to `lseek` in POSIX. + // Parameters: + // fd: fle descriptor + // offset: The number of bytes to move. + // whence: The base from which the offset is relative + // offset0: The result (The new offset of the file descriptor, relative to the start of the file.) is stored at this offset in memory + fn fd_seek(fd: i32, offset: i64, whence: i32, offset0: i32) -> i32; + + // Synchronize the data and metadata of a file to disk. Note: This is similar to `fsync` in POSIX. + // Parameters: + // fd: file descriptor + fn fd_sync(fd: i32) -> i32; + + // Return the current offset of a file descriptor. Note: This is similar to `lseek(fd, 0, SEEK_CUR)` in POSIX. + // Parameters: + // fd: file descriptor + // offset0: Offset into the memory where result is stored + // Successfl result: The current offset of the file descriptor, relative to the start of the file. + fn fd_tell(fd: i32, offset0: i32) -> i32; + + // Write to a file descriptor. Note: This is similar to `writev` in POSIX. + // Parameters: + // fd: file descriptor + // ciov_buf, ciov_buf_len: used to create ciovec, which is the list of scatter/gather vectors from which to retrieve data. + // offset0: Offset into the memory where result(size written) is stored + fn fd_write(fd: i32, ciov_buf: i32, ciov_buf_len: i32, offset0: i32) -> i32; + + // Create a directory. Note: This is similar to `mkdirat` in POSIX. + // Parameters: + // fd: file descriptor + // offset, length: offset/length pair used to create a guest pointer into host memory. this pointer references the path string + // The path at which to create the directory. + fn path_create_directory(fd: i32, offset: i32, length: i32) -> i32; + + // Return the attributes of a file or directory. Note: This is similar to `stat` in POSIX. + // Parameters: + // fd: file descriptor + // flags: Flags determining the method of how the path is resolved. + // offset,length: offset/length pair used to create a guest pointer into host memory. + // this pointer references the path string. The path of the file or directory to inspect. + // offset0: The buffer where the file's attributes are stored. + fn path_filestat_get(fd: i32, flags: i32, offset: i32, length: i32, offset0: i32) -> i32; + + // Adjust the timestamps of a file or directory. Note: This is similar to `utimensat` in POSIX. + // Parameters: + // fd: file descriptor + // flags: Flags determining the method of how the path is resolved. + // offset,length: offset/length pair used to create a guest pointer into host memory. + // this pointer references the path string. The path of the file or directory to operate on. + // atim: The desired values of the data access timestamp. + // mtim: The desired values of the data modification timestamp. + // fst_flags: A bitmask indicating which timestamps to adjust. + fn path_filestat_set_times( + fd: i32, + flags: i32, + offset: i32, + length: i32, + atim: i64, + mtim: i64, + fst_flags: i32, + ) -> i32; + + // Create a hard link. Note: This is similar to `linkat` in POSIX. + // Parameters: + // old_fd: file descriptor + // old_flags: Flags determining the method of how the path is resolved. + // old_offset,old_length: offset/length pair used to create a guest pointer into host memory. + // this pointer references the path string, i.e. The source path from which to link. + // new_fd: The working directory at which the resolution of the new path starts. + // new_offset,new_length: offset/length pair used to create a guest pointer into host memory. + // this pointer references the path string, i.e. The destination path at which to create the hard link. + fn path_link( + old_fd: i32, + old_flags: i32, + old_offset: i32, + old_length: i32, + new_fd: i32, + new_offset: i32, + new_length: i32, + ) -> i32; + + // Open a file or directory. + // The returned file descriptor is not guaranteed to be the lowest-numbered file descriptor not currently open; + // it is randomized to prevent applications from depending on making assumptions about indexes, since this is error-prone in multi-threaded contexts. + // The returned file descriptor is guaranteed to be less than 2**31. + // Note: This is similar to `openat` in POSIX. + // Parameters: + // fd: file descriptor + // dirflags: Flags determining the method of how the path is resolved. + // offset,length: offset/length pair used to create a guest pointer into host memory. + // this pointer references the path string. i.e. The relative path of the file or directory to open, relative to the path_open::fd directory. + // oflags: The method by which to open the file. + // fs_rights_base: The initial rights of the newly created file descriptor + // fs_rights_inheriting: rights + // fdflags + // offset0: offset into memory where result is stored. Result is the file descriptor of the file that has been opened. + fn path_open( + fd: i32, + dirflags: i32, + offset: i32, + length: i32, + oflags: i32, + fs_rights_base: i64, + fdflags: i64, + fs_rights_inheriting: i32, + offfset0: i32, + ) -> i32; + + // Read the contents of a symbolic link. Note: This is similar to `readlinkat` in POSIX. + // Parameters: + // fd: file descriptor + // offset,length: offset/length pair used to create a guest pointer into host memory. + // this pointer references the path string. i.e. The path of the symbolic link from which to read. + // buf: The buffer to which to write the contents of the symbolic link. + // buf_len + // offset0: offset into memory where result is stored. Result is the number of bytes placed in the buffer. + fn path_readlink( + fd: i32, + offset: i32, + length: i32, + buf: i32, + buf_len: i32, + offset0: i32, + ) -> i32; + + // Remove a directory. Return `errno::notempty` if the directory is not empty. + // Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. + // Parameters: + // fd: file descriptor + // offset,length: offset/length pair used to create a guest pointer into host memory. + // this pointer references the path string. The path to a directory to remove. + fn path_remove_directory(fd: i32, offset: i32, length: i32) -> i32; + + // Rename a file or directory. Note: This is similar to `renameat` in POSIX. + // Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. + // Parameters: + // fd: file descriptor + // old_offset,old_length: offset/length pair used to create a guest pointer into host memory. + // this pointer references the path string, i.e. The source path of the file or directory to rename. + // new_fd: The working directory at which the resolution of the new path starts. + // new_offset,new_length: offset/length pair used to create a guest pointer into host memory. + // this pointer references the path string, i.e. The destination path to which to rename the file or directory. + fn path_rename( + fd: i32, + old_offset: i32, + old_length: i32, + new_fd: i32, + new_offset: i32, + new_length: i32, + ) -> i32; + + // Create a symbolic link. Note: This is similar to `symlinkat` in POSIX. + // Parameters: + // old_offset,old_length: offset/length pair used to create a guest pointer into host memory. + // this pointer references the path string, i.e. The contents of the symbolic link. + // fd: file descriptor + // new_offset,new_length: offset/length pair used to create a guest pointer into host memory. + // this pointer references the path string, i.e. The destination path at which to create the symbolic link. + fn path_symlink( + old_offset: i32, + old_length: i32, + fd: i32, + new_offset: i32, + new_length: i32, + ) -> i32; + + // Unlink a file. Return errno::isdir if the path refers to a directory. + // Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. + // Parameters: + // fd: file descriptor + // offset,length: offset/length pair used to create a guest pointer into host memory. + // this pointer references the path string. The path to a file to unlink. + fn path_unlink_file(fd: i32, offset: i32, length: i32) -> i32; + + // Concurrently poll for the occurrence of a set of events. + // in_: The events to which to subscribe. + // out: The events that have occurred. + // nsubscriptions: Both the number of subscriptions and events. + // offset0: offset into memory where result is stored. Result is the number of events stored. + fn poll_oneoff(in_: i32, out: i32, nsubscriptions: i32, offset0: i32) -> i32; + + // Terminate the process normally. An exit code of 0 indicates successful termination of the program. + // The meanings of other values is dependent on the environment. + // Parameters: + // rval: The exit code returned by the process. + fn proc_exit(rval: i32) -> (); + + // Send a signal to the process of the calling thread. + // Note: This is similar to `raise` in POSIX. + // Parameters: + // sig: The signal condition to trigger. + fn proc_raise(sig: i32) -> i32; + + // Temporarily yield execution of the calling thread. + // Note: This is similar to sched_yield in POSIX. + fn sched_yield() -> i32; + + // Write high-quality random data into a buffer. + // Parameters: + // buf: The buffer to fill with random data. + // buf_len + fn random_get(buf: i32, buf_len: i32) -> i32; + + // Accept a new incoming connection. + // Note: This is similar to `accept` in POSIX. + // Parameters: + // fd: The listening socket. + // flags: The desired values of the file descriptor flags. + // offset0: offset into memory where result is stored. Result is the new socket connection fd + fn sock_accept(fd: i32, flags: i32, offset0: i32) -> i32; + + // Receive a message from a socket. + // Note: This is similar to `recv` in POSIX, + // though it also supports reading the data into multiple buffers in the manner of `readv`. + // Parameters: + // fd: file descriptor + // iov_buf, iov_buf_len: used to create iovec, which is the list of scatter/gather vectors in which to store data. + // ri_flags: Message flags. + // offset0, offset1: offset into memory where result is stored. + // Results are Number of bytes stored in ri_data and message flags. + fn sock_recv( + fd: i32, + iov_buf: i32, + iov_buf_len: i32, + ri_flags: i32, + offset0: i32, + offset1: i32, + ) -> i32; + + // Send a message on a socket. + // Note: This is similar to `send` in POSIX, + // though it also supports writing the data from multiple buffers in the manner of `writev` + // Parameters: + // fd: file descriptor + // ciov_buf, ciov_buf_len: used to create ciovec, which is the list of scatter/gather vectors from which to retrieve data. + // si_flags: Message flags + // offset0: offset into memory where result is stored. Result is Number of bytes transmitted. + fn sock_send(fd: i32, ciov_buf: i32, ciov_buf_len: i32, si_flags: i32, offset0: i32) -> i32; + // Shut down socket send and receive channels. + // Note: This is similar to `shutdown` in POSIX. + // Parameters: + // fd: file descriptor + // how: Which channels on the socket to shut down. + fn sock_shutdown(fd: i32, how: i32) -> i32; ); /// Adds the entire `WASI API` to the [`Linker`] From 94c3577cbde6f25a3779dd4dc8034bfdf2adb35d Mon Sep 17 00:00:00 2001 From: OLUWAMUYIWA Date: Fri, 11 Nov 2022 00:20:10 +0100 Subject: [PATCH 4/6] fixed ci failures --- crates/wasi/Cargo.toml | 4 ---- crates/wasi/tests/wasi_wat.rs | 2 +- scripts/run-local-ci.sh | 2 +- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/crates/wasi/Cargo.toml b/crates/wasi/Cargo.toml index f3d05fffa8..1db0e4abe4 100644 --- a/crates/wasi/Cargo.toml +++ b/crates/wasi/Cargo.toml @@ -15,7 +15,3 @@ wasmi_core = { version = "0.5.0", path = "../core"} [dev-dependencies] wat = "1.0.50" - -[features] -default = ["std"] -std = [] \ No newline at end of file diff --git a/crates/wasi/tests/wasi_wat.rs b/crates/wasi/tests/wasi_wat.rs index 8d95d2962c..7752cbcfab 100644 --- a/crates/wasi/tests/wasi_wat.rs +++ b/crates/wasi/tests/wasi_wat.rs @@ -43,5 +43,5 @@ fn test_hello_world() { .and_then(Extern::into_func) .unwrap(); let mut result = []; - f.call(&mut store, &vec![], &mut result).unwrap(); + f.call(&mut store, &[], &mut result).unwrap(); } diff --git a/scripts/run-local-ci.sh b/scripts/run-local-ci.sh index 27d42d7903..759251a8df 100755 --- a/scripts/run-local-ci.sh +++ b/scripts/run-local-ci.sh @@ -6,7 +6,7 @@ cargo +nightly fmt && echo " Building ..." && cargo +stable build --workspace && echo " Building no_std ..." && -cargo +stable build --workspace --exclude wasmi_cli --no-default-features --target thumbv7em-none-eabi && +cargo +stable build --workspace --exclude wasmi_cli --exclude wasmi_wasi --no-default-features --target thumbv7em-none-eabi && echo " Clippy ..." && cargo +stable clippy --workspace -- -D warnings && echo " Docs ..." && From ac18d557674dd59702e8e5fdd4fb30a881d1472f Mon Sep 17 00:00:00 2001 From: OLUWAMUYIWA Date: Sat, 12 Nov 2022 03:19:19 +0100 Subject: [PATCH 5/6] modified rust.yml and removed dep wasmi_core --- .github/workflows/rust.yml | 2 +- crates/wasi/Cargo.toml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index e62c7eb9cd..6cb5713246 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -36,7 +36,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: build - args: --workspace --lib --no-default-features --target thumbv7em-none-eabi --exclude wasmi_cli + args: --workspace --lib --no-default-features --target thumbv7em-none-eabi --exclude wasmi_cli --exclude wasmi_wasi - name: Build (wasm32) uses: actions-rs/cargo@v1 with: diff --git a/crates/wasi/Cargo.toml b/crates/wasi/Cargo.toml index 1db0e4abe4..f0f8790c52 100644 --- a/crates/wasi/Cargo.toml +++ b/crates/wasi/Cargo.toml @@ -11,7 +11,6 @@ wasi-common = "2.0.1" wasi-cap-std-sync = "2.0.1" wiggle = "2.0.0" wasmi = { version = "0.20.0", path = "../wasmi" } -wasmi_core = { version = "0.5.0", path = "../core"} [dev-dependencies] wat = "1.0.50" From 6b999acad4cbb33d8913fd73f7662a36694be61d Mon Sep 17 00:00:00 2001 From: OLUWAMUYIWA Date: Sat, 12 Nov 2022 14:16:56 +0100 Subject: [PATCH 6/6] modified rust.yml --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 6cb5713246..7a6fdf5570 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -41,7 +41,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: build - args: --workspace --no-default-features --target wasm32-unknown-unknown + args: --workspace --no-default-features --target wasm32-unknown-unknown --exclude wasmi_wasi test: name: Test