Table and FuncRef
In this example, we'll present how to use Table and FuncRef stored in a slot of a Table
instance to implement indirect function invocation.
The code in the following example is verified on
- wasmedge-sdk v0.5.0
- wasmedge-sys v0.10.0
- wasmedge-types v0.3.0
Let's start off by getting all imports right away so you can follow along
#![allow(unused)] fn main() { // If the version of rust used is less than v1.63, please uncomment the follow attribute. // #![feature(explicit_generic_args_with_impl_trait)] #![feature(never_type)] use wasmedge_sdk::{ config::{CommonConfigOptions, ConfigBuilder}, error::HostFuncError, host_function, params, types::Val, Caller, Executor, Func, ImportObjectBuilder, RefType, Store, Table, TableType, ValType, WasmVal, WasmValue, }; }
Define host function
In this example we defines a native function real_add
that takes two numbers and returns their sum. This function will be registered as a host function into WasmEdge runtime environment
#![allow(unused)] fn main() { #[host_function] fn real_add(_caller: &Caller, input: Vec<WasmValue>) -> Result<Vec<WasmValue>, HostFuncError> { println!("Rust: Entering Rust function real_add"); if input.len() != 2 { return Err(HostFuncError::User(1)); } let a = if input[0].ty() == ValType::I32 { input[0].to_i32() } else { return Err(HostFuncError::User(2)); }; let b = if input[1].ty() == ValType::I32 { input[1].to_i32() } else { return Err(HostFuncError::User(3)); }; let c = a + b; println!("Rust: calculating in real_add c: {:?}", c); println!("Rust: Leaving Rust function real_add"); Ok(vec![WasmValue::from_i32(c)]) } }
Register Table instance
The first thing we need to do is to create a Table
instance. After that, we register the table instance along with an import module into the WasmEdge runtime environment. Now let's see the code.
#![allow(unused)] fn main() { // create an executor let config = ConfigBuilder::new(CommonConfigOptions::default()).build()?; let mut executor = Executor::new(Some(&config), None)?; // create a store let mut store = Store::new()?; // create a table instance let result = Table::new(TableType::new(RefType::FuncRef, 10, Some(20))); assert!(result.is_ok()); let table = result.unwrap(); // create an import object let import = ImportObjectBuilder::new() .with_table("my-table", table)? .build("extern")?; // register the import object into the store store.register_import_module(&mut executor, &import)?; }
In the code snippet above, we create a Table
instance with the initial size of 10
and the maximum size of 20. The element type of the Table
instance is reference to function
.
Store a function reference into Table
In the previous steps, we defined a native function real_add
and registered a Table
instance named my-table
into the runtime environment. Now we'll save a reference to read_add
function to a slot of my-table
.
#![allow(unused)] fn main() { // get the imported module instance let instance = store .module_instance("extern") .expect("Not found module instance named 'extern'"); // get the exported table instance let mut table = instance .table("my-table") .expect("Not found table instance named 'my-table'"); // create a host function let host_func = Func::wrap::<(i32, i32), i32, !>(Box::new(real_add), None)?; // store the reference to host_func at the given index of the table instance table.set(3, Val::FuncRef(Some(host_func.as_ref())))?; }
We save the reference to host_func
into the third slot of my-table
. Next, we can retrieve the function reference from the table instance by index and call the function via its reference.
Call native function via FuncRef
#![allow(unused)] fn main() { // retrieve the function reference at the given index of the table instance let value = table.get(3)?; if let Val::FuncRef(Some(func_ref)) = value { // get the function type by func_ref let func_ty = func_ref.ty()?; // arguments assert_eq!(func_ty.args_len(), 2); let param_tys = func_ty.args().unwrap(); assert_eq!(param_tys, [ValType::I32, ValType::I32]); // returns assert_eq!(func_ty.returns_len(), 1); let return_tys = func_ty.returns().unwrap(); assert_eq!(return_tys, [ValType::I32]); // call the function by func_ref let returns = func_ref.call(&mut executor, params!(1, 2))?; assert_eq!(returns.len(), 1); assert_eq!(returns[0].to_i32(), 3); } }
The complete code of this example can be found in table_and_funcref.rs.