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.

    1. #![allow(unused)]
    2. fn main() {
    3. // create a Config context
    4. let mut config = Config::create()?;
    5. config.bulk_memory_operations(true);
    6. assert!(config.bulk_memory_operations_enabled());
    7. config.wasi(true);
    8. assert!(config.wasi_enabled());
    9. config.wasmedge_process(true);
    10. assert!(config.wasmedge_process_enabled());
    11. // create a Vm context with the given Config and Store
    12. let mut vm = Vm::create(Some(config), None)?;
    13. }
  • Retrieve the Wasi and WasmEdgeProcess module instances from the Vm.

    1. #![allow(unused)]
    2. fn main() {
    3. // get the default Wasi module
    4. let wasi_instance = vm.wasi_module_mut()?;
    5. assert_eq!(wasi_instance.name(), "wasi_snapshot_preview1");
    6. // get the default WasmEdgeProcess module instance
    7. let wasmedge_process_instance = vm.wasmedge_process_module_mut()?;
    8. assert_eq!(wasmedge_process_instance.name(), "wasmedge_process");
    9. }
  • Register an import module as a named module into the Vm.

    1. #![allow(unused)]
    2. fn main() {
    3. // create ImportModule instance
    4. let module_name = "extern_module";
    5. let mut import = ImportModule::create(module_name)?;
    6. // a function to import
    7. fn real_add(inputs: Vec<WasmValue>) -> Result<Vec<WasmValue>, u8> {
    8. if inputs.len() != 2 {
    9. return Err(1);
    10. }
    11. let a = if inputs[0].ty() == ValType::I32 {
    12. inputs[0].to_i32()
    13. } else {
    14. return Err(2);
    15. };
    16. let b = if inputs[1].ty() == ValType::I32 {
    17. inputs[1].to_i32()
    18. } else {
    19. return Err(3);
    20. };
    21. let c = a + b;
    22. Ok(vec![WasmValue::from_i32(c)])
    23. }
    24. // add host function
    25. let func_ty = FuncType::create(vec![ValType::I32; 2], vec![ValType::I32])?;
    26. let host_func = Function::create(&func_ty, Box::new(real_add), 0)?;
    27. import.add_func("add", host_func);
    28. // add table
    29. let table_ty = TableType::create(RefType::FuncRef, 0..=u32::MAX)?;
    30. let table = Table::create(&table_ty)?;
    31. import.add_table("table", table);
    32. // add memory
    33. let mem_ty = MemType::create(0..=u32::MAX)?;
    34. let memory = Memory::create(&mem_ty)?;
    35. import.add_memory("mem", memory);
    36. // add global
    37. let ty = GlobalType::create(ValType::F32, Mutability::Const)?;
    38. let global = Global::create(&ty, WasmValue::from_f32(3.5))?;
    39. import.add_global("global", global);
    40. // register the import module as a named module
    41. vm.register_wasm_from_import(ImportObject::Import(import))?;
    42. }
  • Retrieve the internal Store instance from the Vm, and retrieve the named module instance from the Store instance.

    1. #![allow(unused)]
    2. fn main() {
    3. let mut store = vm.store_mut()?;
    4. let named_instance = store.module(module_name)?;
    5. assert!(named_instance.get_func("add").is_ok());
    6. assert!(named_instance.get_table("table").is_ok());
    7. assert!(named_instance.get_memory("mem").is_ok());
    8. assert!(named_instance.get_global("global").is_ok());
    9. }
  • Register an active module into the Vm.

    1. #![allow(unused)]
    2. fn main() {
    3. // read the wasm bytes
    4. let wasm_bytes = wat2wasm(
    5. br#"
    6. (module
    7. (export "fib" (func $fib))
    8. (func $fib (param $n i32) (result i32)
    9. (if
    10. (i32.lt_s
    11. (get_local $n)
    12. (i32.const 2)
    13. )
    14. (return
    15. (i32.const 1)
    16. )
    17. )
    18. (return
    19. (i32.add
    20. (call $fib
    21. (i32.sub
    22. (get_local $n)
    23. (i32.const 2)
    24. )
    25. )
    26. (call $fib
    27. (i32.sub
    28. (get_local $n)
    29. (i32.const 1)
    30. )
    31. )
    32. )
    33. )
    34. )
    35. )
    36. "#,
    37. )?;
    38. // load a wasm module from a in-memory bytes, and the loaded wasm module works as an anoymous
    39. // module (aka. active module in WasmEdge terminology)
    40. vm.load_wasm_from_bytes(&wasm_bytes)?;
    41. // validate the loaded active module
    42. vm.validate()?;
    43. // instatiate the loaded active module
    44. vm.instantiate()?;
    45. // get the active module instance
    46. let active_instance = vm.active_module()?;
    47. assert!(active_instance.get_func("fib").is_ok());
    48. }
  • Retrieve the active module from the Vm.

    1. #![allow(unused)]
    2. fn main() {
    3. // get the active module instance
    4. let active_instance = vm.active_module()?;
    5. assert!(active_instance.get_func("fib").is_ok());
    6. }

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.

    1. #![allow(unused)]
    2. fn main() {
    3. // create an Executor context
    4. let mut executor = Executor::create(None, None)?;
    5. // create a Store context
    6. let mut store = Store::create()?;
    7. }
  • Register an import module into the Executor.

    1. #![allow(unused)]
    2. fn main() {
    3. // read the wasm bytes
    4. let wasm_bytes = wat2wasm(
    5. br#"
    6. (module
    7. (export "fib" (func $fib))
    8. (func $fib (param $n i32) (result i32)
    9. (if
    10. (i32.lt_s
    11. (get_local $n)
    12. (i32.const 2)
    13. )
    14. (return
    15. (i32.const 1)
    16. )
    17. )
    18. (return
    19. (i32.add
    20. (call $fib
    21. (i32.sub
    22. (get_local $n)
    23. (i32.const 2)
    24. )
    25. )
    26. (call $fib
    27. (i32.sub
    28. (get_local $n)
    29. (i32.const 1)
    30. )
    31. )
    32. )
    33. )
    34. )
    35. )
    36. "#,
    37. )?;
    38. // load module from a wasm file
    39. let config = Config::create()?;
    40. let loader = Loader::create(Some(config))?;
    41. let module = loader.from_bytes(&wasm_bytes)?;
    42. // validate module
    43. let config = Config::create()?;
    44. let validator = Validator::create(Some(config))?;
    45. validator.validate(&module)?;
    46. // register a wasm module into the store context
    47. let module_name = "extern";
    48. let named_instance = executor.register_named_module(&mut store, &module, module_name)?;
    49. assert!(named_instance.get_func("fib").is_ok());
    50. }
  • Register an active module into the Executor.

    1. #![allow(unused)]
    2. fn main() {
    3. // read the wasm bytes
    4. let wasm_bytes = wat2wasm(
    5. br#"
    6. (module
    7. (export "fib" (func $fib))
    8. (func $fib (param $n i32) (result i32)
    9. (if
    10. (i32.lt_s
    11. (get_local $n)
    12. (i32.const 2)
    13. )
    14. (return
    15. (i32.const 1)
    16. )
    17. )
    18. (return
    19. (i32.add
    20. (call $fib
    21. (i32.sub
    22. (get_local $n)
    23. (i32.const 2)
    24. )
    25. )
    26. (call $fib
    27. (i32.sub
    28. (get_local $n)
    29. (i32.const 1)
    30. )
    31. )
    32. )
    33. )
    34. )
    35. )
    36. "#,
    37. )?;
    38. // load module from a wasm file
    39. let config = Config::create()?;
    40. let loader = Loader::create(Some(config))?;
    41. let module = loader.from_bytes(&wasm_bytes)?;
    42. // validate module
    43. let config = Config::create()?;
    44. let validator = Validator::create(Some(config))?;
    45. validator.validate(&module)?;
    46. // register a wasm module as an active module
    47. let active_instance = executor.register_active_module(&mut store, &module)?;
    48. assert!(active_instance.get_func("fib").is_ok());
    49. }

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