Introduction to WasmEdge module instance

Overview

In this section, we will talk about module instance. In wasmedge-sys crate, four kinds of module instances are defined:

  • Instance

  • An Instance represents a runtime module instance which is held by a WasmEdge Store context. The Store context is either held by a WasmEdge Vm, or related to a WasmEdge Executor.

  • APIs to retrieve an Instance.

    • If a Vm is available, then

    • with the Vm::active_module API, you can get an anonymous module instance from this Vm.

    • with the Vm::store_mut and Store::module APIs, you can get a named module instance from this Vm.
    • If an Executor is available, then

    • with the Executor::register_named_module API, you can get a named module instance from this Executor.

    • with the Executor::register_active_module API, you can get an anonymous module instance from this Executor.
    • ImportModule
  • ImportModule, also called import module, represents a module instance to be registered into a WasmEdge Vm or Executor. ImportModule implements the ImportObject trait, meaning that WebAssembly function, table, memory and global instances can be added to an import module, and then be registered and instantiated together when the import module is registered into a Vm or Executor.

  • WasiModule and WasmEdgeProcessModule

  • WasiModule and WasmEdgeProcessModule are module instances for WASI and WasmEdgeProcess specification, respectively. They also implement the ImportObject trait. Different from ImportModule, these two kinds of module instances can not only be created, but be retrieved from a Vm.

  • APIs to retrieve WasiModule and WasmEdgeProcessModule.

    • If a Vm is available, then

    • with the Vm::wasi_module API, you can get a module instance of WasiModule type.

    • with the Vm::wasmedge_process_module API, you can get a WasmEdgeProcessModule from this Vm.

Examples

Example 1

In this example, we’ll demonstrate how to use the APIs of Vm to

  • Create Wasi and WasmEdgeProcess module instances implicitly by using a Config while creating a Vm.

``` 2. #![allow(unused)]

  1. fn main() {
  2. // create a Config context
  3. let mut config = Config::create()?;
  4. config.bulk_memory_operations(true);
  5. assert!(config.bulk_memory_operations_enabled());
  6. config.wasi(true);
  7. assert!(config.wasi_enabled());
  8. config.wasmedge_process(true);
  9. assert!(config.wasmedge_process_enabled());

  10. // create a Vm context with the given Config and Store

  11. let mut vm = Vm::create(Some(config), None)?;

  12. } ```

  13. Retrieve the Wasi and WasmEdgeProcess module instances from the Vm.

``` 2. #![allow(unused)]

  1. fn main() {
  2. // get the default Wasi module
  3. let wasi_instance = vm.wasi_module_mut()?;
  4. assert_eq!(wasi_instance.name(), "wasi_snapshot_preview1");
  5. // get the default WasmEdgeProcess module instance
  6. let wasmedge_process_instance = vm.wasmedge_process_module_mut()?;
  7. assert_eq!(wasmedge_process_instance.name(), "wasmedge_process");

  8. } ```

  9. Register an import module as a named module into the Vm.

``` 2. #![allow(unused)]

  1. fn main() {
  2. // create ImportModule instance
  3. let module_name = "extern_module";
  4. let mut import = ImportModule::create(module_name)?;

  5. // a function to import

  6. fn real_add(inputs: Vec) -> Result<Vec, u8> {
  7. if inputs.len() != 2 {
  8. return Err(1);
  9. }

  10. let a = if inputs[0].ty() == ValType::I32 {

  11. inputs[0].to_i32()
  12. } else {
  13. return Err(2);
  14. };

  15. let b = if inputs[1].ty() == ValType::I32 {

  16. inputs[1].to_i32()
  17. } else {
  18. return Err(3);
  19. };

  20. let c = a + b;

  21. Ok(vec![WasmValue::from_i32(c)])

  22. }

  23. // add host function

  24. let func_ty = FuncType::create(vec![ValType::I32; 2], vec![ValType::I32])?;
  25. let host_func = Function::create(&func_ty, Box::new(real_add), 0)?;
  26. import.add_func("add", host_func);

  27. // add table

  28. let table_ty = TableType::create(RefType::FuncRef, 0..=u32::MAX)?;
  29. let table = Table::create(&table_ty)?;
  30. import.add_table("table", table);

  31. // add memory

  32. let mem_ty = MemType::create(0..=u32::MAX)?;
  33. let memory = Memory::create(&mem_ty)?;
  34. import.add_memory("mem", memory);

  35. // add global

  36. let ty = GlobalType::create(ValType::F32, Mutability::Const)?;
  37. let global = Global::create(&ty, WasmValue::from_f32(3.5))?;
  38. import.add_global("global", global);

  39. // register the import module as a named module

  40. vm.register_wasm_from_import(ImportObject::Import(import))?;

  41. } ```

  42. Retrieve the internal Store instance from the Vm, and retrieve the named module instance from the Store instance.

``` 2. #![allow(unused)]

  1. fn main() {
  2. let mut store = vm.store_mut()?;
  3. let named_instance = store.module(module_name)?;
  4. assert!(named_instance.get_func("add").is_ok());
  5. assert!(named_instance.get_table("table").is_ok());
  6. assert!(named_instance.get_memory("mem").is_ok());
  7. assert!(named_instance.get_global("global").is_ok());

  8. } ```

  9. Register an active module into the Vm.

