From fe687600c9566e2e8979c7baf758fcea13dc3ae3 Mon Sep 17 00:00:00 2001 From: jubianchi Date: Fri, 30 Oct 2020 16:02:51 +0100 Subject: [PATCH 01/12] doc(examples): Clean and comment the memory example Closes #1749 --- examples/memory.rs | 115 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 91 insertions(+), 24 deletions(-) diff --git a/examples/memory.rs b/examples/memory.rs index 9da6d8131b2..123b55542b2 100644 --- a/examples/memory.rs +++ b/examples/memory.rs @@ -1,11 +1,32 @@ -use wasmer::{imports, wat2wasm, Instance, Module, NativeFunc, Pages, Store}; +//! With Wasmer you'll be able to interact with guest module memory. +//! +//! This example illustrates the basics of 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 wasmer::{imports, wat2wasm, Instance, Module, NativeFunc, Pages, Store, Bytes}; use wasmer_compiler_cranelift::Cranelift; use wasmer_engine_jit::JIT; +use std::mem; // 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 @@ -32,48 +53,94 @@ fn main() -> anyhow::Result<()> { .as_bytes(), )?; - // We set up our store with an engine and a compiler. + // Create a Store. + // Note that we don't need to specify the engine/compiler if we want to use + // the default provided by Wasmer. + // You can use `Store::default()` for that. let store = Store::new(&JIT::new(&Cranelift::default()).engine()); - // Then compile our Wasm. + + 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! {}; - // And instantiate it with no imports. + + println!("Instantiating module..."); + // Let's instantiate the Wasm module. let instance = Instance::new(&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: NativeFunc<(), i32> = instance.exports.get_native_function("mem_size")?; let get_at: NativeFunc = instance.exports.get_native_function("get_at")?; let set_at: NativeFunc<(i32, i32), ()> = instance.exports.get_native_function("set_at")?; let memory = instance.exports.get_memory("memory")?; - let mem_addr = 0x2220; - let val = 0xFEFEFFE; - + // 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..."); assert_eq!(memory.size(), Pages::from(1)); + assert_eq!(memory.size().bytes(), Bytes::from(65536 as usize)); + assert_eq!(memory.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()?; + println!("Memory size: {:?}", result); + assert_eq!(Pages::from(result as u32), memory.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..."); + // Here we are requesting two more pages for our memory. memory.grow(2)?; assert_eq!(memory.size(), Pages::from(3)); - let result = mem_size.call()?; - assert_eq!(result, 3); - // ------------- + // From an `Instance` we can fetch any exported entities from the Wasm module. + // Each of these entities is covered in others examples. + // + // Here we are fetching the exported function. We won't go into details here + // as the main focus of this example is to show how to create an instance out + // of a Wasm module and have basic interactions with it. + + // 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 function, 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(mem_addr, val)?; - // ------------- + let result = get_at.call(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 result = get_at.call(page_size * 3 - 4)?; - memory.grow(1025)?; - assert_eq!(result, 123456); - assert_eq!(memory.size(), Pages::from(1028)); - set_at.call(page_size * 1027 - 4, 123456)?; - let result = get_at.call(page_size * 1027 - 4)?; - assert_eq!(result, 123456); - set_at.call(1024, 123456)?; - let result = get_at.call(1024)?; - assert_eq!(result, 123456); - - // ------------- + let mem_addr = (page_size * 2) - mem::size_of_val(&val) as i32; + let val = 0xFEA09; + set_at.call(mem_addr, val)?; + let result = get_at.call(mem_addr)?; + println!("Value at {:#x?}: {:?}", mem_addr, result); assert_eq!(result, val); - // ------------- Ok(()) } From 8c78b7cfcf30282a8adced8f6c7cde2de9cd8da4 Mon Sep 17 00:00:00 2001 From: jubianchi Date: Fri, 30 Oct 2020 17:22:56 +0100 Subject: [PATCH 02/12] doc(examples): Add comments to the early exit example --- examples/early_exit.rs | 46 +++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/examples/early_exit.rs b/examples/early_exit.rs index 814ab01ff6c..ccbabb110b7 100644 --- a/examples/early_exit.rs +++ b/examples/early_exit.rs @@ -1,5 +1,11 @@ //! This example shows how the host can terminate execution of Wasm early from //! inside a host function called by the Wasm. +//! +//! ```shell +//! cargo run --example early-exit --release --features "cranelift" +//! ``` +//! +//! Ready? use anyhow::bail; use std::fmt; @@ -21,12 +27,6 @@ impl fmt::Display for ExitCode { // And then we implement `std::error::Error`. impl std::error::Error for ExitCode {} -// The host function that we'll use to terminate execution. -fn early_exit() { - // This is where it happens. - RuntimeError::raise(Box::new(ExitCode(1))); -} - fn main() -> anyhow::Result<()> { // Let's declare the Wasm module with the text representation. let wasm_bytes = wat2wasm( @@ -44,34 +44,52 @@ fn main() -> anyhow::Result<()> { "#, )?; + // Create a Store. + // Note that we don't need to specify the engine/compiler if we want to use + // the default provided by Wasmer. + // You can use `Store::default()` for that. let store = Store::new(&JIT::new(&Cranelift::default()).engine()); + + println!("Compiling module..."); + // Let's compile the Wasm module. let module = Module::new(&store, wasm_bytes)?; + // We declare the host function that we'll use to terminate execution. + fn early_exit() { + // This is where it happens. + RuntimeError::raise(Box::new(ExitCode(1))); + } + + // Create an import object. let import_object = imports! { "env" => { "early_exit" => Function::new_native(&store, early_exit), } }; + + println!("Instantiating module..."); + // Let's instantiate the Wasm module. let instance = Instance::new(&module, &import_object)?; - // Get the `run` function which we'll use as our entrypoint. + // Here we go. + // + // The Wasm module exports a function called `sum`. We'll use this function + // as our entrypoint. let run_func: NativeFunc<(i32, i32), i32> = instance.exports.get_native_function("run").unwrap(); - // When we call a function it can either succeed or fail. + // When we call a function it can either succeed or fail. We expect it to fail. match run_func.call(1, 7) { Ok(result) => { - bail!( - "Expected early termination with `ExitCode`, found: {}", - result - ); + bail!("Expected early termination with `ExitCode`, found: {}", result); } - // We're expecting it to fail. - // We attempt to downcast the error into the error type that we were expecting. + // In case of a failure, which we expect, we attempt to downcast the error into the error + // type that we were expecting. Err(e) => match e.downcast::() { // We found the exit code used to terminate execution. Ok(exit_code) => { println!("Exited early with exit code: {}", exit_code); + Ok(()) } Err(e) => { From 109bcf1bc296f7cb162e11d47314c82011c4e352 Mon Sep 17 00:00:00 2001 From: jubianchi Date: Fri, 30 Oct 2020 22:45:21 +0100 Subject: [PATCH 03/12] doc(examples): Add an example on how to handle errors --- examples/errors.rs | 103 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 examples/errors.rs diff --git a/examples/errors.rs b/examples/errors.rs new file mode 100644 index 00000000000..fef46c26fe7 --- /dev/null +++ b/examples/errors.rs @@ -0,0 +1,103 @@ +//! A Wasm module can sometimes be invalid or trigger traps, and in those case we will get +//! an error back from the API. +//! +//! In this example we'll see how to handle such errors in the most +//! basic way. To do that we'll use a Wasm module that we know will +//! produce an error. +//! +//! You can run the example directly by executing in Wasmer root: +//! +//! ```shell +//! cargo run --example errors --release --features "cranelift" +//! ``` +//! +//! Ready? + +use wasmer::{imports, wat2wasm, Instance, Module, Store}; +use wasmer_compiler_cranelift::Cranelift; +use wasmer_engine_jit::JIT; + +fn main() -> Result<(), Box> { + // Let's declare the Wasm module with the text representation. + let wasm_bytes = wat2wasm( + br#" +(module + (type $do_div_by_zero_t (func (result i32))) + (func $do_div_by_zero_f (type $do_div_by_zero_t) (result i32) + i32.const 4 + i32.const 0 + i32.div_s) + + (type $div_by_zero_t (func (result i32))) + (func $div_by_zero_f (type $div_by_zero_t) (result i32) + call $do_div_by_zero_f) + (export "div_by_zero" (func $div_by_zero_f))) +"#, + )?; + + // Create a Store. + // Note that we don't need to specify the engine/compiler if we want to use + // the default provided by Wasmer. + // You can use `Store::default()` for that. + let store = Store::new(&JIT::new(&Cranelift::default()).engine()); + + println!("Compiling module..."); + // Let's compile the Wasm module. + let module = Module::new(&store, wasm_bytes)?; + + // Create an import object. + let import_object = imports! {}; + + println!("Instantiating module..."); + // Let's instantiate the Wasm module. + let instance = Instance::new(&module, &import_object)?; + + // Here we go. + // + // The Wasm module exports a function called `div_by_zero`. As its name + // implies, this function will try to do a division by zero and thus + // produce an error. + // + // Let's get it. + let div_by_zero = instance + .exports + .get_function("div_by_zero")? + .native::<(), i32>()?; + + println!("Calling `div_by_zero` function..."); + // Let's call the `div_by_zero` exported function. + let result = div_by_zero.call(); + + // When we call a function it can either succeed or fail. We expect it to fail. + match result { + Ok(_) => { + // This should have thrown an error, return an error + panic!("div_by_zero did not error"); + } + Err(e) => { + // Log the error + println!("Error caught from `div_by_zero`: {}", e.message()); + + // Errors come with a trace we can inspect to get more + // information on the execution flow. + let frames = e.trace(); + let frames_len = frames.len(); + + for i in 0..frames_len { + println!( + " Frame #{}: {:?}::{:?}", + frames_len - i, + frames[i].module_name(), + frames[i].function_name().or(Some("")).unwrap() + ); + } + } + } + + Ok(()) +} + +#[test] +fn test_exported_function() -> Result<(), Box> { + main() +} From 2e49d21a8c51cbd4b484929e6bdcd1d5243ac2a9 Mon Sep 17 00:00:00 2001 From: jubianchi Date: Fri, 30 Oct 2020 22:55:42 +0100 Subject: [PATCH 04/12] doc(examples): Add an examples on how to do basic instantiation --- examples/instance.rs | 80 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 examples/instance.rs diff --git a/examples/instance.rs b/examples/instance.rs new file mode 100644 index 00000000000..9adc7910c06 --- /dev/null +++ b/examples/instance.rs @@ -0,0 +1,80 @@ +//! Wasmer will let you easily run Wasm module in a Rust host. +//! +//! This example illustrates the basics of using Wasmer through a "Hello World"-like project: +//! +//! 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 instance --release --features "cranelift" +//! ``` +//! +//! Ready? + +use wasmer::{imports, wat2wasm, Instance, Module, Store}; +use wasmer_compiler_cranelift::Cranelift; +use wasmer_engine_jit::JIT; + +fn main() -> Result<(), Box> { + // 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( + br#" +(module + (type $add_one_t (func (param i32) (result i32))) + (func $add_one_f (type $add_one_t) (param $value i32) (result i32) + local.get $value + i32.const 1 + i32.add) + (export "add_one" (func $add_one_f))) +"#, + )?; + + // Create a Store. + // Note that we don't need to specify the engine/compiler if we want to use + // the default provided by Wasmer. + // You can use `Store::default()` for that. + let store = Store::new(&JIT::new(&Cranelift::default()).engine()); + + 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(&module, &import_object)?; + + // We now have an instance ready to be used. + // + // From an `Instance` we can retrieve any exported entities. + // Each of these entities is covered in others examples. + // + // Here we are retrieving the exported function. We won't go into details here + // as the main focus of this example is to show how to create an instance out + // of a Wasm module and have basic interactions with it. + let add_one = instance + .exports + .get_function("add_one")? + .native::()?; + + println!("Calling `add_one` function..."); + let result = add_one.call(1)?; + + println!("Results of `add_one`: {:?}", result); + assert_eq!(result, 2); + + Ok(()) +} + +#[test] +fn test_exported_function() -> Result<(), Box> { + main() +} From d463b44e96d954edd5d336fdcb5dc09e64de7642 Mon Sep 17 00:00:00 2001 From: jubianchi Date: Fri, 30 Oct 2020 22:56:23 +0100 Subject: [PATCH 05/12] chore(examples): Update README and Cargo.toml with new examples --- Cargo.toml | 10 ++ examples/README.md | 228 +++++++++++++++++++++-------------- examples/exports_function.rs | 4 +- examples/imports_function.rs | 2 +- 4 files changed, 149 insertions(+), 95 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index caafff5a374..54ba9c5b31b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -230,3 +230,13 @@ required-features = ["cranelift"] name = "memory" path = "examples/memory.rs" required-features = ["cranelift"] + +[[example]] +name = "instance" +path = "examples/instance.rs" +required-features = ["cranelift"] + +[[example]] +name = "errors" +path = "examples/errors.rs" +required-features = ["cranelift"] \ No newline at end of file diff --git a/examples/README.md b/examples/README.md index 3d107cac533..e4d19458a9e 100644 --- a/examples/README.md +++ b/examples/README.md @@ -37,245 +37,288 @@ The examples are written in a difficulty/discovery order. Concepts that are explained in an example is not necessarily re-explained in a next example. -### Engines +### Basics -1. [**JIT engine**][engine-jit], explains what an engine is, what the - JIT engine is, and how to set it up. The example completes itself - with the compilation of the Wasm module, its instantiation, and - finally, by calling an exported function. +1. [**Instantiating a module][instance], explains the basics of using Wasmer + and how to create an instance out of a Wasm module. - _Keywords_: JIT, engine, in-memory, executable code. + _Keywords_: instance, module.
- Execute the example + Execute the example - ```shell - $ cargo run --example engine-jit --release --features "cranelift" - ``` + ```shell + $ cargo run --example instance --release --features "cranelift" + ```
-2. [**Native engine**][engine-native], explains what a native engine - is, and how to set it up. The example completes itself with the - compilation of the Wasm module, its instantiation, and finally, by - calling an exported function. +2. [**Handling errors][errors], explains the basics of interacting with + Wasm module memory. - _Keywords_: native, engine, shared library, dynamic library, - executable code. + _Keywords_: memory, module. + +
+ Execute the example + ```shell + $ cargo run --example memory --release --features "cranelift" + ``` + +
+ +3. [**Interacting with memory][memory], explains the basics of interacting with + Wasm module memory. + + _Keywords_: memory, module. +
- Execute the example + Execute the example - ```shell - $ cargo run --example engine-native --release --features "cranelift" - ``` + ```shell + $ cargo run --example memory --release --features "cranelift" + ```
-3. [**Headless engines**][engine-headless], explains what a headless - engine is, what problem it does solve, and what are the benefits of - it. The example completes itself with the instantiation of a - pre-compiled Wasm module, and finally, by calling an exported - function. +### Exports + +1. [**Exported global**][exported-global], explains how to work with + exported globals: get/set their value, have information about their + type. - _Keywords_: native, engine, constrained environment, ahead-of-time - compilation, cross-compilation, executable code, serialization. + _Keywords_: export, global.
Execute the example ```shell - $ cargo run --example engine-headless --release --features "cranelift" + $ cargo run --example exported-globals --release --features "cranelift" ```
- -4. [**Cross-compilation**][cross-compilation], illustrates the power - of the abstraction over the engines and the compilers, such as it - is possible to cross-compile a Wasm module for a custom target. - _Keywords_: engine, compiler, cross-compilation. +2. [**Exported function**][exported-function], explains how to get and + how to call an exported function. They come in 2 flavors: dynamic, + and “static”/native. The pros and cons are discussed briefly. + + _Keywords_: export, function, dynamic, static, native.
Execute the example ```shell - $ cargo run --example cross-compilation --release --features "cranelift" + $ cargo run --example exported-function --release --features "cranelift" ```
-### Compilers -5. [**Singlepass compiler**][compiler-singlepass], explains how to use - the [`wasmer-compiler-singlepass`] compiler. +3. [**Exported memory**][exported-memory], explains how to read from + and write to exported memory. - _Keywords_: compiler, singlepass. + _Keywords_: export, memory.
Execute the example ```shell - $ cargo run --example compiler-singlepass --release --features "singlepass" + $ cargo run --example exported-memory --release --features "cranelift" ```
-6. [**Cranelift compiler**][compiler-cranelift], explains how to use - the [`wasmer-compiler-cranelift`] compiler. +### Imports + +1. [**Imported global**][imported-global], explains how to work with + imported globals: create globals, import them, get/set their value. - _Keywords_: compiler, cranelift. + _Keywords_: import, global.
Execute the example ```shell - $ cargo run --example compiler-cranelift --release --features "cranelift" + $ cargo run --example imported-globals --release --features "cranelift" ```
-7. [**LLVM compiler**][compiler-llvm], explains how to use the - [`wasmer-compiler-llvm`] compiler. +2. [**Imported function**][imported-function], explains how to define + an imported function. They come in 2 flavors: dynamic, + and “static”/native. - _Keywords_: compiler, llvm. + _Keywords_: import, function, dynamic, static, native.
Execute the example ```shell - $ cargo run --example compiler-llvm --release --features "llvm" + $ cargo run --example imported-function --release --features "cranelift" ```
-### Exports +### Externs -8. [**Exported global**][exported-global], explains how to work with - exported globals: get/set their value, have information about their - type. - - _Keywords_: export, global. +1. [**Table**][table], explains how to use Wasm Tables from the Wasmer API. + + _Keywords_: basic, table, call_indirect
Execute the example ```shell - $ cargo run --example exported-globals --release --features "cranelift" + $ cargo run --example table --release --features "cranelift" ```
-9. [**Exported function**][exported-function], explains how to get and - how to call an exported function. They come in 2 flavors: dynamic, - and “static”/native. The pros and cons are discussed briefly. - - _Keywords_: export, function, dynamic, static, native. +2. [**Memory**][memory], explains how to use Wasm Memories from + the Wasmer API. Memory example is a work in progress. + + _Keywords_: basic, memory
Execute the example ```shell - $ cargo run --example exported-function --release --features "cranelift" + $ cargo run --example memory --release --features "cranelift" ```
+### Tunables -10. [**Exported memory**][exported-memory], explains how to read from - and write to exported memory. - - _Keywords_: export, memory. +1. **Limit memory**, explains how to use Tunables to limit the + size of an exported Wasm Memories + + _Keywords_: basic, tunables, memory
Execute the example ```shell - $ cargo run --example exported-memory --release --features "cranelift" + $ cargo run --example tunables-limit-memory --release --features "cranelift" ```
-### Imports +### Engines -11. [**Imported global**][imported-global], explains how to work with - imported globals: create globals, import them, get/set their value. +1. [**JIT engine**][engine-jit], explains what an engine is, what the + JIT engine is, and how to set it up. The example completes itself + with the compilation of the Wasm module, its instantiation, and + finally, by calling an exported function. + + _Keywords_: JIT, engine, in-memory, executable code. - _Keywords_: import, global. -
Execute the example ```shell - $ cargo run --example imported-globals --release --features "cranelift" + $ cargo run --example engine-jit --release --features "cranelift" ```
-12. [**Imported function**][imported-function], explains how to define - an imported function. They come in 2 flavors: dynamic, - and “static”/native. +2. [**Native engine**][engine-native], explains what a native engine + is, and how to set it up. The example completes itself with the + compilation of the Wasm module, its instantiation, and finally, by + calling an exported function. - _Keywords_: import, function, dynamic, static, native. + _Keywords_: native, engine, shared library, dynamic library, + executable code.
Execute the example ```shell - $ cargo run --example imported-function --release --features "cranelift" + $ cargo run --example engine-native --release --features "cranelift" ```
-### Externs +3. [**Headless engines**][engine-headless], explains what a headless + engine is, what problem it does solve, and what are the benefits of + it. The example completes itself with the instantiation of a + pre-compiled Wasm module, and finally, by calling an exported + function. + + _Keywords_: native, engine, constrained environment, ahead-of-time + compilation, cross-compilation, executable code, serialization. -13. [**Table**][table], explains how to use Wasm Tables from the Wasmer API. +
+ Execute the example - _Keywords_: basic, table, call_indirect + ```shell + $ cargo run --example engine-headless --release --features "cranelift" + ``` + +
+ +4. [**Cross-compilation**][cross-compilation], illustrates the power + of the abstraction over the engines and the compilers, such as it + is possible to cross-compile a Wasm module for a custom target. + + _Keywords_: engine, compiler, cross-compilation.
Execute the example ```shell - $ cargo run --example table --release --features "cranelift" + $ cargo run --example cross-compilation --release --features "cranelift" ```
- -14. [**Memory**][memory], explains how to use Wasm Memories from - the Wasmer API. Memory example is a work in progress. - _Keywords_: basic, memory +### Compilers + +1. [**Singlepass compiler**][compiler-singlepass], explains how to use + the [`wasmer-compiler-singlepass`] compiler. + + _Keywords_: compiler, singlepass.
Execute the example ```shell - $ cargo run --example memory --release --features "cranelift" + $ cargo run --example compiler-singlepass --release --features "singlepass" ```
-### Tunables +2. [**Cranelift compiler**][compiler-cranelift], explains how to use + the [`wasmer-compiler-cranelift`] compiler. + + _Keywords_: compiler, cranelift. -15. **Limit memory**, explains how to use Tunables to limit the - size of an exported Wasm Memories +
+ Execute the example - _Keywords_: basic, tunables, memory + ```shell + $ cargo run --example compiler-cranelift --release --features "cranelift" + ``` + +
+ +3. [**LLVM compiler**][compiler-llvm], explains how to use the + [`wasmer-compiler-llvm`] compiler. + + _Keywords_: compiler, llvm.
Execute the example ```shell - $ cargo run --example tunables-limit-memory --release --features "cranelift" + $ cargo run --example compiler-llvm --release --features "llvm" ```
- ### Integrations -16. [**WASI**][wasi], explains how to use the [WebAssembly System +1. [**WASI**][wasi], explains how to use the [WebAssembly System Interface][WASI] (WASI), i.e. the [`wasmer-wasi`] crate. _Keywords_: wasi, system, interface @@ -301,6 +344,7 @@ example. [exported-memory]: ./exports_memory.rs [imported-global]: ./imports_global.rs [imported-function]: ./imports_function.rs +[instance]: ./instance.rs [wasi]: ./wasi.rs [table]: ./table.rs [memory]: ./memory.rs diff --git a/examples/exports_function.rs b/examples/exports_function.rs index 941733847bb..bbc5b43e490 100644 --- a/examples/exports_function.rs +++ b/examples/exports_function.rs @@ -85,13 +85,13 @@ fn main() -> Result<(), Box> { // `Rets`, respectively for the parameters and the results. If // those values don't match the exported function signature, an // error will be raised. - let sum = sum.native::<(i32, i32), i32>()?; + let sum_native = sum.native::<(i32, i32), i32>()?; println!("Calling `sum` function (natively)..."); // Let's call the `sum` exported function. The parameters are // statically typed Rust values of type `i32` and `i32`. The // result, in this case particular case, in a unit of type `i32`. - let result = sum.call(1, 2)?; + let result = sum_native.call(1, 2)?; println!("Results: {:?}", result); assert_eq!(result, 3); diff --git a/examples/imports_function.rs b/examples/imports_function.rs index 7007b2f4f6e..56496fefe6d 100644 --- a/examples/imports_function.rs +++ b/examples/imports_function.rs @@ -70,7 +70,7 @@ fn main() -> Result<(), Box> { } let multiply_native = Function::new_native(&store, multiply); - // Create an empty import object. + // Create an import object. let import_object = imports! { "env" => { "multiply_dynamic" => multiply_dynamic, From c67a6d3e5463f67c4f1d524784410160ba3f2e0c Mon Sep 17 00:00:00 2001 From: jubianchi Date: Tue, 3 Nov 2020 21:11:29 +0100 Subject: [PATCH 06/12] chore(examples): Minor edits to the exported functions example --- examples/exports_function.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/examples/exports_function.rs b/examples/exports_function.rs index bbc5b43e490..853f3699865 100644 --- a/examples/exports_function.rs +++ b/examples/exports_function.rs @@ -72,10 +72,11 @@ fn main() -> Result<(), Box> { println!("Calling `sum` function..."); // Let's call the `sum` exported function. The parameters are a // slice of `Value`s. The results are a boxed slice of `Value`s. - let results = sum.call(&[Value::I32(1), Value::I32(2)])?; + let args = [Value::I32(1), Value::I32(2)]; + let result = sum.call(&args)?; - println!("Results: {:?}", results); - assert_eq!(results.to_vec(), vec![Value::I32(3)]); + println!("Results: {:?}", result); + assert_eq!(result.to_vec(), vec![Value::I32(3)]); // That was fun. But what if we can get rid of the `Value`s? Well, // that's possible with the `NativeFunction` API. The function @@ -91,10 +92,10 @@ fn main() -> Result<(), Box> { // Let's call the `sum` exported function. The parameters are // statically typed Rust values of type `i32` and `i32`. The // result, in this case particular case, in a unit of type `i32`. - let result = sum_native.call(1, 2)?; + let result = sum_native.call(3, 4)?; println!("Results: {:?}", result); - assert_eq!(result, 3); + assert_eq!(result, 7); // Much nicer, isn't it? // From ed5a882028965bdf4d4aae6ea15408013fa47f06 Mon Sep 17 00:00:00 2001 From: jubianchi Date: Tue, 3 Nov 2020 21:12:41 +0100 Subject: [PATCH 07/12] chore(examples): Minor edits to the exported globals example --- examples/exports_global.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/examples/exports_global.rs b/examples/exports_global.rs index 0ad1e9b65ac..8884a1406a7 100644 --- a/examples/exports_global.rs +++ b/examples/exports_global.rs @@ -68,16 +68,16 @@ fn main() -> Result<(), Box> { let one = instance.exports.get_global("one")?; let some = instance.exports.get_global("some")?; - println!("Getting global type informations..."); + println!("Getting globals types information..."); // Let's get the globals types. The results are `GlobalType`s. let one_type = one.ty(); let some_type = some.ty(); - println!("one type: {:?} {:?}", one_type.mutability, one_type.ty); + println!("`one` type: {:?} {:?}", one_type.mutability, one_type.ty); assert_eq!(one_type.mutability, Mutability::Const); assert_eq!(one_type.ty, Type::F32); - println!("some type: {:?} {:?}", some_type.mutability, some_type.ty); + println!("`some` type: {:?} {:?}", some_type.mutability, some_type.ty); assert_eq!(some_type.mutability, Mutability::Var); assert_eq!(some_type.ty, Type::F32); @@ -93,14 +93,14 @@ fn main() -> Result<(), Box> { .get_function("get_one")? .native::<(), f32>()?; - let one_result = get_one.call()?; - let some_result = some.get(); + let one_value = get_one.call()?; + let some_value = some.get(); - println!("one value: {:?}", one_result); - assert_eq!(one_result, 1.0); + println!("`one` value: {:?}", one_value); + assert_eq!(one_value, 1.0); - println!("some value: {:?}", some_result); - assert_eq!(some_result, Value::F32(0.0)); + println!("`some` value: {:?}", some_value); + assert_eq!(some_value, Value::F32(0.0)); println!("Setting global values..."); // Trying to set the value of a immutable global (`const`) @@ -112,7 +112,7 @@ fn main() -> Result<(), Box> { ); let one_result = one.get(); - println!("one value after `set`: {:?}", one_result); + println!("`one` value after `set`: {:?}", one_result); assert_eq!(one_result, Value::F32(1.0)); // Setting the values of globals can be done in two ways: @@ -126,12 +126,12 @@ fn main() -> Result<(), Box> { .native::()?; set_some.call(21.0)?; let some_result = some.get(); - println!("some value after `set_some`: {:?}", some_result); + println!("`some` value after `set_some`: {:?}", some_result); assert_eq!(some_result, Value::F32(21.0)); some.set(Value::F32(42.0))?; let some_result = some.get(); - println!("some value after `set`: {:?}", some_result); + println!("`some` value after `set`: {:?}", some_result); assert_eq!(some_result, Value::F32(42.0)); Ok(()) From bd1986f773ec6ff6e6d89386d8807bebbb83ef40 Mon Sep 17 00:00:00 2001 From: jubianchi Date: Tue, 3 Nov 2020 21:13:39 +0100 Subject: [PATCH 08/12] chore(examples): Minor edits to the memory example --- examples/memory.rs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/examples/memory.rs b/examples/memory.rs index 123b55542b2..d78b6dda5f9 100644 --- a/examples/memory.rs +++ b/examples/memory.rs @@ -1,6 +1,6 @@ //! With Wasmer you'll be able to interact with guest module memory. //! -//! This example illustrates the basics of the basics of interacting with Wasm 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 @@ -14,10 +14,10 @@ //! //! Ready? -use wasmer::{imports, wat2wasm, Instance, Module, NativeFunc, Pages, Store, Bytes}; +use std::mem; +use wasmer::{imports, wat2wasm, Bytes, Instance, Module, NativeFunc, Pages, Store}; use wasmer_compiler_cranelift::Cranelift; use wasmer_engine_jit::JIT; -use std::mem; // this example is a work in progress: // TODO: clean it up and comment it https://github.com/wasmerio/wasmer/issues/1749 @@ -99,7 +99,6 @@ fn main() -> anyhow::Result<()> { println!("Memory size: {:?}", result); assert_eq!(Pages::from(result as u32), memory.size()); - // Now that we know the size of our memory, it's time to see how wa // can change this. // @@ -109,18 +108,12 @@ fn main() -> anyhow::Result<()> { // Here we are requesting two more pages for our memory. memory.grow(2)?; assert_eq!(memory.size(), Pages::from(3)); - - // From an `Instance` we can fetch any exported entities from the Wasm module. - // Each of these entities is covered in others examples. - // - // Here we are fetching the exported function. We won't go into details here - // as the main focus of this example is to show how to create an instance out - // of a Wasm module and have basic interactions with it. + assert_eq!(memory.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 function, the goal + // 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; From 855eaab51e58b8162285b34236a48be6f7b936fe Mon Sep 17 00:00:00 2001 From: jubianchi Date: Tue, 3 Nov 2020 21:16:44 +0100 Subject: [PATCH 09/12] fix(examples): Fix a typo in the tunables example --- examples/README.md | 2 +- examples/tunables_limit_memory.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/README.md b/examples/README.md index e4d19458a9e..5607ed7e843 100644 --- a/examples/README.md +++ b/examples/README.md @@ -191,7 +191,7 @@ example. ### Tunables 1. **Limit memory**, explains how to use Tunables to limit the - size of an exported Wasm Memories + size of an exported Wasm memory _Keywords_: basic, tunables, memory diff --git a/examples/tunables_limit_memory.rs b/examples/tunables_limit_memory.rs index dfe5f40cd1d..c87f371bc21 100644 --- a/examples/tunables_limit_memory.rs +++ b/examples/tunables_limit_memory.rs @@ -16,7 +16,7 @@ use wasmer_engine_jit::JIT; /// After adjusting the memory limits, it delegates all other logic /// to the base tunables. pub struct LimitingTunables { - /// The maxium a linear memory is allowed to be (in Wasm pages, 65 KiB each). + /// The maximum a linear memory is allowed to be (in Wasm pages, 64 KiB each). /// Since Wasmer ensures there is only none or one memory, this is practically /// an upper limit for the guest memory. limit: Pages, From 11abe0d898e847e18132dd52ac59f3bcb5493925 Mon Sep 17 00:00:00 2001 From: jubianchi Date: Tue, 3 Nov 2020 21:17:17 +0100 Subject: [PATCH 10/12] doc(examples): Add a link to every example in the readme --- examples/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/README.md b/examples/README.md index 5607ed7e843..ab5df284271 100644 --- a/examples/README.md +++ b/examples/README.md @@ -39,7 +39,7 @@ example. ### Basics -1. [**Instantiating a module][instance], explains the basics of using Wasmer +1. [**Instantiating a module**][instance], explains the basics of using Wasmer and how to create an instance out of a Wasm module. _Keywords_: instance, module. @@ -53,7 +53,7 @@ example. -2. [**Handling errors][errors], explains the basics of interacting with +2. [**Handling errors**][errors], explains the basics of interacting with Wasm module memory. _Keywords_: memory, module. @@ -67,7 +67,7 @@ example. -3. [**Interacting with memory][memory], explains the basics of interacting with +3. [**Interacting with memory**][memory], explains the basics of interacting with Wasm module memory. _Keywords_: memory, module. From f97d3da36157f8dd72d0f147ad66442b2e36f0c5 Mon Sep 17 00:00:00 2001 From: jubianchi Date: Mon, 2 Nov 2020 23:37:30 +0100 Subject: [PATCH 11/12] doc(examples): Clean and comment the early-exit example --- examples/early_exit.rs | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/examples/early_exit.rs b/examples/early_exit.rs index ccbabb110b7..911fd3d6089 100644 --- a/examples/early_exit.rs +++ b/examples/early_exit.rs @@ -1,5 +1,12 @@ -//! This example shows how the host can terminate execution of Wasm early from -//! inside a host function called by the Wasm. +//! There are cases where you may want to interrupt this synchronous execution of the WASM module +//! while the it is calling a host function. This can be useful for saving resources, and not +//! returning back to the guest WASM for execution, when you already know the WASM execution will +//! fail, or no longer be needed. +//! +//! In this example, we will run a WASM module that calls the imported host function +//! interrupt_execution. This host function will immediately stop executing the WebAssembly module. +//! +//! You can run the example directly by executing in Wasmer root: //! //! ```shell //! cargo run --example early-exit --release --features "cranelift" @@ -73,15 +80,17 @@ fn main() -> anyhow::Result<()> { // Here we go. // - // The Wasm module exports a function called `sum`. We'll use this function - // as our entrypoint. - let run_func: NativeFunc<(i32, i32), i32> = - instance.exports.get_native_function("run").unwrap(); + // Get the `run` function which we'll use as our entrypoint. + println!("Calling `run` function..."); + let run_func: NativeFunc<(i32, i32), i32> = instance.exports.get_native_function("run")?; // When we call a function it can either succeed or fail. We expect it to fail. match run_func.call(1, 7) { Ok(result) => { - bail!("Expected early termination with `ExitCode`, found: {}", result); + bail!( + "Expected early termination with `ExitCode`, found: {}", + result + ); } // In case of a failure, which we expect, we attempt to downcast the error into the error // type that we were expecting. From b2177a8120e07b7a5f80ac8687739523e885d73a Mon Sep 17 00:00:00 2001 From: jubianchi Date: Tue, 3 Nov 2020 22:37:00 +0100 Subject: [PATCH 12/12] doc(examples): Add an example on how to use functions env --- Cargo.toml | 7 +- examples/imports_function_env.rs | 129 +++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 examples/imports_function_env.rs diff --git a/Cargo.toml b/Cargo.toml index 54ba9c5b31b..5a1f672110b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -239,4 +239,9 @@ required-features = ["cranelift"] [[example]] name = "errors" path = "examples/errors.rs" -required-features = ["cranelift"] \ No newline at end of file +required-features = ["cranelift"] + +[[example]] +name = "imported-function-env" +path = "examples/imports_function_env.rs" +required-features = ["cranelift"] diff --git a/examples/imports_function_env.rs b/examples/imports_function_env.rs new file mode 100644 index 00000000000..88caf3c6ef7 --- /dev/null +++ b/examples/imports_function_env.rs @@ -0,0 +1,129 @@ +//! A Wasm module can import entities, like functions, memories, +//! globals and tables. +//! +//! In this example, we'll create a system for getting and adjusting a counter value. However, host +//! functions are not limited to storing data outside of WASM, they're normal host functions and +//! can do anything that the host can do. +//! +//! 1. There will be a `get_counter` function that will return an i32 of +//! the current global counter, +//! 2. There will be an `add_to_counter` function will add the passed +//! i32 value to the counter, and return an i32 of the current +//! global counter. +//! +//! You can run the example directly by executing in Wasmer root: +//! +//! ```shell +//! cargo run --example imported-function-env --release --features "cranelift" +//! ``` +//! +//! Ready? + +use std::cell::RefCell; +use std::sync::Arc; +use wasmer::{imports, wat2wasm, Function, Instance, Module, Store}; +use wasmer_compiler_cranelift::Cranelift; +use wasmer_engine_jit::JIT; + +fn main() -> Result<(), Box> { + // Let's declare the Wasm module with the text representation. + let wasm_bytes = wat2wasm( + br#" +(module + (func $get_counter (import "env" "get_counter") (result i32)) + (func $add_to_counter (import "env" "add_to_counter") (param i32) (result i32)) + + (type $increment_t (func (param i32) (result i32))) + (func $increment_f (type $increment_t) (param $x i32) (result i32) + (block + (loop + (call $add_to_counter (i32.const 1)) + (set_local $x (i32.sub (get_local $x) (i32.const 1))) + (br_if 1 (i32.eq (get_local $x) (i32.const 0))) + (br 0))) + call $get_counter) + (export "increment_counter_loop" (func $increment_f))) +"#, + )?; + + // Create a Store. + // Note that we don't need to specify the engine/compiler if we want to use + // the default provided by Wasmer. + // You can use `Store::default()` for that. + let store = Store::new(&JIT::new(&Cranelift::default()).engine()); + + println!("Compiling module..."); + // Let's compile the Wasm module. + let module = Module::new(&store, wasm_bytes)?; + + // We create some shared data here, `Arc` is required because we may + // move our WebAssembly instance to another thread to run it. RefCell + // lets us get shared mutabilty which is fine because we know we won't + // run host calls concurrently. If concurrency is a possibilty, we'd have + // to use a `Mutex`. + let shared_counter: Arc> = Arc::new(RefCell::new(0)); + + // Once we have our counter we'll wrap it inside en `Env` which we'll pass + // to our imported functions. + // + // This struct may have been anything. The only constraint is it must be + // possible to know the size of the `Env` at compile time (i.e it has to + // implement the `Sized` trait). + struct Env { + counter: Arc>, + } + + // Create the functions + fn get_counter(env: &mut Env) -> i32 { + *env.counter.borrow() + } + fn add_to_counter(env: &mut Env, add: i32) -> i32 { + let mut counter_ref = env.counter.borrow_mut(); + + *counter_ref += add; + *counter_ref + } + + // Create an import object. + let import_object = imports! { + "env" => { + "get_counter" => Function::new_native_with_env(&store, Env { counter: shared_counter.clone() }, get_counter), + "add_to_counter" => Function::new_native_with_env(&store, Env { counter: shared_counter.clone() }, add_to_counter), + } + }; + + println!("Instantiating module..."); + // Let's instantiate the Wasm module. + let instance = Instance::new(&module, &import_object)?; + + // Here we go. + // + // The Wasm module exports a function called `increment_counter_loop`. Let's get it. + let increment_counter_loop = instance + .exports + .get_function("increment_counter_loop")? + .native::()?; + + let counter_value: i32 = *shared_counter.borrow(); + println!("Initial ounter value: {:?}", counter_value); + + println!("Calling `increment_counter_loop` function..."); + // Let's call the `increment_counter_loop` exported function. + // + // It will loop five times thus incrementing our counter five times. + let result = increment_counter_loop.call(5)?; + + let counter_value: i32 = *shared_counter.borrow(); + println!("New counter value (host): {:?}", counter_value); + assert_eq!(counter_value, 5); + + println!("New counter value (guest): {:?}", counter_value); + assert_eq!(result, 5); + + Ok(()) +} + +#[test] +fn test_imported_function_env() -> Result<(), Box> { + main() +}