Skip to content

Commit

Permalink
doc(examples): Add Function examples
Browse files Browse the repository at this point in the history
  • Loading branch information
jubianchi committed Oct 26, 2020
1 parent 8782c0f commit 64bbfe6
Show file tree
Hide file tree
Showing 5 changed files with 281 additions and 11 deletions.
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
20 changes: 18 additions & 2 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,9 +197,24 @@ example.

</details>

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.

<details>
<summary><em>Execute the example</em></summary>

```shell
$ cargo run --example imported-function --release --features "cranelift"
```

</details>

### 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
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion examples/exports_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {

// 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.

Expand Down
107 changes: 107 additions & 0 deletions examples/imports_function.rs
Original file line number Diff line number Diff line change
@@ -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<dyn std::error::Error>> {
// 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<dyn std::error::Error>> {
main()
}
158 changes: 150 additions & 8 deletions lib/api/src/externals/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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| {
Expand Down Expand Up @@ -107,7 +107,7 @@ impl Function {
/// ```
/// # use wasmer::{Function, FunctionType, Type, Store, Value};
/// # let store = Store::default();
///
/// #
/// struct Env {
/// multiplier: i32,
/// };
Expand Down Expand Up @@ -160,7 +160,7 @@ impl Function {
/// ```
/// # use wasmer::{Store, Function};
/// # let store = Store::default();
///
/// #
/// fn sum(a: i32, b: i32) -> i32 {
/// a + b
/// }
Expand Down Expand Up @@ -202,7 +202,7 @@ impl Function {
/// ```
/// # use wasmer::{Store, Function};
/// # let store = Store::default();
///
/// #
/// struct Env {
/// multiplier: i32,
/// };
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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<Box<[Val]>, RuntimeError> {
let mut results = vec![Val::null(); self.result_arity()];

Expand Down Expand Up @@ -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<NativeFunc<'a, Args, Rets>, RuntimeError>
where
Args: WasmTypeList,
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 64bbfe6

Please sign in to comment.