``` 2. #![allow(unused)]

  1. fn main() {
  2. // read the wasm bytes
  3. let wasm_bytes = wat2wasm(
  4. br#"
  5. (module
  6. (export "fib" (func $fib))
  7. (func $fib (param $n i32) (result i32)
  8. (if
  9. (i32.lt_s
  10. (get_local $n)
  11. (i32.const 2)
  12. )
  13. (return
  14. (i32.const 1)
  15. )
  16. )
  17. (return
  18. (i32.add
  19. (call $fib
  20. (i32.sub
  21. (get_local $n)
  22. (i32.const 2)
  23. )
  24. )
  25. (call $fib
  26. (i32.sub
  27. (get_local $n)
  28. (i32.const 1)
  29. )
  30. )
  31. )
  32. )
  33. )
  34. )
  35. "#,
  36. )?;

  37. // load a wasm module from a in-memory bytes, and the loaded wasm module works as an anoymous

  38. // module (aka. active module in WasmEdge terminology)
  39. vm.load_wasm_from_bytes(&wasm_bytes)?;

  40. // validate the loaded active module

  41. vm.validate()?;

  42. // instatiate the loaded active module

  43. vm.instantiate()?;

  44. // get the active module instance

  45. let active_instance = vm.active_module()?;
  46. assert!(active_instance.get_func("fib").is_ok());

  47. } ```

  48. Retrieve the active module from the Vm.

``` 2. #![allow(unused)]

  1. fn main() {
  2. // get the active module instance
  3. let active_instance = vm.active_module()?;
  4. assert!(active_instance.get_func("fib").is_ok());

  5. } ```

The complete code in this demo can be found on WasmEdge Github.

Example 2

In this example, we’ll demonstrate how to use the APIs of Executor to

  • Create an Executor and a Store.

``` 2. #![allow(unused)]

  1. fn main() {
  2. // create an Executor context
  3. let mut executor = Executor::create(None, None)?;

  4. // create a Store context

  5. let mut store = Store::create()?;

  6. } ```

  7. Register an import module into the Executor.

``` 2. #![allow(unused)]

  1. fn main() {
  2. // read the wasm bytes
  3. let wasm_bytes = wat2wasm(
  4. br#"
  5. (module
  6. (export "fib" (func $fib))
  7. (func $fib (param $n i32) (result i32)
  8. (if
  9. (i32.lt_s
  10. (get_local $n)
  11. (i32.const 2)
  12. )
  13. (return
  14. (i32.const 1)
  15. )
  16. )
  17. (return
  18. (i32.add
  19. (call $fib
  20. (i32.sub
  21. (get_local $n)
  22. (i32.const 2)
  23. )
  24. )
  25. (call $fib
  26. (i32.sub
  27. (get_local $n)
  28. (i32.const 1)
  29. )
  30. )
  31. )
  32. )
  33. )
  34. )
  35. "#,
  36. )?;

  37. // load module from a wasm file

  38. let config = Config::create()?;
  39. let loader = Loader::create(Some(config))?;
  40. let module = loader.from_bytes(&wasm_bytes)?;

  41. // validate module

  42. let config = Config::create()?;
  43. let validator = Validator::create(Some(config))?;
  44. validator.validate(&module)?;

  45. // register a wasm module into the store context

  46. let module_name = "extern";
  47. let named_instance = executor.register_named_module(&mut store, &module, module_name)?;
  48. assert!(named_instance.get_func("fib").is_ok());

  49. } ```

  50. Register an active module into the Executor.

``` 2. #![allow(unused)]

  1. fn main() {
  2. // read the wasm bytes
  3. let wasm_bytes = wat2wasm(
  4. br#"
  5. (module
  6. (export "fib" (func $fib))
  7. (func $fib (param $n i32) (result i32)
  8. (if
  9. (i32.lt_s
  10. (get_local $n)
  11. (i32.const 2)
  12. )
  13. (return
  14. (i32.const 1)
  15. )
  16. )
  17. (return
  18. (i32.add
  19. (call $fib
  20. (i32.sub
  21. (get_local $n)
  22. (i32.const 2)
  23. )
  24. )
  25. (call $fib
  26. (i32.sub
  27. (get_local $n)
  28. (i32.const 1)
  29. )
  30. )
  31. )
  32. )
  33. )
  34. )
  35. "#,
  36. )?;

  37. // load module from a wasm file

  38. let config = Config::create()?;
  39. let loader = Loader::create(Some(config))?;
  40. let module = loader.from_bytes(&wasm_bytes)?;

  41. // validate module

  42. let config = Config::create()?;
  43. let validator = Validator::create(Some(config))?;
  44. validator.validate(&module)?;

  45. // register a wasm module as an active module

  46. let active_instance = executor.register_active_module(&mut store, &module)?;
  47. assert!(active_instance.get_func("fib").is_ok());

  48. } ```

The complete code in this demo can be found on WasmEdge Github.