96 lines
3.1 KiB
Rust
96 lines
3.1 KiB
Rust
use anyhow::Result;
|
|
use wasmtime::*;
|
|
|
|
struct MyState {
|
|
name: String,
|
|
count: usize,
|
|
}
|
|
|
|
const HELLO: &[u8; 78] =
|
|
b"(module (func $hello (import \"\" \"hello\")) (func (export \"run\") (call $hello)))";
|
|
|
|
fn main() -> Result<()> {
|
|
println!("Compiling module...");
|
|
|
|
// The Engine is the thing that manages and runs WASM instances.
|
|
let engine = Engine::default();
|
|
|
|
// The Module loads, compiles, and creates a new WASM module, ready to be
|
|
// run.
|
|
let module = Module::new(&engine, HELLO)?;
|
|
|
|
println!("Initializing...");
|
|
// The Store is where a WASM instance runs and includes its memory pages. It
|
|
// is the mechanism through which the host and guest programs interact.
|
|
let mut store = Store::new(
|
|
&engine,
|
|
MyState {
|
|
name: "hello world".to_string(),
|
|
count: 0,
|
|
},
|
|
);
|
|
|
|
println!("Creating callback...");
|
|
// A Func is a function defined by the host program and that can be called
|
|
// from a WASM module. Here, we're creating a host function that accesses
|
|
// data inside the store, and increments a mutable value within that data.
|
|
// Due to the nature of the store, all such functions end up as Fn: Send +
|
|
// Sync + 'static, with no 'FnMut' or 'FnOnce' defined, but the `Caller`
|
|
// type there still grants access to the inner workings. See the
|
|
// documentation for more.
|
|
let hello_func = Func::wrap(&mut store, |mut caller: Caller<'_, MyState>| {
|
|
println!("Calling back...");
|
|
println!("> {}, {}", caller.data().name, caller.data().count);
|
|
caller.data_mut().count += 1;
|
|
});
|
|
|
|
// An Instance is a running instance of the WASM module. By "running" I mean
|
|
// that it is now somewhere in memory and its functions can be called by the
|
|
// host. Note that the imports put *into* the instance are in order, not
|
|
// named, and must be extracted by a similarly ordered collection of imports
|
|
// on the other end.
|
|
println!("Instantiating module...");
|
|
let imports = [hello_func.into()];
|
|
let instance = Instance::new(&mut store, &module, &imports)?;
|
|
|
|
// Inside the module, we have a named export, "run". Here, we access it.
|
|
println!("Extracting import...");
|
|
let run = instance.get_typed_func::<(), ()>(&mut store, "run")?;
|
|
|
|
// And now we run it a couple of times. `run` calls `hello_func` defined
|
|
// above, and with each run the data persistent in the `store` object gets
|
|
// updated, so we can see the count rising:
|
|
println!("Running export...");
|
|
run.call(&mut store, ())?;
|
|
run.call(&mut store, ())?;
|
|
run.call(&mut store, ())?;
|
|
run.call(&mut store, ())?;
|
|
run.call(&mut store, ())?;
|
|
|
|
println!("Done!");
|
|
|
|
/*
|
|
The output should look like this:
|
|
|
|
Compiling module...
|
|
Initializing...
|
|
Creating callback...
|
|
Instantiating module...
|
|
Extracting import...
|
|
Running export...
|
|
Calling back...
|
|
> hello world, 0
|
|
Calling back...
|
|
> hello world, 1
|
|
Calling back...
|
|
> hello world, 2
|
|
Calling back...
|
|
> hello world, 3
|
|
Calling back...
|
|
> hello world, 4
|
|
Done!
|
|
*/
|
|
|
|
Ok(())
|
|
}
|