From 176dd108cf9e2e0bb3f641cc1fe8af13e0a38437 Mon Sep 17 00:00:00 2001 From: Xin Liu Date: Mon, 12 Jun 2023 19:32:55 +0800 Subject: [PATCH] [feat] implement a separate `VmBuilder::build` method for `async` cases (#3) * chore(rust-sys): expose WasiCtx Signed-off-by: Xin Liu * chore(rust-sdk): expose WasiCtx Signed-off-by: Xin Liu * refactor(rust-sdk): split VmBuilder::build Signed-off-by: Xin Liu * chore(rust-sdk): update test code Signed-off-by: Xin Liu * chore(rust-sdk): update examples Signed-off-by: Xin Liu * chore(rust-sdk): update test code Signed-off-by: Xin Liu * chore(rust-sdk): update rustdoc Signed-off-by: Xin Liu * ci(bindings-rust): update PR paths Signed-off-by: Xin Liu * feat(async-wasi): implement new APIs in WasiCtx Signed-off-by: Xin Liu * feat(rust-sys): implement new APIs in AsyncWasiModule Signed-off-by: Xin Liu * feat(rust-sdk): implement new APIs in WasiInstance Signed-off-by: Xin Liu * chore(rust-sdk): refactor build method of VmBuilder Signed-off-by: Xin Liu * chore(rust-sdk): update examples Signed-off-by: Xin Liu * ci(bindings-rust): rust-1.70 Signed-off-by: Xin Liu * ci(bindings-rust): update rust version Signed-off-by: Xin Liu * chore(rust-sys): update test code Signed-off-by: Xin Liu --------- Signed-off-by: Xin Liu --- .github/workflows/bindings-rust.yml | 11 +- crates/async-wasi/src/snapshots/mod.rs | 15 +- crates/wasmedge-sys/src/async_wasi.rs | 4 +- crates/wasmedge-sys/src/executor.rs | 16 +- crates/wasmedge-sys/src/instance/module.rs | 168 ++++++++++++++++----- crates/wasmedge-sys/src/lib.rs | 3 + examples/async_hello_world.rs | 6 +- examples/async_run_func.rs | 4 +- examples/create_host_func_in_host_func.rs | 21 ++- examples/hello_world.rs | 36 +++-- examples/host_func_with_custom_error.rs | 62 ++++---- examples/hostfunc_call_chain.rs | 78 +++++----- examples/run_func_in_aot_mode.rs | 6 +- examples/vm.rs | 27 ++-- src/compiler.rs | 1 + src/executor.rs | 4 +- src/lib.rs | 68 +++++---- src/vm.rs | 140 +++++++++++------ src/wasi.rs | 42 ++++++ 19 files changed, 468 insertions(+), 244 deletions(-) diff --git a/.github/workflows/bindings-rust.yml b/.github/workflows/bindings-rust.yml index 501c77ef3..79802d5ee 100644 --- a/.github/workflows/bindings-rust.yml +++ b/.github/workflows/bindings-rust.yml @@ -26,6 +26,9 @@ on: - main paths: - ".github/workflows/bindings-rust.yml" + - "examples/**" + - "src/**" + - "crates/**" jobs: build_ubuntu: @@ -34,7 +37,7 @@ jobs: strategy: matrix: os: [ubuntu-22.04] - rust: [1.69, 1.68] + rust: [1.70.0, 1.69, 1.68] container: image: wasmedge/wasmedge:ubuntu-build-clang @@ -112,7 +115,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - rust: [1.69, 1.68] + rust: [1.70.0, 1.69, 1.68] container: image: fedora:latest steps: @@ -197,7 +200,7 @@ jobs: strategy: matrix: os: [macos-11, macos-12] - rust: [1.69, 1.68] + rust: [1.70.0, 1.69, 1.68] steps: - name: Checkout sources @@ -260,7 +263,7 @@ jobs: runs-on: windows-2022 strategy: matrix: - rust: [1.69, 1.68] + rust: [1.70.0, 1.69, 1.68] env: WASMEDGE_DIR: ${{ github.workspace }}\WasmEdge WASMEDGE_BUILD_DIR: ${{ github.workspace }}\WasmEdge\build diff --git a/crates/async-wasi/src/snapshots/mod.rs b/crates/async-wasi/src/snapshots/mod.rs index 7e6aeaf59..0a647c5a1 100644 --- a/crates/async-wasi/src/snapshots/mod.rs +++ b/crates/async-wasi/src/snapshots/mod.rs @@ -1,10 +1,11 @@ pub mod common; pub mod env; pub mod preview_1; -use common::error::Errno; use crate::object_pool::ObjectPool; +use common::error::Errno; use env::{wasi_types::__wasi_fd_t, VFD}; +use std::path::PathBuf; #[derive(Debug)] pub struct WasiCtx { @@ -44,7 +45,7 @@ impl WasiCtx { } } - pub fn push_preopen(&mut self, host_path: std::path::PathBuf, guest_path: std::path::PathBuf) { + pub fn push_preopen(&mut self, host_path: PathBuf, guest_path: PathBuf) { let preopen = env::vfs::WasiPreOpenDir::new(host_path, guest_path); self.vfs .push(VFD::Inode(env::vfs::INode::PreOpenDir(preopen))); @@ -55,11 +56,19 @@ impl WasiCtx { self.args.push(arg); } + pub fn push_args(&mut self, args: Vec) { + self.args.extend(args); + } + /// The format of the `env` argument should be "KEY=VALUE" pub fn push_env(&mut self, env: String) { self.envs.push(env); } + pub fn push_envs(&mut self, envs: Vec) { + self.envs.extend(envs); + } + fn remove_closed(&mut self) { if let Some(closed) = self.closed.take() { let _ = self.remove_vfd(closed); @@ -642,7 +651,7 @@ pub mod serialize { let mut wasi_ctx = super::WasiCtx::new(); wasi_ctx.push_arg("abc".to_string()); wasi_ctx.push_env("a=1".to_string()); - wasi_ctx.push_preopen(".".parse().unwrap(), ".".parse().unwrap()); + wasi_ctx.push_preopen(PathBuf::from("."), PathBuf::from(".")); // tcp4 let state = net::WasiSocketState::default(); diff --git a/crates/wasmedge-sys/src/async_wasi.rs b/crates/wasmedge-sys/src/async_wasi.rs index 9cd525a5c..5cfbfbae2 100644 --- a/crates/wasmedge-sys/src/async_wasi.rs +++ b/crates/wasmedge-sys/src/async_wasi.rs @@ -163,9 +163,9 @@ pub fn args_sizes_get( pub fn environ_get( frame: CallingFrame, args: Vec, - data: Option<&mut WasiCtx>, + ctx: Option<&mut WasiCtx>, ) -> std::result::Result, HostFuncError> { - let data = data.unwrap(); + let data = ctx.unwrap(); let mut mem = frame.memory_mut(0).ok_or(HostFuncError::Runtime(0x88))?; diff --git a/crates/wasmedge-sys/src/executor.rs b/crates/wasmedge-sys/src/executor.rs index 6f493d313..32770dd87 100644 --- a/crates/wasmedge-sys/src/executor.rs +++ b/crates/wasmedge-sys/src/executor.rs @@ -370,8 +370,6 @@ mod tests { AsImport, CallingFrame, Config, FuncType, Function, Global, GlobalType, ImportModule, MemType, Memory, Statistics, Table, TableType, }; - #[cfg(all(feature = "async", target_os = "linux"))] - use async_wasi::snapshots::WasiCtx; use std::{ sync::{Arc, Mutex}, thread, @@ -593,13 +591,8 @@ mod tests { assert!(result.is_ok()); let mut store = result.unwrap(); - // create async wasi context - let mut async_wasi_ctx = WasiCtx::new(); - async_wasi_ctx.push_arg("abc".into()); - async_wasi_ctx.push_env("a=1".into()); - // create an AsyncWasiModule - let result = AsyncWasiModule::create(Some(&mut async_wasi_ctx)); + let result = AsyncWasiModule::create(Some(vec!["abc"]), Some(vec![("a", "1")]), None); assert!(result.is_ok()); let async_wasi_module = result.unwrap(); @@ -650,13 +643,8 @@ mod tests { assert!(result.is_ok()); let mut store = result.unwrap(); - // create async wasi context - let mut async_wasi_ctx = WasiCtx::new(); - async_wasi_ctx.push_arg("abc".into()); - async_wasi_ctx.push_env("a=1".into()); - // create an AsyncWasiModule - let result = AsyncWasiModule::create(Some(&mut async_wasi_ctx)); + let result = AsyncWasiModule::create(Some(vec!["abc"]), Some(vec![("a", "1")]), None); assert!(result.is_ok()); let async_wasi_module = result.unwrap(); diff --git a/crates/wasmedge-sys/src/instance/module.rs b/crates/wasmedge-sys/src/instance/module.rs index 6f647aa57..22426bdbf 100644 --- a/crates/wasmedge-sys/src/instance/module.rs +++ b/crates/wasmedge-sys/src/instance/module.rs @@ -1,7 +1,10 @@ //! Defines WasmEdge Instance and other relevant types. #[cfg(all(feature = "async", target_os = "linux"))] -use crate::async_wasi::{wasi_impls, WasiFunc}; +use crate::{ + async_wasi::{wasi_impls, WasiFunc}, + WasiCtx, +}; use crate::{ error::{InstanceError, WasmEdgeError}, ffi, @@ -9,9 +12,9 @@ use crate::{ types::WasmEdgeString, Function, Global, Memory, Table, WasmEdgeResult, }; -#[cfg(all(feature = "async", target_os = "linux"))] -use async_wasi::snapshots::WasiCtx as AsyncWasiCtx; use std::sync::Arc; +#[cfg(all(feature = "async", target_os = "linux"))] +use std::{path::PathBuf, sync::Mutex}; /// An [Instance] represents an instantiated module. In the instantiation process, An [Instance] is created from al[Module](crate::Module). From an [Instance] the exported [functions](crate::Function), [tables](crate::Table), [memories](crate::Memory), and [globals](crate::Global) can be fetched. #[derive(Debug)] @@ -875,6 +878,7 @@ pub struct AsyncWasiModule { pub(crate) inner: Arc, pub(crate) registered: bool, name: String, + wasi_ctx: Arc>, } #[cfg(all(feature = "async", target_os = "linux"))] impl Drop for AsyncWasiModule { @@ -888,62 +892,150 @@ impl Drop for AsyncWasiModule { } #[cfg(all(feature = "async", target_os = "linux"))] impl AsyncWasiModule { - pub fn create(async_wasi_ctx: Option<&mut AsyncWasiCtx>) -> WasmEdgeResult { + pub fn create( + args: Option>, + envs: Option>, + preopens: Option>, + ) -> WasmEdgeResult { + // create wasi context + let mut wasi_ctx = WasiCtx::new(); + if let Some(args) = args { + wasi_ctx.push_args(args.iter().map(|x| x.to_string()).collect()); + } + if let Some(envs) = envs { + wasi_ctx.push_envs(envs.iter().map(|(k, v)| format!("{}={}", k, v)).collect()); + } + if let Some(preopens) = preopens { + for (host_dir, guest_dir) in preopens { + wasi_ctx.push_preopen(host_dir, guest_dir) + } + } + + // create wasi module let name = "wasi_snapshot_preview1"; let raw_name = WasmEdgeString::from(name); let ctx = unsafe { ffi::WasmEdge_ModuleInstanceCreate(raw_name.as_raw()) }; - if ctx.is_null() { return Err(Box::new(WasmEdgeError::Instance( InstanceError::CreateImportModule, ))); } - let mut async_wasi_module = Self { inner: std::sync::Arc::new(InnerInstance(ctx)), registered: false, name: name.to_string(), + wasi_ctx: Arc::new(Mutex::new(wasi_ctx)), }; // add sync/async host functions to the module - match async_wasi_ctx { - Some(ctx_data) => { - for wasi_func in wasi_impls() { - match wasi_func { - WasiFunc::SyncFn(name, (ty_args, ty_rets), real_fn) => { - let func_ty = crate::FuncType::create(ty_args, ty_rets)?; - let func = Function::create(&func_ty, real_fn, Some(ctx_data), 0)?; - async_wasi_module.add_func(name, func); - } - WasiFunc::AsyncFn(name, (ty_args, ty_rets), real_async_fn) => { - let func_ty = crate::FuncType::create(ty_args, ty_rets)?; - let func = - Function::create_async(&func_ty, real_async_fn, Some(ctx_data), 0)?; - async_wasi_module.add_func(name, func); - } - } + for wasi_func in wasi_impls() { + match wasi_func { + WasiFunc::SyncFn(name, (ty_args, ty_rets), real_fn) => { + let func_ty = crate::FuncType::create(ty_args, ty_rets)?; + let func = Function::create( + &func_ty, + real_fn, + Some( + &mut async_wasi_module + .wasi_ctx + .lock() + .expect("[wasmedge-sys] failed to get the lock on wasi_ctx"), + ), + 0, + )?; + async_wasi_module.add_func(name, func); } - } - None => { - for wasi_func in wasi_impls() { - match wasi_func { - WasiFunc::SyncFn(name, (ty_args, ty_rets), real_fn) => { - let func_ty = crate::FuncType::create(ty_args, ty_rets)?; - let func = Function::create(&func_ty, real_fn, None, 0)?; - async_wasi_module.add_func(name, func); - } - WasiFunc::AsyncFn(name, (ty_args, ty_rets), real_async_fn) => { - let func_ty = crate::FuncType::create(ty_args, ty_rets)?; - let func = Function::create_async(&func_ty, real_async_fn, None, 0)?; - async_wasi_module.add_func(name, func); - } - } + WasiFunc::AsyncFn(name, (ty_args, ty_rets), real_async_fn) => { + let func_ty = crate::FuncType::create(ty_args, ty_rets)?; + let func = Function::create_async( + &func_ty, + real_async_fn, + Some( + &mut async_wasi_module + .wasi_ctx + .lock() + .expect("[wasmedge-sys] failed to get the lock on wasi_ctx"), + ), + 0, + )?; + async_wasi_module.add_func(name, func); } } - }; + } Ok(async_wasi_module) } + + pub fn init_wasi( + &mut self, + args: Option>, + envs: Option>, + preopens: Option>, + ) -> WasmEdgeResult<()> { + // create wasi context + let mut wasi_ctx = WasiCtx::new(); + if let Some(args) = args { + wasi_ctx.push_args(args.iter().map(|x| x.to_string()).collect()); + } + if let Some(envs) = envs { + wasi_ctx.push_envs(envs.iter().map(|(k, v)| format!("{}={}", k, v)).collect()); + } + if let Some(preopens) = preopens { + for (host_dir, guest_dir) in preopens { + wasi_ctx.push_preopen(host_dir, guest_dir) + } + } + + self.wasi_ctx = Arc::new(Mutex::new(wasi_ctx)); + + // add sync/async host functions to the module + for wasi_func in wasi_impls() { + match wasi_func { + WasiFunc::SyncFn(name, (ty_args, ty_rets), real_fn) => { + let func_ty = crate::FuncType::create(ty_args, ty_rets)?; + let func = Function::create( + &func_ty, + real_fn, + Some( + &mut self + .wasi_ctx + .lock() + .expect("[wasmedge-sys] failed to get the lock on wasi_ctx"), + ), + 0, + )?; + self.add_func(name, func); + } + WasiFunc::AsyncFn(name, (ty_args, ty_rets), real_async_fn) => { + let func_ty = crate::FuncType::create(ty_args, ty_rets)?; + let func = Function::create_async( + &func_ty, + real_async_fn, + Some( + &mut self + .wasi_ctx + .lock() + .expect("[wasmedge-sys] failed to get the lock on wasi_ctx"), + ), + 0, + )?; + self.add_func(name, func); + } + } + } + + Ok(()) + } + + /// Returns the WASI exit code. + /// + /// The WASI exit code can be accessed after running the "_start" function of a `wasm32-wasi` program. + pub fn exit_code(&self) -> u32 { + self.wasi_ctx + .lock() + .expect("[wasmedge-sys] failed to get the lock on wasi_ctx") + .exit_code + } } #[cfg(all(feature = "async", target_os = "linux"))] impl AsInstance for AsyncWasiModule { diff --git a/crates/wasmedge-sys/src/lib.rs b/crates/wasmedge-sys/src/lib.rs index 5d199046c..6bdea4991 100644 --- a/crates/wasmedge-sys/src/lib.rs +++ b/crates/wasmedge-sys/src/lib.rs @@ -114,6 +114,9 @@ pub use types::WasmValue; pub use validator::Validator; use wasmedge_types::{error, WasmEdgeResult}; +#[cfg(all(feature = "async", target_os = "linux"))] +pub type WasiCtx = ::async_wasi::snapshots::WasiCtx; + /// The object that is used to perform a [host function](crate::Function) is required to implement this trait. pub trait Engine { /// Runs a host function instance and returns the results. diff --git a/examples/async_hello_world.rs b/examples/async_hello_world.rs index 48e379173..0d6bda036 100644 --- a/examples/async_hello_world.rs +++ b/examples/async_hello_world.rs @@ -4,13 +4,13 @@ //! cargo run -p wasmedge-sdk --features async --example async_hello_world -- --nocapture //! ``` //! -#[cfg(feature = "async")] +#[cfg(all(feature = "async", target_os = "linux"))] use wasmedge_sdk::{ async_host_function, error::HostFuncError, params, r#async::AsyncState, Caller, ImportObjectBuilder, NeverType, VmBuilder, WasmValue, }; -#[cfg(feature = "async")] +#[cfg(all(feature = "async", target_os = "linux"))] #[async_host_function] async fn say_hello( caller: Caller, @@ -43,7 +43,7 @@ async fn say_hello( #[tokio::main] async fn main() -> Result<(), Box> { - #[cfg(feature = "async")] + #[cfg(all(feature = "async", target_os = "linux"))] { // create an import module let import = ImportObjectBuilder::new() diff --git a/examples/async_run_func.rs b/examples/async_run_func.rs index 526168011..b9e96d9ac 100644 --- a/examples/async_run_func.rs +++ b/examples/async_run_func.rs @@ -5,7 +5,7 @@ //! cd /bindings/rust/ //! cargo run -p wasmedge-sdk --features async --example async_run_func //! ``` -#[cfg(feature = "async")] +#[cfg(all(feature = "async", target_os = "linux"))] use wasmedge_sdk::{ config::{CommonConfigOptions, ConfigBuilder}, params, @@ -15,7 +15,7 @@ use wasmedge_sdk::{ #[tokio::main] async fn main() -> Result<(), Box> { - #[cfg(feature = "async")] + #[cfg(all(feature = "async", target_os = "linux"))] { let wasm_file = std::env::current_dir()?.join("examples/data/fibonacci.wat"); diff --git a/examples/create_host_func_in_host_func.rs b/examples/create_host_func_in_host_func.rs index 016088d67..e9aeb674c 100644 --- a/examples/create_host_func_in_host_func.rs +++ b/examples/create_host_func_in_host_func.rs @@ -1,8 +1,10 @@ +#[cfg(not(feature = "async"))] use wasmedge_sdk::{ error::HostFuncError, host_function, params, Caller, Executor, Func, ImportObjectBuilder, NeverType, ValType, VmBuilder, WasmVal, WasmValue, }; +#[cfg(not(feature = "async"))] #[host_function] fn func( _caller: Caller, @@ -65,15 +67,18 @@ fn func( #[cfg_attr(test, test)] fn main() -> anyhow::Result<()> { - // create an import module - let import = ImportObjectBuilder::new() - .with_func::<(), (), NeverType>("outer-func", func, None)? - .build("extern")?; + #[cfg(not(feature = "async"))] + { + // create an import module + let import = ImportObjectBuilder::new() + .with_func::<(), (), NeverType>("outer-func", func, None)? + .build("extern")?; - let _ = VmBuilder::new() - .build()? - .register_import_module(import)? - .run_func(Some("extern"), "outer-func", params!())?; + let _ = VmBuilder::new() + .build()? + .register_import_module(import)? + .run_func(Some("extern"), "outer-func", params!())?; + } Ok(()) } diff --git a/examples/hello_world.rs b/examples/hello_world.rs index ac1b3e870..d62188b7c 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -1,3 +1,4 @@ +#[cfg(not(feature = "async"))] use wasmedge_sdk::{ self, error::HostFuncError, host_function, params, wat2wasm, Caller, ImportObjectBuilder, Module, NeverType, VmBuilder, WasmValue, @@ -5,6 +6,7 @@ use wasmedge_sdk::{ // We define a function to act as our "env" "say_hello" function imported in the // Wasm program above. +#[cfg(not(feature = "async"))] #[host_function] pub fn say_hello( _caller: Caller, @@ -18,13 +20,15 @@ pub fn say_hello( #[cfg_attr(test, test)] fn main() -> anyhow::Result<()> { - // create an import module - let import = ImportObjectBuilder::new() - .with_func::<(), (), NeverType>("say_hello", say_hello, None)? - .build("env")?; + #[cfg(not(feature = "async"))] + { + // create an import module + let import = ImportObjectBuilder::new() + .with_func::<(), (), NeverType>("say_hello", say_hello, None)? + .build("env")?; - let wasm_bytes = wat2wasm( - br#" + let wasm_bytes = wat2wasm( + br#" (module ;; First we define a type with no parameters and no results. (type $no_args_no_rets_t (func (param) (result))) @@ -39,17 +43,17 @@ fn main() -> anyhow::Result<()> { ;; And mark it as an exported function named "run". (export "run" (func $run))) "#, - )?; + )?; - // loads a wasm module from the given in-memory bytes - let module = Module::from_bytes(None, wasm_bytes)?; - - // create an executor - VmBuilder::new() - .build()? - .register_import_module(import)? - .register_module(Some("extern"), module)? - .run_func(Some("extern"), "run", params!())?; + // loads a wasm module from the given in-memory bytes + let module = Module::from_bytes(None, wasm_bytes)?; + // create an executor + VmBuilder::new() + .build()? + .register_import_module(import)? + .register_module(Some("extern"), module)? + .run_func(Some("extern"), "run", params!())?; + } Ok(()) } diff --git a/examples/host_func_with_custom_error.rs b/examples/host_func_with_custom_error.rs index 0e5159769..c03205b98 100644 --- a/examples/host_func_with_custom_error.rs +++ b/examples/host_func_with_custom_error.rs @@ -13,7 +13,9 @@ //! ``` //! which means that the one or two input numbers of i32 type of the host function are out of upper bound (here is 100). +#[cfg(not(feature = "async"))] use thiserror::Error; +#[cfg(not(feature = "async"))] use wasmedge_sdk::{ config::ConfigBuilder, error::{HostFuncError, WasmEdgeError}, @@ -23,6 +25,7 @@ use wasmedge_sdk::{ }; // Define custom error type +#[cfg(not(feature = "async"))] #[derive(Error, Clone, Debug, PartialEq, Eq)] enum MyError { #[error("The number of arguments should be 3")] @@ -34,6 +37,7 @@ enum MyError { #[error("The value of argument is out of upper bound.")] OutOfUpperBound, } +#[cfg(not(feature = "async"))] impl From for u32 { fn from(err: MyError) -> Self { match err { @@ -44,6 +48,7 @@ impl From for u32 { } } } +#[cfg(not(feature = "async"))] impl From for MyError { fn from(err: u32) -> Self { match err { @@ -57,6 +62,7 @@ impl From for MyError { } // compute the sum of two numbers, which are less than 100 +#[cfg(not(feature = "async"))] #[host_function] fn real_add( _caller: Caller, @@ -98,34 +104,38 @@ fn real_add( #[cfg_attr(test, test)] fn main() -> anyhow::Result<()> { - // create import module - let import = ImportObjectBuilder::new() - .with_func::<(ExternRef, i32, i32), i32, NeverType>("add", real_add, None)? - .build("extern_module")?; + #[cfg(not(feature = "async"))] + { + // create import module + let import = ImportObjectBuilder::new() + .with_func::<(ExternRef, i32, i32), i32, NeverType>("add", real_add, None)? + .build("extern_module")?; - // create a vm instance - let config = ConfigBuilder::default().build()?; - let mut vm = VmBuilder::new() - .with_config(config) - .build()? - .register_import_module(import)?; + // create a vm instance + let config = ConfigBuilder::default().build()?; + let mut vm = VmBuilder::new() + .with_config(config) + .build()? + .register_import_module(import)?; - // run the export wasm function named "call_add" from func.wasm - let wasm_file = std::env::current_dir()?.join("crates/wasmedge-sys/examples/data/funcs.wat"); - let add_ref = ExternRef::new(&mut real_add::); - let a: i32 = 1234; - let b: i32 = 5678; - match vm.run_func_from_file(wasm_file, "call_add", params!(add_ref, a, b)) { - Ok(returns) => { - let ret = returns[0].to_i32(); - assert_eq!(ret, 1234 + 5678); - println!("result from call_add: {ret}") - } - Err(e) => match *e { - WasmEdgeError::User(code) => println!("User error: {:?}", MyError::from(code)), - err => println!("Runtime error: {err:?}"), - }, - }; + // run the export wasm function named "call_add" from func.wasm + let wasm_file = + std::env::current_dir()?.join("crates/wasmedge-sys/examples/data/funcs.wat"); + let add_ref = ExternRef::new(&mut real_add::); + let a: i32 = 1234; + let b: i32 = 5678; + match vm.run_func_from_file(wasm_file, "call_add", params!(add_ref, a, b)) { + Ok(returns) => { + let ret = returns[0].to_i32(); + assert_eq!(ret, 1234 + 5678); + println!("result from call_add: {ret}") + } + Err(e) => match *e { + WasmEdgeError::User(code) => println!("User error: {:?}", MyError::from(code)), + err => println!("Runtime error: {err:?}"), + }, + }; + } Ok(()) } diff --git a/examples/hostfunc_call_chain.rs b/examples/hostfunc_call_chain.rs index 780c6313a..638e6a173 100644 --- a/examples/hostfunc_call_chain.rs +++ b/examples/hostfunc_call_chain.rs @@ -16,6 +16,7 @@ //! There is layer2! //! ``` +#[cfg(not(feature = "async"))] use wasmedge_sdk::{ error::HostFuncError, params, wat2wasm, Caller, CallingFrame, ImportObjectBuilder, Module, NeverType, VmBuilder, WasmValue, @@ -23,43 +24,45 @@ use wasmedge_sdk::{ #[cfg_attr(test, test)] fn main() -> Result<(), Box> { - let vm = VmBuilder::new().build()?; + #[cfg(not(feature = "async"))] + { + let vm = VmBuilder::new().build()?; - let host_layer1 = |_frame: CallingFrame, - _args: Vec, - _data: Option<&mut NeverType>| - -> Result, HostFuncError> { - println!("There is layer1!"); - Ok(vec![]) - }; + let host_layer1 = |_frame: CallingFrame, + _args: Vec, + _data: Option<&mut NeverType>| + -> Result, HostFuncError> { + println!("There is layer1!"); + Ok(vec![]) + }; - let host_layer2 = move |frame: CallingFrame, - _args: Vec, - _data: Option<&mut NeverType>| - -> Result, HostFuncError> { - let caller = Caller::new(frame); - let executor = caller.executor().unwrap(); - let active_instance = caller.instance().unwrap(); - let fn_host_layer1 = active_instance - .func("layer1") - .expect("fail to find host function 'host_layer1'"); - fn_host_layer1.run(executor, params!()).unwrap(); + let host_layer2 = move |frame: CallingFrame, + _args: Vec, + _data: Option<&mut NeverType>| + -> Result, HostFuncError> { + let caller = Caller::new(frame); + let executor = caller.executor().unwrap(); + let active_instance = caller.instance().unwrap(); + let fn_host_layer1 = active_instance + .func("layer1") + .expect("fail to find host function 'host_layer1'"); + fn_host_layer1.run(executor, params!()).unwrap(); - println!("There is layer2!"); - Ok(vec![]) - }; + println!("There is layer2!"); + Ok(vec![]) + }; - // create an import object - let import = ImportObjectBuilder::new() - .with_func::<(), (), NeverType>("layer1", host_layer1, None)? - .with_func::<(), (), NeverType>("layer2", host_layer2, None)? - .build("host")?; + // create an import object + let import = ImportObjectBuilder::new() + .with_func::<(), (), NeverType>("layer1", host_layer1, None)? + .with_func::<(), (), NeverType>("layer2", host_layer2, None)? + .build("host")?; - // register the import object into vm - let vm = vm.register_import_module(import)?; + // register the import object into vm + let vm = vm.register_import_module(import)?; - let wasm_bytes = wat2wasm( - br#" + let wasm_bytes = wat2wasm( + br#" (module (import "host" "layer1" (func $host_layer1)) (import "host" "layer2" (func $host_layer2)) @@ -70,15 +73,16 @@ fn main() -> Result<(), Box> { call $host_layer2) ) "#, - )?; + )?; - let module = Module::from_bytes(None, wasm_bytes)?; + let module = Module::from_bytes(None, wasm_bytes)?; - // register the wasm module into vm - let vm = vm.register_module(None, module)?; + // register the wasm module into vm + let vm = vm.register_module(None, module)?; - // call the host function - vm.run_func(None, "layer2", params!())?; + // call the host function + vm.run_func(None, "layer2", params!())?; + } Ok(()) } diff --git a/examples/run_func_in_aot_mode.rs b/examples/run_func_in_aot_mode.rs index 56b056fe2..b3d37e2e5 100644 --- a/examples/run_func_in_aot_mode.rs +++ b/examples/run_func_in_aot_mode.rs @@ -5,9 +5,9 @@ //! cargo run -p wasmedge-sdk --example run_func_in_aot_mode -- --nocapture //! ``` -#[cfg(all(feature = "aot", target_family = "unix"))] +#[cfg(all(feature = "aot", target_family = "unix", not(feature = "async")))] use std::os::unix::fs::PermissionsExt; -#[cfg(all(feature = "aot", target_family = "unix"))] +#[cfg(all(feature = "aot", target_family = "unix", not(feature = "async")))] use wasmedge_sdk::{ config::{ CommonConfigOptions, CompilerConfigOptions, ConfigBuilder, HostRegistrationConfigOptions, @@ -17,7 +17,7 @@ use wasmedge_sdk::{ #[cfg_attr(test, test)] fn main() -> Result<(), Box> { - #[cfg(all(feature = "aot", target_family = "unix"))] + #[cfg(all(feature = "aot", target_family = "unix", not(feature = "async")))] { // create a Config context let config = ConfigBuilder::new(CommonConfigOptions::new().bulk_memory_operations(true)) diff --git a/examples/vm.rs b/examples/vm.rs index ad778ae0c..9984a5e76 100644 --- a/examples/vm.rs +++ b/examples/vm.rs @@ -3,17 +3,21 @@ // If the version of rust used is less than v1.63, please uncomment the follow attribute. // #![feature(explicit_generic_args_with_impl_trait)] +#[cfg(not(feature = "async"))] use wasmedge_sdk::{params, VmBuilder, WasmVal}; +#[cfg(not(feature = "async"))] use wasmedge_types::wat2wasm; #[cfg_attr(test, test)] fn main() -> Result<(), Box> { - // create a Vm context - let vm = VmBuilder::new().build()?; + #[cfg(not(feature = "async"))] + { + // create a Vm context + let vm = VmBuilder::new().build()?; - // register a wasm module from the given in-memory wasm bytes - let wasm_bytes = wat2wasm( - br#"(module + // register a wasm module from the given in-memory wasm bytes + let wasm_bytes = wat2wasm( + br#"(module (export "fib" (func $fib)) (func $fib (param $n i32) (result i32) (if @@ -44,13 +48,14 @@ fn main() -> Result<(), Box> { ) ) "#, - )?; - let vm = vm.register_module_from_bytes("extern", wasm_bytes)?; + )?; + let vm = vm.register_module_from_bytes("extern", wasm_bytes)?; - // run `fib` function in the named module instance - let returns = vm.run_func(Some("extern"), "fib", params!(10))?; - assert_eq!(returns.len(), 1); - assert_eq!(returns[0].to_i32(), 89); + // run `fib` function in the named module instance + let returns = vm.run_func(Some("extern"), "fib", params!(10))?; + assert_eq!(returns.len(), 1); + assert_eq!(returns[0].to_i32(), 89); + } Ok(()) } diff --git a/src/compiler.rs b/src/compiler.rs index 0b60dcd42..904196bdf 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -91,6 +91,7 @@ impl Compiler { } } +#[cfg(not(feature = "async"))] #[cfg(test)] mod tests { use super::*; diff --git a/src/executor.rs b/src/executor.rs index 8d15b406b..aed60ea0f 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -131,9 +131,9 @@ mod tests { config::{CommonConfigOptions, ConfigBuilder}, params, wat2wasm, Module, Statistics, Store, WasmVal, }; - #[cfg(feature = "async")] + #[cfg(all(feature = "async", target_os = "linux"))] use crate::{error::HostFuncError, CallingFrame}; - #[cfg(feature = "async")] + #[cfg(all(feature = "async", target_os = "linux"))] use wasmedge_types::NeverType; #[test] diff --git a/src/lib.rs b/src/lib.rs index 0468d2077..24136b1d1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,6 +35,7 @@ //! The example below is using `wasmedge-sdk` to run a WebAssembly module written with its WAT format (textual format). If you would like more examples, please refer to [Examples of WasmEdge RustSDK](https://github.com/second-state/wasmedge-rustsdk-examples). //! //! ```rust +//! #[cfg(not(feature = "async"))] //! use wasmedge_sdk::{ //! error::HostFuncError, host_function, params, wat2wasm, Caller, ImportObjectBuilder, Module, //! VmBuilder, WasmValue, NeverType @@ -42,6 +43,7 @@ //! //! // We define a function to act as our "env" "say_hello" function imported in the //! // Wasm program above. +//! #[cfg(not(feature = "async"))] //! #[host_function] //! pub fn say_hello(_caller: Caller, _args: Vec, _data: Option<&mut T>) -> Result, HostFuncError> { //! println!("Hello, world!"); @@ -51,38 +53,41 @@ //! //! #[cfg_attr(test, test)] //! fn main() -> anyhow::Result<()> { -//! // create an import module -//! let import = ImportObjectBuilder::new() -//! .with_func::<(), (), NeverType>("say_hello", say_hello, None)? -//! .build("env")?; -//! -//! let wasm_bytes = wat2wasm( -//! br#" -//! (module -//! ;; First we define a type with no parameters and no results. -//! (type $no_args_no_rets_t (func (param) (result))) +//! #[cfg(not(feature = "async"))] +//! { +//! // create an import module +//! let import = ImportObjectBuilder::new() +//! .with_func::<(), (), NeverType>("say_hello", say_hello, None)? +//! .build("env")?; //! -//! ;; Then we declare that we want to import a function named "env" "say_hello" with -//! ;; that type signature. -//! (import "env" "say_hello" (func $say_hello (type $no_args_no_rets_t))) +//! let wasm_bytes = wat2wasm( +//! br#" +//! (module +//! ;; First we define a type with no parameters and no results. +//! (type $no_args_no_rets_t (func (param) (result))) +//! +//! ;; Then we declare that we want to import a function named "env" "say_hello" with +//! ;; that type signature. +//! (import "env" "say_hello" (func $say_hello (type $no_args_no_rets_t))) +//! +//! ;; Finally we create an entrypoint that calls our imported function. +//! (func $run (type $no_args_no_rets_t) +//! (call $say_hello)) +//! ;; And mark it as an exported function named "run". +//! (export "run" (func $run))) +//! "#, +//! )?; //! -//! ;; Finally we create an entrypoint that calls our imported function. -//! (func $run (type $no_args_no_rets_t) -//! (call $say_hello)) -//! ;; And mark it as an exported function named "run". -//! (export "run" (func $run))) -//! "#, -//! )?; -//! -//! // loads a wasm module from the given in-memory bytes -//! let module = Module::from_bytes(None, wasm_bytes)?; -//! -//! // create an executor -//! VmBuilder::new() -//! .build()? -//! .register_import_module(import)? -//! .register_module(Some("extern"), module)? -//! .run_func(Some("extern"), "run", params!())?; +//! // loads a wasm module from the given in-memory bytes +//! let module = Module::from_bytes(None, wasm_bytes)?; +//! +//! // create an executor +//! VmBuilder::new() +//! .build()? +//! .register_import_module(import)? +//! .register_module(Some("extern"), module)? +//! .run_func(Some("extern"), "run", params!())?; +//! } //! //! Ok(()) //! } @@ -166,6 +171,9 @@ pub mod r#async { pub type AsyncHostFn = wasmedge_sys::AsyncHostFn; } +#[cfg(all(feature = "async", target_os = "linux"))] +pub type WasiCtx = wasmedge_sys::WasiCtx; + /// The object that is used to perform a [host function](crate::Func) is required to implement this trait. pub trait Engine { /// Runs a host function instance and returns the results. diff --git a/src/vm.rs b/src/vm.rs index 2a100dbc8..c279fb588 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -102,6 +102,7 @@ impl VmBuilder { /// # Error /// /// If fail to create, then an error is returned. + #[cfg(not(feature = "async"))] pub fn build(mut self) -> WasmEdgeResult { // executor let executor = Executor::new(self.config.as_ref(), self.stat.as_mut())?; @@ -126,7 +127,6 @@ impl VmBuilder { }; // * built-in host instances - #[cfg(not(feature = "async"))] if let Some(cfg) = vm.config.as_ref() { if cfg.wasi_enabled() { if let Ok(wasi_module) = sys::WasiModule::create(None, None, None) { @@ -144,10 +144,57 @@ impl VmBuilder { } } } - #[cfg(all(feature = "async", target_os = "linux"))] + + // * load and register plugin instances + for (pname, mname) in self.plugins.iter() { + match Self::create_plugin_instance(pname, mname) { + Some(instance) => { + vm.plugin_host_instances.push(instance); + vm.executor.inner.register_plugin_instance( + &mut vm.store.inner, + &vm.plugin_host_instances.last().unwrap().inner, + )?; + } + None => panic!("Not found {}::{} plugin", pname, mname), + } + } + + Ok(vm) + } + + /// Creates a new [Vm]. + /// + /// # Error + /// + /// If fail to create, then an error is returned. + #[cfg(all(feature = "async", target_os = "linux"))] + pub fn build(mut self) -> WasmEdgeResult { + // executor + let executor = Executor::new(self.config.as_ref(), self.stat.as_mut())?; + + // store + let store = match self.store { + Some(store) => store, + None => Store::new()?, + }; + + // create a Vm instance + let mut vm = Vm { + config: self.config, + stat: self.stat, + executor, + store, + named_instances: HashMap::new(), + active_instance: None, + imports: Vec::new(), + builtin_host_instances: HashMap::new(), + plugin_host_instances: Vec::new(), + }; + + // * built-in host instances if let Some(cfg) = vm.config.as_ref() { if cfg.wasi_enabled() { - if let Ok(wasi_module) = sys::AsyncWasiModule::create(None) { + if let Ok(wasi_module) = sys::AsyncWasiModule::create(None, None, None) { vm.executor.inner.register_import_object( &mut vm.store.inner, &sys::ImportObject::AsyncWasi(wasi_module.clone()), @@ -197,55 +244,57 @@ impl VmBuilder { /// ```rust /// // If the version of rust used is less than v1.63, please uncomment the follow attribute. /// // #![feature(explicit_generic_args_with_impl_trait)] -/// -/// use wasmedge_sdk::{params, VmBuilder, WasmVal}; -/// use wasmedge_types::{wat2wasm, ValType}; +/// #[cfg(not(feature = "async"))] +/// use wasmedge_sdk::{params, VmBuilder, WasmVal, wat2wasm, ValType}; /// /// #[cfg_attr(test, test)] /// fn main() -> Result<(), Box> { -/// // create a Vm context -/// let vm = VmBuilder::new().build()?; +/// #[cfg(not(feature = "async"))] +/// { +/// // create a Vm context +/// let vm = VmBuilder::new().build()?; /// -/// // register a wasm module from the given in-memory wasm bytes -/// let wasm_bytes = wat2wasm( -/// br#"(module -/// (export "fib" (func $fib)) -/// (func $fib (param $n i32) (result i32) -/// (if -/// (i32.lt_s -/// (get_local $n) -/// (i32.const 2) -/// ) -/// (return -/// (i32.const 1) -/// ) -/// ) -/// (return -/// (i32.add -/// (call $fib -/// (i32.sub -/// (get_local $n) -/// (i32.const 2) -/// ) -/// ) -/// (call $fib -/// (i32.sub -/// (get_local $n) -/// (i32.const 1) +/// // register a wasm module from the given in-memory wasm bytes +/// let wasm_bytes = wat2wasm( +/// br#"(module +/// (export "fib" (func $fib)) +/// (func $fib (param $n i32) (result i32) +/// (if +/// (i32.lt_s +/// (get_local $n) +/// (i32.const 2) +/// ) +/// (return +/// (i32.const 1) +/// ) +/// ) +/// (return +/// (i32.add +/// (call $fib +/// (i32.sub +/// (get_local $n) +/// (i32.const 2) +/// ) +/// ) +/// (call $fib +/// (i32.sub +/// (get_local $n) +/// (i32.const 1) +/// ) +/// ) +/// ) +/// ) /// ) /// ) -/// ) -/// ) -/// ) -/// ) -/// "#, -/// )?; -/// let mut vm = vm.register_module_from_bytes("extern", wasm_bytes)?; +/// "#, +/// )?; +/// let mut vm = vm.register_module_from_bytes("extern", wasm_bytes)?; /// -/// // run `fib` function in the named module instance -/// let returns = vm.run_func(Some("extern"), "fib", params!(10))?; -/// assert_eq!(returns.len(), 1); -/// assert_eq!(returns[0].to_i32(), 89); +/// // run `fib` function in the named module instance +/// let returns = vm.run_func(Some("extern"), "fib", params!(10))?; +/// assert_eq!(returns.len(), 1); +/// assert_eq!(returns[0].to_i32(), 89); +/// } /// /// Ok(()) /// } @@ -799,6 +848,7 @@ enum HostRegistrationInstance { Wasi(crate::wasi::WasiInstance), } +#[cfg(not(feature = "async"))] #[cfg(test)] mod tests { use super::*; diff --git a/src/wasi.rs b/src/wasi.rs index 9f0bf5dc3..a6b553c45 100644 --- a/src/wasi.rs +++ b/src/wasi.rs @@ -177,6 +177,48 @@ pub struct WasiInstance { pub(crate) inner: sys::AsyncWasiModule, } #[cfg(all(feature = "async", target_os = "linux"))] +impl WasiInstance { + /// Initializes the WASI host module with the given parameters. + /// + /// # Arguments + /// + /// * `args` - The commandline arguments. Note that the first argument is the program name. + /// + /// * `envs` - The environment variables. + /// + /// * `preopens` - The directories to pre-open. + /// + /// # Example + /// + /// ```ignore + /// let wasi_module = vm.wasi_module_mut().ok_or("failed to get wasi module")?; + /// wasi_module.initialize( + /// None, + /// Some(vec![("ENV", "VAL")]), + /// Some(vec![( + /// std::path::PathBuf::from("."), + /// std::path::PathBuf::from("."), + /// )]), + /// )?; + /// ``` + /// + pub fn initialize( + &mut self, + args: Option>, + envs: Option>, + preopens: Option>, + ) -> WasmEdgeResult<()> { + self.inner.init_wasi(args, envs, preopens) + } + + /// Returns the WASI exit code. + /// + /// The WASI exit code can be accessed after running the "_start" function of a `wasm32-wasi` program. + pub fn exit_code(&self) -> u32 { + self.inner.exit_code() + } +} +#[cfg(all(feature = "async", target_os = "linux"))] impl AsInstance for WasiInstance { fn name(&self) -> &str { self.inner.name()