Files
wasmer/examples/memory.rs
2024-11-01 09:59:40 +01:00

154 lines
5.2 KiB
Rust

//! With Wasmer you'll be able to interact with guest module memory.
//!
//! This example illustrates the basics of interacting with Wasm module memory.:
//!
//! 1. How to load a Wasm modules as bytes
//! 2. How to compile the module
//! 3. How to create an instance of the module
//!
//! You can run the example directly by executing in Wasmer root:
//!
//! ```shell
//! cargo run --example memory --release --features "cranelift"
//! ```
//!
//! Ready?
use std::mem;
use wasmer::{imports, wat2wasm, Bytes, Instance, Module, Pages, Store, TypedFunction};
// this example is a work in progress:
// TODO: clean it up and comment it https://github.com/wasmerio/wasmer/issues/1749
fn main() -> anyhow::Result<()> {
// Let's declare the Wasm module.
//
// We are using the text representation of the module here but you can also load `.wasm`
// files using the `include_bytes!` macro.
let wasm_bytes = wat2wasm(
r#"
(module
(type $mem_size_t (func (result i32)))
(type $get_at_t (func (param i32) (result i32)))
(type $set_at_t (func (param i32) (param i32)))
(memory $mem 1)
(func $get_at (type $get_at_t) (param $idx i32) (result i32)
(i32.load (local.get $idx)))
(func $set_at (type $set_at_t) (param $idx i32) (param $val i32)
(i32.store (local.get $idx) (local.get $val)))
(func $mem_size (type $mem_size_t) (result i32)
(memory.size))
(export "get_at" (func $get_at))
(export "set_at" (func $set_at))
(export "mem_size" (func $mem_size))
(export "memory" (memory $mem)))
"#
.as_bytes(),
)?;
// Create a Store.
let mut store = Store::default();
println!("Compiling module...");
// Let's compile the Wasm module.
let module = Module::new(&store, wasm_bytes)?;
// Create an empty import object.
let import_object = imports! {};
println!("Instantiating module...");
// Let's instantiate the Wasm module.
let instance = Instance::new(&mut store, &module, &import_object)?;
// The module exports some utility functions, let's get them.
//
// These function will be used later in this example.
let mem_size: TypedFunction<(), i32> = instance
.exports
.get_typed_function(&mut store, "mem_size")?;
let get_at: TypedFunction<i32, i32> =
instance.exports.get_typed_function(&mut store, "get_at")?;
let set_at: TypedFunction<(i32, i32), ()> =
instance.exports.get_typed_function(&mut store, "set_at")?;
let memory = instance.exports.get_memory("memory")?;
// We now have an instance ready to be used.
//
// We will start by querying the most intersting information
// about the memory: its size. There are mainly two ways of getting
// this:
// * the size as a number of `Page`s
// * the size as a number of bytes
//
// The size in bytes can be found either by querying its pages or by
// querying the memory directly.
println!("Querying memory size...");
let memory_view = memory.view(&store);
assert_eq!(memory_view.size(), Pages::from(1));
assert_eq!(memory_view.size().bytes(), Bytes::from(65536 as usize));
assert_eq!(memory_view.data_size(), 65536);
// Sometimes, the guest module may also export a function to let you
// query the memory. Here we have a `mem_size` function, let's try it:
let result = mem_size.call(&mut store)?;
let memory_view = memory.view(&store);
println!("Memory size: {:?}", result);
assert_eq!(Pages::from(result as u32), memory_view.size());
// Now that we know the size of our memory, it's time to see how wa
// can change this.
//
// A memory can be grown to allow storing more things into it. Let's
// see how we can do that:
println!("Growing memory...");
// 'wamr' does not support direct calls to memory.grow()
#[cfg(not(feature = "wamr"))]
{
// Here we are requesting two more pages for our memory.
memory.grow(&mut store, 2)?;
let memory_view = memory.view(&store);
assert_eq!(memory_view.size(), Pages::from(3));
assert_eq!(memory_view.data_size(), 65536 * 3);
// Now that we know how to query and adjust the size of the memory,
// let's see how wa can write to it or read from it.
//
// We'll only focus on how to do this using exported functions, the goal
// is to show how to work with memory addresses. Here we'll use absolute
// addresses to write and read a value.
let mem_addr = 0x2220;
let val = 0xFEFEFFE;
set_at.call(&mut store, mem_addr, val)?;
let result = get_at.call(&mut store, mem_addr)?;
println!("Value at {:#x?}: {:?}", mem_addr, result);
assert_eq!(result, val);
// Now instead of using hard coded memory addresses, let's try to write
// something at the end of the second memory page and read it.
let page_size = 0x1_0000;
let mem_addr = (page_size * 2) - mem::size_of_val(&val) as i32;
let val = 0xFEA09;
set_at.call(&mut store, mem_addr, val)?;
let result = get_at.call(&mut store, mem_addr)?;
println!("Value at {:#x?}: {:?}", mem_addr, result);
assert_eq!(result, val);
}
Ok(())
}
#[test]
fn test_memory() -> anyhow::Result<()> {
main()
}