From 8782c0f9ea750ba17c4528a9ed1a54773ff209d5 Mon Sep 17 00:00:00 2001 From: jubianchi Date: Thu, 22 Oct 2020 18:20:40 +0200 Subject: [PATCH 1/6] doc(examples): Add Global examples --- Cargo.toml | 10 +++ examples/README.md | 37 ++++++++- examples/exports_global.rs | 143 ++++++++++++++++++++++++++++++++ examples/imports_global.rs | 120 +++++++++++++++++++++++++++ lib/api/src/externals/global.rs | 110 +++++++++++++++++++++++- 5 files changed, 415 insertions(+), 5 deletions(-) create mode 100644 examples/exports_global.rs create mode 100644 examples/imports_global.rs diff --git a/Cargo.toml b/Cargo.toml index d57dc5679b5..bab9b51ff72 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -191,6 +191,16 @@ name = "exported-function" path = "examples/exports_function.rs" required-features = ["cranelift"] +[[example]] +name = "exported-global" +path = "examples/exports_global.rs" +required-features = ["cranelift"] + +[[example]] +name = "imported-global" +path = "examples/imports_global.rs" +required-features = ["cranelift"] + [[example]] name = "wasi" path = "examples/wasi.rs" diff --git a/examples/README.md b/examples/README.md index e53b87a00d1..a2650bc3156 100644 --- a/examples/README.md +++ b/examples/README.md @@ -150,8 +150,23 @@ example. ### Exports + +8. [**Exported global**][exported-global], explains how to work with + exported globals: get/set their value, have information about their + type. -8. [**Exported function**][exported-function], explains how to get and + _Keywords_: export, global. + +
+ Execute the example + + ```shell + $ cargo run --example exported-globals --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. @@ -166,9 +181,25 @@ example. +### Imports + +10. [**Imported global**][imported-global], explains how to work with + imported globals: create globals, import them, get/set their value. + + _Keywords_: import, global. + +
+ Execute the example + + ```shell + $ cargo run --example imported-globals --release --features "cranelift" + ``` + +
+ ### Integrations -9. [**WASI**][wasi], explains how to use the [WebAssembly System +11. [**WASI**][wasi], explains how to use the [WebAssembly System Interface][WASI] (WASI), i.e. the [`wasmer-wasi`] crate. _Keywords_: wasi, system, interface @@ -189,7 +220,9 @@ example. [compiler-cranelift]: ./compiler_cranelift.rs [compiler-llvm]: ./compiler_llvm.rs [cross-compilation]: ./engine_cross_compilation.rs +[exported-global]: ./exports_global.rs [exported-function]: ./exports_function.rs +[imported-global]: imports_global.rs [wasi]: ./wasi.rs [`wasmer-compiler-singlepass`]: https://github.com/wasmerio/wasmer/tree/master/lib/compiler-singlepass [`wasmer-compiler-cranelift`]: https://github.com/wasmerio/wasmer/tree/master/lib/compiler-cranelift diff --git a/examples/exports_global.rs b/examples/exports_global.rs new file mode 100644 index 00000000000..0ad1e9b65ac --- /dev/null +++ b/examples/exports_global.rs @@ -0,0 +1,143 @@ +//! A Wasm module can export entities, like functions, memories, +//! globals and tables. +//! +//! This example illustrates how to use exported globals. They come +//! in 2 flavors: +//! +//! 1. Immutable globals (const), +//! 2. Mutable globals. +//! +//! You can run the example directly by executing in Wasmer root: +//! +//! ```shell +//! cargo run --example exported-global --release --features "cranelift" +//! ``` +//! +//! Ready? + +use wasmer::{imports, wat2wasm, Instance, Module, Mutability, Store, Type, Value}; +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 + (global $one (export "one") f32 (f32.const 1)) + (global $some (export "some") (mut f32) (f32.const 0)) + + (func (export "get_one") (result f32) (global.get $one)) + (func (export "get_some") (result f32) (global.get $some)) + + (func (export "set_some") (param f32) (global.set $some (local.get 0)))) +"#, + )?; + + // 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)?; + + // Here we go. + // + // The Wasm module exports some globals. Let's get them. + // Note that + // + // ``` + // get_global(name) + // ``` + // + // is just an alias to + // + // ``` + // get::(name)`. + // ``` + let one = instance.exports.get_global("one")?; + let some = instance.exports.get_global("some")?; + + println!("Getting global type informations..."); + // 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); + assert_eq!(one_type.mutability, Mutability::Const); + assert_eq!(one_type.ty, Type::F32); + + println!("some type: {:?} {:?}", some_type.mutability, some_type.ty); + assert_eq!(some_type.mutability, Mutability::Var); + assert_eq!(some_type.ty, Type::F32); + + println!("Getting global values..."); + // Getting the values of globals can be done in two ways: + // 1. Through an exported function, + // 2. Using the Global API directly. + // + // We will use an exported function for the `one` global + // and the Global API for `some`. + let get_one = instance + .exports + .get_function("get_one")? + .native::<(), f32>()?; + + let one_result = get_one.call()?; + let some_result = some.get(); + + println!("one value: {:?}", one_result); + assert_eq!(one_result, 1.0); + + println!("some value: {:?}", some_result); + assert_eq!(some_result, Value::F32(0.0)); + + println!("Setting global values..."); + // Trying to set the value of a immutable global (`const`) + // will result in a `RuntimeError`. + let result = one.set(Value::F32(42.0)); + assert_eq!( + result.expect_err("Expected an error").message(), + "Attempted to set an immutable global" + ); + + let one_result = one.get(); + 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: + // 1. Through an exported function, + // 2. Using the Global API directly. + // + // We will use both for the `some` global. + let set_some = instance + .exports + .get_function("set_some")? + .native::()?; + set_some.call(21.0)?; + let some_result = some.get(); + 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); + assert_eq!(some_result, Value::F32(42.0)); + + Ok(()) +} + +#[test] +fn test_exported_global() -> Result<(), Box> { + main() +} diff --git a/examples/imports_global.rs b/examples/imports_global.rs new file mode 100644 index 00000000000..bba94b88390 --- /dev/null +++ b/examples/imports_global.rs @@ -0,0 +1,120 @@ +//! A Wasm module can import entities, like functions, memories, +//! globals and tables. +//! +//! This example illustrates how to use imported globals. They come +//! in 2 flavors: +//! +//! 1. Immutable globals (const), +//! 2. Mutable globals. +//! +//! You can run the example directly by executing in Wasmer root: +//! +//! ```shell +//! cargo run --example imported-global --release --features "cranelift" +//! ``` +//! +//! Ready? + +use wasmer::{imports, wat2wasm, Global, Instance, Module, Store, Value}; +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 + (global $some (import "env" "some") f32) + (global $other (import "env" "other") (mut f32)) + + (func (export "get_some") (result f32) (global.get $some)) + (func (export "get_other") (result f32) (global.get $other)) + + (func (export "set_other") (param f32) (global.set $other (local.get 0)))) +"#, + )?; + + // 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 the globals + let some = Global::new(&store, Value::F32(1.0)); + let other = Global::new_mut(&store, Value::F32(2.0)); + + // Create an import object. + // We add the two required globals in the `env` namespace. + let import_object = imports! { + "env" => { + "some" => some.clone(), + "other" => other.clone(), + } + }; + + println!("Instantiating module..."); + // Let's instantiate the Wasm module. + let instance = Instance::new(&module, &import_object)?; + + // Here we go. + // + // The Wasm module only imports some globals. We'll have to interact + // with them either using the Global API or exported functions. + let get_some = instance + .exports + .get_function("get_some")? + .native::<(), f32>()?; + let get_other = instance + .exports + .get_function("get_other")? + .native::<(), f32>()?; + + let some_result = get_some.call()?; + let other_result = get_other.call()?; + + println!("some value (via `get_some`): {:?}", some_result); + println!("some value (via Global API): {:?}", some.get()); + println!("other value (via `get_other`): {:?}", other_result); + println!("other value (via Global API): {:?}", other.get()); + + assert_eq!(some_result, some.get().f32().unwrap()); + assert_eq!(other_result, other.get().f32().unwrap()); + + println!("Setting global values..."); + // Trying to set the value of a immutable global (`const`) + // will result in a `RuntimeError`. + let result = some.set(Value::F32(42.0)); + assert_eq!( + result.expect_err("Expected an error").message(), + "Attempted to set an immutable global" + ); + + other.set(Value::F32(21.0))?; + let other_result = other.get(); + println!("other value after `set`: {:?}", other_result); + assert_eq!(other_result, Value::F32(21.0)); + + println!("Altering global values through exported functions..."); + // Changes made to global through exported functions will + // be reflected on the host side. + let set_other = instance + .exports + .get_function("set_other")? + .native::()?; + set_other.call(42.0)?; + + println!("other value (via Global API): {:?}", other.get()); + assert_eq!(other.get(), Value::F32(42.0)); + + Ok(()) +} + +#[test] +fn test_imported_global() -> Result<(), Box> { + main() +} diff --git a/lib/api/src/externals/global.rs b/lib/api/src/externals/global.rs index 9cdfef3c1ac..a306b9a94e5 100644 --- a/lib/api/src/externals/global.rs +++ b/lib/api/src/externals/global.rs @@ -23,11 +23,35 @@ pub struct Global { impl Global { /// Create a new `Global` with the initial value [`Val`]. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Global, Mutability, Store, Value}; + /// # let store = Store::default(); + /// # + /// let g = Global::new(&store, Value::I32(1)); + /// + /// assert_eq!(g.get(), Value::I32(1)); + /// assert_eq!(g.ty().mutability, Mutability::Const); + /// ``` pub fn new(store: &Store, val: Val) -> Self { Self::from_value(store, val, Mutability::Const).unwrap() } /// Create a mutable `Global` with the initial value [`Val`]. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Global, Mutability, Store, Value}; + /// # let store = Store::default(); + /// # + /// let g = Global::new_mut(&store, Value::I32(1)); + /// + /// assert_eq!(g.get(), Value::I32(1)); + /// assert_eq!(g.ty().mutability, Mutability::Var); + /// ``` pub fn new_mut(store: &Store, val: Val) -> Self { Self::from_value(store, val, Mutability::Var).unwrap() } @@ -54,27 +78,96 @@ impl Global { } /// Returns the [`GlobalType`] of the `Global`. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Global, Mutability, Store, Type, Value, GlobalType}; + /// # let store = Store::default(); + /// # + /// let c = Global::new(&store, Value::I32(1)); + /// let v = Global::new_mut(&store, Value::I64(1)); + /// + /// assert_eq!(c.ty(), &GlobalType::new(Type::I32, Mutability::Const)); + /// assert_eq!(v.ty(), &GlobalType::new(Type::I64, Mutability::Var)); + /// ``` pub fn ty(&self) -> &GlobalType { self.global.ty() } /// Returns the [`Store`] where the `Global` belongs. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Global, Store, Value}; + /// # let store = Store::default(); + /// # + /// let g = Global::new(&store, Value::I32(1)); + /// + /// assert_eq!(g.store(), &store); + /// ``` pub fn store(&self) -> &Store { &self.store } /// Retrieves the current value [`Val`] that the Global has. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Global, Store, Value}; + /// # let store = Store::default(); + /// # + /// let g = Global::new(&store, Value::I32(1)); + /// + /// assert_eq!(g.get(), Value::I32(1)); + /// ``` pub fn get(&self) -> Val { self.global.get() } /// Sets a custom value [`Val`] to the runtime Global. /// + /// # Example + /// + /// ``` + /// # use wasmer::{Global, Store, Value}; + /// # let store = Store::default(); + /// # + /// let g = Global::new_mut(&store, Value::I32(1)); + /// + /// assert_eq!(g.get(), Value::I32(1)); + /// + /// g.set(Value::I32(2)); + /// + /// assert_eq!(g.get(), Value::I32(2)); + /// ``` + /// /// # Errors /// - /// This function will error if: - /// * The global is not mutable - /// * The type of the `Val` doesn't matches the Global type. + /// Trying to mutate a immutable global will raise an error: + /// + /// ```should_panic + /// # use wasmer::{Global, Store, Value}; + /// # let store = Store::default(); + /// # + /// let g = Global::new(&store, Value::I32(1)); + /// + /// g.set(Value::I32(2)).unwrap(); + /// ``` + /// + /// Trying to set a value of a incompatible type will raise an error: + /// + /// ```should_panic + /// # use wasmer::{Global, Store, Value}; + /// # let store = Store::default(); + /// # + /// let g = Global::new(&store, Value::I32(1)); + /// + /// // This results in an error: `RuntimeError`. + /// g.set(Value::I64(2)).unwrap(); + /// ``` pub fn set(&self, val: Val) -> Result<(), RuntimeError> { if !val.comes_from_same_store(&self.store) { return Err(RuntimeError::new("cross-`Store` values are not supported")); @@ -95,6 +188,17 @@ impl Global { } /// Returns whether or not these two globals refer to the same data. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Global, Store, Value}; + /// # let store = Store::default(); + /// # + /// let g = Global::new(&store, Value::I32(1)); + /// + /// assert!(g.same(&g)); + /// ``` pub fn same(&self, other: &Self) -> bool { Arc::ptr_eq(&self.global, &other.global) } From 64bbfe620613cfda6d163aad4c1d4bd814d7f895 Mon Sep 17 00:00:00 2001 From: jubianchi Date: Fri, 23 Oct 2020 13:40:04 +0200 Subject: [PATCH 2/6] doc(examples): Add Function examples --- Cargo.toml | 5 + examples/README.md | 20 +++- examples/exports_function.rs | 2 +- examples/imports_function.rs | 107 ++++++++++++++++++++ lib/api/src/externals/function.rs | 158 ++++++++++++++++++++++++++++-- 5 files changed, 281 insertions(+), 11 deletions(-) create mode 100644 examples/imports_function.rs diff --git a/Cargo.toml b/Cargo.toml index bab9b51ff72..38a551a2850 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -196,6 +196,11 @@ name = "exported-global" path = "examples/exports_global.rs" required-features = ["cranelift"] +[[example]] +name = "imported-function" +path = "examples/imports_function.rs" +required-features = ["cranelift"] + [[example]] name = "imported-global" path = "examples/imports_global.rs" diff --git a/examples/README.md b/examples/README.md index a2650bc3156..2e860830e0f 100644 --- a/examples/README.md +++ b/examples/README.md @@ -197,9 +197,24 @@ example. +11. [**Imported function**][imported-function], explains how to define + an imported function. They come in 2 flavors: dynamic, + and “static”/native. + + _Keywords_: import, function, dynamic, static, native. + +
+ Execute the example + + ```shell + $ cargo run --example imported-function --release --features "cranelift" + ``` + +
+ ### Integrations -11. [**WASI**][wasi], explains how to use the [WebAssembly System +12. [**WASI**][wasi], explains how to use the [WebAssembly System Interface][WASI] (WASI), i.e. the [`wasmer-wasi`] crate. _Keywords_: wasi, system, interface @@ -222,7 +237,8 @@ example. [cross-compilation]: ./engine_cross_compilation.rs [exported-global]: ./exports_global.rs [exported-function]: ./exports_function.rs -[imported-global]: imports_global.rs +[imported-global]: ./imports_global.rs +[imported-function]: ./imports_function.rs [wasi]: ./wasi.rs [`wasmer-compiler-singlepass`]: https://github.com/wasmerio/wasmer/tree/master/lib/compiler-singlepass [`wasmer-compiler-cranelift`]: https://github.com/wasmerio/wasmer/tree/master/lib/compiler-cranelift diff --git a/examples/exports_function.rs b/examples/exports_function.rs index 9448cb06d1d..941733847bb 100644 --- a/examples/exports_function.rs +++ b/examples/exports_function.rs @@ -98,7 +98,7 @@ fn main() -> Result<(), Box> { // Much nicer, isn't it? // - // Those two API exist because they addres different needs. The + // Those two API exist because they address different needs. The // former has a more dynamic approach, while the second has a more // static approach. diff --git a/examples/imports_function.rs b/examples/imports_function.rs new file mode 100644 index 00000000000..7007b2f4f6e --- /dev/null +++ b/examples/imports_function.rs @@ -0,0 +1,107 @@ +//! A Wasm module can import entities, like functions, memories, +//! globals and tables. +//! +//! This example illustrates how to use imported functions. They come +//! in 2 flavors: +//! +//! 1. Dynamic functions, where parameters and results are of a +//! slice of `Value`, +//! 2. Native function, where parameters and results are statically +//! typed Rust values. +//! +//! You can run the example directly by executing in Wasmer root: +//! +//! ```shell +//! cargo run --example imported-function --release --features "cranelift" +//! ``` +//! +//! Ready? + +use wasmer::{imports, wat2wasm, Function, FunctionType, Instance, Module, Store, Type, Value}; +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 $multiply_dynamic (import "env" "multiply_dynamic") (param i32) (result i32)) + (func $multiply_native (import "env" "multiply_native") (param i32) (result i32)) + + (type $sum_t (func (param i32) (param i32) (result i32))) + (func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32) + (call $multiply_dynamic (local.get $x)) + (call $multiply_native (local.get $y)) + i32.add) + (export "sum" (func $sum_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 the functions + let multiply_dynamic_signature = FunctionType::new(vec![Type::I32], vec![Type::I32]); + let multiply_dynamic = Function::new(&store, &multiply_dynamic_signature, |args| { + println!("Calling `multiply_dynamic`..."); + + let result = args[0].unwrap_i32() * 2; + + println!("Result of `multiply_dynamic`: {:?}", result); + + Ok(vec![Value::I32(result)]) + }); + + fn multiply(a: i32) -> i32 { + println!("Calling `multiply_native`..."); + let result = a * 3; + + println!("Result of `multiply_native`: {:?}", result); + + result + } + let multiply_native = Function::new_native(&store, multiply); + + // Create an empty import object. + let import_object = imports! { + "env" => { + "multiply_dynamic" => multiply_dynamic, + "multiply_native" => multiply_native, + } + }; + + 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 `sum`. Let's get it. + let sum = instance + .exports + .get_function("sum")? + .native::<(i32, i32), i32>()?; + + println!("Calling `sum` function..."); + // Let's call the `sum` exported function. It will call each + // of the imported functions. + let result = sum.call(1, 2)?; + + println!("Results of `sum`: {:?}", result); + assert_eq!(result, 8); + + Ok(()) +} + +#[test] +fn test_exported_function() -> Result<(), Box> { + main() +} diff --git a/lib/api/src/externals/function.rs b/lib/api/src/externals/function.rs index 32af4b6cc3f..4cdf6b9121e 100644 --- a/lib/api/src/externals/function.rs +++ b/lib/api/src/externals/function.rs @@ -64,7 +64,7 @@ impl Function { /// ``` /// # use wasmer::{Function, FunctionType, Type, Store, Value}; /// # let store = Store::default(); - /// + /// # /// let signature = FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32]); /// /// let f = Function::new(&store, &signature, |args| { @@ -107,7 +107,7 @@ impl Function { /// ``` /// # use wasmer::{Function, FunctionType, Type, Store, Value}; /// # let store = Store::default(); - /// + /// # /// struct Env { /// multiplier: i32, /// }; @@ -160,7 +160,7 @@ impl Function { /// ``` /// # use wasmer::{Store, Function}; /// # let store = Store::default(); - /// + /// # /// fn sum(a: i32, b: i32) -> i32 { /// a + b /// } @@ -202,7 +202,7 @@ impl Function { /// ``` /// # use wasmer::{Store, Function}; /// # let store = Store::default(); - /// + /// # /// struct Env { /// multiplier: i32, /// }; @@ -245,7 +245,24 @@ impl Function { }, } } + /// Returns the [`FunctionType`] of the `Function`. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Function, Store, Type}; + /// # let store = Store::default(); + /// # + /// fn sum(a: i32, b: i32) -> i32 { + /// a + b + /// } + /// + /// let f = Function::new_native(&store, sum); + /// + /// assert_eq!(f.ty().params(), vec![Type::I32, Type::I32]); + /// assert_eq!(f.ty().results(), vec![Type::I32]); + /// ``` pub fn ty(&self) -> &FunctionType { &self.exported.signature } @@ -325,22 +342,74 @@ impl Function { } /// Returns the number of parameters that this function takes. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Function, Store, Type}; + /// # let store = Store::default(); + /// # + /// fn sum(a: i32, b: i32) -> i32 { + /// a + b + /// } + /// + /// let f = Function::new_native(&store, sum); + /// + /// assert_eq!(f.param_arity(), 2); + /// ``` pub fn param_arity(&self) -> usize { self.ty().params().len() } /// Returns the number of results this function produces. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Function, Store, Type}; + /// # let store = Store::default(); + /// # + /// fn sum(a: i32, b: i32) -> i32 { + /// a + b + /// } + /// + /// let f = Function::new_native(&store, sum); + /// + /// assert_eq!(f.result_arity(), 1); + /// ``` pub fn result_arity(&self) -> usize { self.ty().results().len() } - /// Call the [`Function`] function. + /// Call the `Function` function. /// /// Depending on where the Function is defined, it will call it. /// 1. If the function is defined inside a WebAssembly, it will call the trampoline /// for the function signature. /// 2. If the function is defined in the host (in a native way), it will /// call the trampoline. + /// + /// # Examples + /// + /// ``` + /// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, Value}; + /// # let store = Store::default(); + /// # let wasm_bytes = wat2wasm(r#" + /// # (module + /// # (func (export "sum") (param $x i32) (param $y i32) (result i32) + /// # local.get $x + /// # local.get $y + /// # i32.add + /// # )) + /// # "#.as_bytes()).unwrap(); + /// # let module = Module::new(&store, wasm_bytes).unwrap(); + /// # let import_object = imports! {}; + /// # let instance = Instance::new(&module, &import_object).unwrap(); + /// # + /// let sum = instance.exports.get_function("sum").unwrap(); + /// + /// assert_eq!(sum.call(&[Value::I32(1), Value::I32(2)]).unwrap().to_vec(), vec![Value::I32(3)]); + /// ``` pub fn call(&self, params: &[Val]) -> Result, RuntimeError> { let mut results = vec![Val::null(); self.result_arity()]; @@ -385,7 +454,80 @@ impl Function { } /// Transform this WebAssembly function into a function with the - /// native ABI. See `NativeFunc` to learn more. + /// native ABI. See [`NativeFunc`] to learn more. + /// + /// # Examples + /// + /// ``` + /// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, Value}; + /// # let store = Store::default(); + /// # let wasm_bytes = wat2wasm(r#" + /// # (module + /// # (func (export "sum") (param $x i32) (param $y i32) (result i32) + /// # local.get $x + /// # local.get $y + /// # i32.add + /// # )) + /// # "#.as_bytes()).unwrap(); + /// # let module = Module::new(&store, wasm_bytes).unwrap(); + /// # let import_object = imports! {}; + /// # let instance = Instance::new(&module, &import_object).unwrap(); + /// # + /// let sum = instance.exports.get_function("sum").unwrap(); + /// let sum_native = sum.native::<(i32, i32), i32>().unwrap(); + /// + /// assert_eq!(sum_native.call(1, 2).unwrap(), 3); + /// ``` + /// + /// # Errors + /// + /// If the `Args` generic parameter does not match the exported function + /// an error will be raised: + /// + /// ```should_panic + /// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, Value}; + /// # let store = Store::default(); + /// # let wasm_bytes = wat2wasm(r#" + /// # (module + /// # (func (export "sum") (param $x i32) (param $y i32) (result i32) + /// # local.get $x + /// # local.get $y + /// # i32.add + /// # )) + /// # "#.as_bytes()).unwrap(); + /// # let module = Module::new(&store, wasm_bytes).unwrap(); + /// # let import_object = imports! {}; + /// # let instance = Instance::new(&module, &import_object).unwrap(); + /// # + /// let sum = instance.exports.get_function("sum").unwrap(); + /// + /// // This results in an error: `RuntimeError` + /// let sum_native = sum.native::<(i64, i64), i32>().unwrap(); + /// ``` + /// + /// If the `Rets` generic parameter does not match the exported function + /// an error will be raised: + /// + /// ```should_panic + /// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, Value}; + /// # let store = Store::default(); + /// # let wasm_bytes = wat2wasm(r#" + /// # (module + /// # (func (export "sum") (param $x i32) (param $y i32) (result i32) + /// # local.get $x + /// # local.get $y + /// # i32.add + /// # )) + /// # "#.as_bytes()).unwrap(); + /// # let module = Module::new(&store, wasm_bytes).unwrap(); + /// # let import_object = imports! {}; + /// # let instance = Instance::new(&module, &import_object).unwrap(); + /// # + /// let sum = instance.exports.get_function("sum").unwrap(); + /// + /// // This results in an error: `RuntimeError` + /// let sum_native = sum.native::<(i32, i32), i64>().unwrap(); + /// ``` pub fn native<'a, Args, Rets>(&self) -> Result, RuntimeError> where Args: WasmTypeList, @@ -569,9 +711,9 @@ mod inner { /// A trait to convert a Rust value to a `WasmNativeType` value, /// or to convert `WasmNativeType` value to a Rust value. /// - /// This trait should ideally be splitted into two traits: + /// This trait should ideally be split into two traits: /// `FromNativeWasmType` and `ToNativeWasmType` but it creates a - /// non-negligeable complexity in the `WasmTypeList` + /// non-negligible complexity in the `WasmTypeList` /// implementation. pub unsafe trait FromToNativeWasmType: Copy where From 8245f369d177fee68cab8573e1971abdf9d85b0c Mon Sep 17 00:00:00 2001 From: jubianchi Date: Fri, 23 Oct 2020 17:18:53 +0200 Subject: [PATCH 3/6] doc(examples): Add Memory examples --- Cargo.toml | 5 ++ examples/README.md | 15 ++++ examples/exports_memory.rs | 120 ++++++++++++++++++++++++++++++++ lib/api/src/externals/memory.rs | 82 +++++++++++++++++++++- 4 files changed, 220 insertions(+), 2 deletions(-) create mode 100644 examples/exports_memory.rs diff --git a/Cargo.toml b/Cargo.toml index 38a551a2850..15ad15988ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -196,6 +196,11 @@ name = "exported-global" path = "examples/exports_global.rs" required-features = ["cranelift"] +[[example]] +name = "exported-memory" +path = "examples/exports_memory.rs" +required-features = ["cranelift"] + [[example]] name = "imported-function" path = "examples/imports_function.rs" diff --git a/examples/README.md b/examples/README.md index 2e860830e0f..7335a4322f1 100644 --- a/examples/README.md +++ b/examples/README.md @@ -181,6 +181,20 @@ example. +10. [**Exported memory**][exported-memory], explains how to read from + and write to exported memory. + + _Keywords_: export, memory. + +
+ Execute the example + + ```shell + $ cargo run --example exported-memory --release --features "cranelift" + ``` + +
+ ### Imports 10. [**Imported global**][imported-global], explains how to work with @@ -237,6 +251,7 @@ example. [cross-compilation]: ./engine_cross_compilation.rs [exported-global]: ./exports_global.rs [exported-function]: ./exports_function.rs +[exported-memory]: ./exports_memory.rs [imported-global]: ./imports_global.rs [imported-function]: ./imports_function.rs [wasi]: ./wasi.rs diff --git a/examples/exports_memory.rs b/examples/exports_memory.rs new file mode 100644 index 00000000000..59b468f5c7e --- /dev/null +++ b/examples/exports_memory.rs @@ -0,0 +1,120 @@ +//! A Wasm module can export entities, like functions, memories, +//! globals and tables. +//! +//! This example illustrates how to use exported memories +//! +//! You can run the example directly by executing in Wasmer root: +//! +//! ```shell +//! cargo run --example exported-memory --release --features "cranelift" +//! ``` +//! +//! Ready? + +use wasmer::{imports, wat2wasm, Array, Instance, Module, Store, WasmPtr}; +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 + (memory (export "mem") 1) + + (global $offset i32 (i32.const 42)) + (global $length (mut i32) (i32.const 13)) + + (func (export "load") (result i32 i32) + global.get $offset + global.get $length) + + (data (global.get $offset) "Hello, World!")) +"#, + )?; + + // 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)?; + + let load = instance + .exports + .get_native_function::<(), (WasmPtr, i32)>("load")?; + + // Here we go. + // + // The Wasm module exports a memory under "mem". Let's get it. + let memory = instance.exports.get_memory("mem")?; + + // Now that we have the exported memory, let's get some + // information about it. + // + // The first thing we might be intersted in is the size of the memory. + // Let's get it! + println!("Memory size (pages) {:?}", memory.size()); + println!("Memory size (bytes) {:?}", memory.data_size()); + + // Next, we'll want to read the contents of the memory. + // + // To do so, we have to get a `View` of the memory. + //let view = memory.view::(); + + // Oh! Wait, before reading the contents, we need to know + // where to find what we are looking for. + // + // Fortunately, the Wasm module exports a `load` function + // which will tell us the offset and length of the string. + let (ptr, length) = load.call()?; + println!("String offset: {:?}", ptr.offset()); + println!("String length: {:?}", length); + + // We now know where to fin our string, let's read it. + // + // We will get bytes out of the memory so we need to + // decode them into a string. + let str = ptr.get_utf8_string(memory, length as u32).unwrap(); + println!("Memory contents: {:?}", str); + + // What about changing the contents of the memory with a more + // appropriate string? + // + // To do that, we'll dereference our pointer and change the content + // of each `Cell` + let new_str = b"Hello, Wasmer!"; + let values = ptr.deref(memory, 0, new_str.len() as u32).unwrap(); + for i in 0..new_str.len() { + values[i].set(new_str[i]); + } + + // And now, let's see the result. + // + // Since the new strings is bigger than the older one, we + // query the length again. The offset remains the same as + // before. + println!("New string length: {:?}", new_str.len()); + + let str = ptr.get_utf8_string(memory, new_str.len() as u32).unwrap(); + println!("New memory contents: {:?}", str); + + // Much better, don't you think? + + Ok(()) +} + +#[test] +fn test_exported_memory() -> Result<(), Box> { + main() +} diff --git a/lib/api/src/externals/memory.rs b/lib/api/src/externals/memory.rs index 6d8cd596f22..2af9be79d88 100644 --- a/lib/api/src/externals/memory.rs +++ b/lib/api/src/externals/memory.rs @@ -34,6 +34,15 @@ impl Memory { /// This function will construct the `Memory` using the store [`Tunables`]. /// /// [`Tunables`]: crate::tunables::Tunables + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value}; + /// # let store = Store::default(); + /// # + /// let m = Memory::new(&store, MemoryType::new(1, None, false)).unwrap(); + /// ``` pub fn new(store: &Store, ty: MemoryType) -> Result { let tunables = store.tunables(); let style = tunables.memory_style(&ty); @@ -46,11 +55,34 @@ impl Memory { } /// Returns the [`MemoryType`] of the `Memory`. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value}; + /// # let store = Store::default(); + /// # + /// let mt = MemoryType::new(1, None, false); + /// let m = Memory::new(&store, mt).unwrap(); + /// + /// assert_eq!(m.ty(), &mt); + /// ``` pub fn ty(&self) -> &MemoryType { self.memory.ty() } /// Returns the [`Store`] where the `Memory` belongs. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value}; + /// # let store = Store::default(); + /// # + /// let m = Memory::new(&store, MemoryType::new(1, None, false)).unwrap(); + /// + /// assert_eq!(m.store(), &store); + /// ``` pub fn store(&self) -> &Store { &self.store } @@ -92,16 +124,51 @@ impl Memory { } /// Returns the size (in [`Pages`]) of the `Memory`. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value}; + /// # let store = Store::default(); + /// # + /// let m = Memory::new(&store, MemoryType::new(1, None, false)).unwrap(); + /// + /// assert_eq!(m.size(), Pages(1)); + /// ``` pub fn size(&self) -> Pages { self.memory.size() } - /// Grow memory by the specified amount of WebAssembly [`Pages`]. + /// Grow memory by the specified amount of WebAssembly [`Pages`] and return + /// the previous memory size. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value, WASM_MAX_PAGES}; + /// # let store = Store::default(); + /// # + /// let m = Memory::new(&store, MemoryType::new(1, Some(3), false)).unwrap(); + /// let p = m.grow(2).unwrap(); + /// + /// assert_eq!(p, Pages(1)); + /// assert_eq!(m.size(), Pages(3)); + /// ``` /// /// # Errors /// /// Returns an error if memory can't be grown by the specified amount /// of pages. + /// + /// ```should_panic + /// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value, WASM_MAX_PAGES}; + /// # let store = Store::default(); + /// # + /// let m = Memory::new(&store, MemoryType::new(1, Some(1), false)).unwrap(); + /// + /// // This results in an error: `MemoryError::CouldNotGrow`. + /// let s = m.grow(1).unwrap(); + /// ``` pub fn grow(&self, delta: IntoPages) -> Result where IntoPages: Into, @@ -155,7 +222,18 @@ impl Memory { } } - /// Returns whether or not these two globals refer to the same data. + /// Returns whether or not these two memories refer to the same data. + /// + /// # Example + /// + /// ``` + /// # use wasmer::{Memory, MemoryType, Store, Value}; + /// # let store = Store::default(); + /// # + /// let m = Memory::new(&store, MemoryType::new(1, None, false)).unwrap(); + /// + /// assert!(m.same(&m)); + /// ``` pub fn same(&self, other: &Self) -> bool { Arc::ptr_eq(&self.memory, &other.memory) } From 120f0e2b5d1833bb5764f074d68f341460c53451 Mon Sep 17 00:00:00 2001 From: jubianchi Date: Fri, 23 Oct 2020 22:37:49 +0200 Subject: [PATCH 4/6] doc(test): Add doctests on ExportError --- lib/api/src/exports.rs | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/lib/api/src/exports.rs b/lib/api/src/exports.rs index c754ab819c9..744605a2476 100644 --- a/lib/api/src/exports.rs +++ b/lib/api/src/exports.rs @@ -14,16 +14,37 @@ use wasmer_vm::Export; /// /// [`Instance`]: crate::Instance /// -/// ```ignore -/// # let my_instance = Instance::new(...); +/// # Examples /// +/// ## Incompatible export type +/// +/// ```should_panic +/// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, Value, ExportError}; +/// # let store = Store::default(); +/// # let wasm_bytes = wat2wasm(r#" +/// # (module +/// # (global $one (export "glob") f32 (f32.const 1))) +/// # "#.as_bytes()).unwrap(); +/// # let module = Module::new(&store, wasm_bytes).unwrap(); +/// # let import_object = imports! {}; +/// # let instance = Instance::new(&module, &import_object).unwrap(); +/// # /// // This results with an error: `ExportError::IncompatibleType`. -/// let missing_import: &Global = my_instance.exports.get("func")?; -/// let missing_import = my_instance.exports.get_global("func")?; +/// let export = instance.exports.get_function("glob").unwrap(); +/// ``` +/// +/// ## Missing export /// +/// ```should_panic +/// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, Value, ExportError}; +/// # let store = Store::default(); +/// # let wasm_bytes = wat2wasm("(module)".as_bytes()).unwrap(); +/// # let module = Module::new(&store, wasm_bytes).unwrap(); +/// # let import_object = imports! {}; +/// # let instance = Instance::new(&module, &import_object).unwrap(); +/// # /// // This results with an error: `ExportError::Missing`. -/// let missing_import: &Function = my_instance.exports.get("unknown")?; -/// let missing_import = my_instance.exports.get_function("unknown")?; +/// let export = instance.exports.get_function("unknown").unwrap(); /// ``` #[derive(Error, Debug)] pub enum ExportError { From 2452b9a604447d4fdf1b5c5ece67c823aeefc0a7 Mon Sep 17 00:00:00 2001 From: jubianchi Date: Fri, 23 Oct 2020 22:39:33 +0200 Subject: [PATCH 5/6] chore(doc): Add the Wasmer logo to the generated API documentation --- lib/api/src/lib.rs | 4 ++++ lib/c-api/src/lib.rs | 2 +- lib/cli/src/lib.rs | 2 +- lib/compiler-llvm/src/lib.rs | 2 +- lib/emscripten/src/lib.rs | 2 +- lib/wasi/src/lib.rs | 2 +- 6 files changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/api/src/lib.rs b/lib/api/src/lib.rs index 2a1f42c246c..38408b2e888 100644 --- a/lib/api/src/lib.rs +++ b/lib/api/src/lib.rs @@ -1,4 +1,8 @@ //! Wasmer API +#![doc( + html_logo_url = "https://github.com/wasmerio.png?size=200", + html_favicon_url = "https://wasmer.io/static/icons/favicon.ico" +)] #![deny( missing_docs, trivial_numeric_casts, diff --git a/lib/c-api/src/lib.rs b/lib/c-api/src/lib.rs index b4e5ff50273..dea53e9feb3 100644 --- a/lib/c-api/src/lib.rs +++ b/lib/c-api/src/lib.rs @@ -18,7 +18,7 @@ //! this repository for the sake of simplicity. #![doc(html_favicon_url = "https://wasmer.io/static/icons/favicon.ico")] -#![doc(html_logo_url = "https://avatars3.githubusercontent.com/u/44205449?s=200&v=4")] +#![doc(html_logo_url = "https://github.com/wasmerio.png?size=200")] // temporary while in transition #![allow(unused_variables)] #![deny( diff --git a/lib/cli/src/lib.rs b/lib/cli/src/lib.rs index 8ee5e428bdb..bd8b30171ac 100644 --- a/lib/cli/src/lib.rs +++ b/lib/cli/src/lib.rs @@ -11,7 +11,7 @@ unstable_features )] #![doc(html_favicon_url = "https://wasmer.io/static/icons/favicon.ico")] -#![doc(html_logo_url = "https://avatars3.githubusercontent.com/u/44205449?s=200&v=4")] +#![doc(html_logo_url = "https://github.com/wasmerio.png?size=200")] #[macro_use] extern crate anyhow; diff --git a/lib/compiler-llvm/src/lib.rs b/lib/compiler-llvm/src/lib.rs index 5bbbc73098a..a640d360577 100644 --- a/lib/compiler-llvm/src/lib.rs +++ b/lib/compiler-llvm/src/lib.rs @@ -12,7 +12,7 @@ )] #![cfg_attr(nightly, feature(unwind_attributes))] #![doc(html_favicon_url = "https://wasmer.io/static/icons/favicon.ico")] -#![doc(html_logo_url = "https://avatars3.githubusercontent.com/u/44205449?s=200&v=4")] +#![doc(html_logo_url = "https://github.com/wasmerio.png?size=200")] mod abi; mod compiler; diff --git a/lib/emscripten/src/lib.rs b/lib/emscripten/src/lib.rs index a08c2532064..def9825b66e 100644 --- a/lib/emscripten/src/lib.rs +++ b/lib/emscripten/src/lib.rs @@ -8,7 +8,7 @@ unreachable_patterns )] #![doc(html_favicon_url = "https://wasmer.io/static/icons/favicon.ico")] -#![doc(html_logo_url = "https://avatars3.githubusercontent.com/u/44205449?s=200&v=4")] +#![doc(html_logo_url = "https://github.com/wasmerio.png?size=200")] #[macro_use] extern crate log; diff --git a/lib/wasi/src/lib.rs b/lib/wasi/src/lib.rs index 74a0fab3819..80934bfb741 100644 --- a/lib/wasi/src/lib.rs +++ b/lib/wasi/src/lib.rs @@ -1,6 +1,6 @@ #![deny(unused_mut)] #![doc(html_favicon_url = "https://wasmer.io/static/icons/favicon.ico")] -#![doc(html_logo_url = "https://avatars3.githubusercontent.com/u/44205449?s=200&v=4")] +#![doc(html_logo_url = "https://github.com/wasmerio.png?size=200")] //! Wasmer's WASI implementation //! From 3f1f94bc84a8baf3ad8b37bd7827688175e3c2ff Mon Sep 17 00:00:00 2001 From: jubianchi Date: Fri, 23 Oct 2020 23:00:58 +0200 Subject: [PATCH 6/6] chore(example): Renumber the examples --- examples/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/README.md b/examples/README.md index 7335a4322f1..aa0e1400a01 100644 --- a/examples/README.md +++ b/examples/README.md @@ -197,7 +197,7 @@ example. ### Imports -10. [**Imported global**][imported-global], explains how to work with +11. [**Imported global**][imported-global], explains how to work with imported globals: create globals, import them, get/set their value. _Keywords_: import, global. @@ -211,7 +211,7 @@ example. -11. [**Imported function**][imported-function], explains how to define +12. [**Imported function**][imported-function], explains how to define an imported function. They come in 2 flavors: dynamic, and “static”/native. @@ -228,7 +228,7 @@ example. ### Integrations -12. [**WASI**][wasi], explains how to use the [WebAssembly System +13. [**WASI**][wasi], explains how to use the [WebAssembly System Interface][WASI] (WASI), i.e. the [`wasmer-wasi`] crate. _Keywords_: wasi, system, interface