Hello World

In this example, we'll use a wasm module, in which a function run is exported and it will call a function say_hello from an import module named env. The imported function say_hello has no inputs and outputs, and only prints a greeting message out.

N.B. In this example, wasmedge-sdk v0.1.1, wasmedge-sys v0.7.1 and wasmedge-types v0.1.3 are used.

Let's start off by getting all imports right away so you can follow along

#![allow(unused)]
fn main() {
// please add this feature if you're using rust of version < 1.63
// #![feature(explicit_generic_args_with_impl_trait)]

use wasmedge_sdk::{params, Executor, ImportObjectBuilder, Module, Store};
use wasmedge_sys::WasmValue;
use wasmedge_types::wat2wasm;
}

Step 1: Define a native function and Create an ImportObject

First, let's define a native function named say_hello_world that prints out Hello, World!.

#![allow(unused)]
fn main() {
fn say_hello_world(_inputs: Vec<WasmValue>) -> Result<Vec<WasmValue>, u8> {
    println!("Hello, world!");

    Ok(vec![])
}
}

To use the native function as an import function in the WasmEdge runtime, we need an ImportObject. wasmedge-sdk defines a ImportObjectBuilder, which provides a group of chaining methods used to create an ImportObject. Let's see how to do it.

#![allow(unused)]
fn main() {
// create an import module
let import = ImportObjectBuilder::new()
    .with_func::<(), ()>("say_hello", say_hello_world)?
    .build("env")?; 
}

Now, we have an import module named env which holds a host function say_hello. As you may notice, the names we used for the import module and the host function are exactly the same as the ones appearing in the wasm module. You can find the wasm module in Step 2.

Step 2: Load a wasm module

Now, let's load a wasm module. wasmedge-sdk defines two methods in Module:

  • from_file loads a wasm module from a file, and meanwhile, validates the loaded wasm module.

  • from_bytes loads a wasm module from an array of in-memory bytes, and meanwhile, validates the loaded wasm module.

Here we choose Module::from_bytes method to load our wasm module from an array of in-memory bytes.

#![allow(unused)]
fn main() {
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)))
"#,
)?;

// loads a wasm module from the given in-memory bytes and returns a compiled module
let module = Module::from_bytes(None, &wasm_bytes)?;
}

Step 3: Register import module and compiled module

To register a compiled module, we need to check if it has dependency on some import modules. In the wasm module this statement (import "env" "say_hello" (func $say_hello (type $no_args_no_rets_t))) tells us that it depends on an import module named env. Therefore, we need to register the import module first before registering the compiled wasm module.

#![allow(unused)]
fn main() {
// create an executor
let mut executor = Executor::new(None, None)?;

// create a store
let mut store = Store::new()?;

// register the import module into the store
store.register_import_module(&mut executor, &import)?;

// register the compiled module into the store and get an module instance
let extern_instance = store.register_named_module(&mut executor, "extern", &module)?;
}

In the code above we use Executor and Store to register the import module and the compiled module. wasmedge-sdk also provides alternative APIs to do the same thing: Vm::register_import_module and Vm::register_module_from_bytes.

Step 4: Run the exported function

Now we are ready to run the exported function.

#![allow(unused)]
fn main() {
// get the exported function "run"
let run = extern_instance
    .func("run")
    .ok_or_else(|| anyhow::Error::msg("Not found exported function named 'run'."))?;

// run host function
run.call(&mut executor, params!())?;
}

In this example we created an instance of Executor, hence, we have two choices to call a function instance:

Any one of these two methods requires that you have to get a function instance.

In addition, Vm defines a group of methods which can invoke host function in different ways. For details, please reference Vm.

The complete example can be found here.