Move all examples to a top-level directory (#1286)

* Move all examples to a top-level directory

This commit moves all API examples (Rust and C) to a top-level
`examples` directory. This is intended to make it more discoverable and
conventional as to where examples are located. Additionally all examples
are now available in both Rust and C to see how to execute the example
in the language you're familiar with. The intention is that as more
languages are supported we'd add more languages as examples here too.

Each example is also accompanied by either a `*.wat` file which is
parsed as input, or a Rust project in a `wasm` folder which is compiled
as input.

A simple driver crate was also added to `crates/misc` which executes all
the examples on CI, ensuring the C and Rust examples all execute
successfully.
This commit is contained in:
Alex Crichton
2020-03-11 15:37:24 -05:00
committed by GitHub
parent d44384da8a
commit 3c51d3adb8
33 changed files with 1131 additions and 528 deletions

View File

@@ -1,60 +0,0 @@
//! Example of instantiating of the WebAssembly module and
//! invoking its exported function.
use wasmtime::*;
const WAT: &str = r#"
(module
(func $gcd (param i32 i32) (result i32)
(local i32)
block ;; label = @1
block ;; label = @2
local.get 0
br_if 0 (;@2;)
local.get 1
local.set 2
br 1 (;@1;)
end
loop ;; label = @2
local.get 1
local.get 0
local.tee 2
i32.rem_u
local.set 0
local.get 2
local.set 1
local.get 0
br_if 0 (;@2;)
end
end
local.get 2
)
(export "gcd" (func $gcd))
)
"#;
fn main() -> anyhow::Result<()> {
// Load our WebAssembly (parsed WAT in our case), and then load it into a
// `Module` which is attached to a `Store` cache.
let store = Store::default();
let module = Module::new(&store, WAT)?;
// Find index of the `gcd` export.
let gcd_index = module
.exports()
.iter()
.enumerate()
.find(|(_, export)| export.name().to_string() == "gcd")
.unwrap()
.0;
// Instantiate the module.
let instance = Instance::new(&module, &[])?;
// Invoke `gcd` export
let gcd = instance.exports()[gcd_index].func().expect("gcd");
let result = gcd.call(&[Val::from(6i32), Val::from(27i32)])?;
println!("{:?}", result);
Ok(())
}

View File

@@ -1,50 +0,0 @@
//! Translation of hello example
use anyhow::{ensure, Context as _, Result};
use wasmtime::*;
fn main() -> Result<()> {
// Configure the initial compilation environment, creating the global
// `Store` structure. Note that you can also tweak configuration settings
// with a `Config` and an `Engine` if desired.
println!("Initializing...");
let store = Store::default();
// Compile the wasm binary into an in-memory instance of a `Module`.
println!("Compiling module...");
let wat = r#"
(module
(func $hello (import "" "hello"))
(func (export "run") (call $hello))
)
"#;
let module = Module::new(&store, wat).context("> Error compiling module!")?;
// Here we handle the imports of the module, which in this case is our
// `HelloCallback` type and its associated implementation of `Callback.
println!("Creating callback...");
let hello_func = Func::wrap0(&store, || {
println!("Calling back...");
println!("> Hello World!");
});
// Once we've got that all set up we can then move to the instantiation
// phase, pairing together a compiled module as well as a set of imports.
// Note that this is where the wasm `start` function, if any, would run.
println!("Instantiating module...");
let imports = vec![hello_func.into()];
let instance = Instance::new(&module, &imports).context("> Error instantiating module!")?;
// Next we poke around a bit to extract the `run` function from the module.
println!("Extracting export...");
let exports = instance.exports();
ensure!(!exports.is_empty(), "> Error accessing exports!");
let run_func = exports[0].func().context("> Error accessing exports!")?;
// And last but not least we can call it!
println!("Calling export...");
run_func.call(&[])?;
println!("Done.");
Ok(())
}

View File

@@ -1,160 +0,0 @@
//! Translation of the memory example
use anyhow::{bail, ensure, Context as _, Error};
use wasmtime::*;
fn get_export_memory(exports: &[Extern], i: usize) -> Result<Memory, Error> {
if exports.len() <= i {
bail!("> Error accessing memory export {}!", i);
}
Ok(exports[i]
.memory()
.with_context(|| format!("> Error accessing memory export {}!", i))?
.clone())
}
fn get_export_func(exports: &[Extern], i: usize) -> Result<Func, Error> {
if exports.len() <= i {
bail!("> Error accessing function export {}!", i);
}
Ok(exports[i]
.func()
.with_context(|| format!("> Error accessing function export {}!", i))?
.clone())
}
macro_rules! check {
($actual:expr, $expected:expr) => {
if $actual != $expected {
bail!("> Error on result, expected {}, got {}", $expected, $actual);
}
};
}
macro_rules! check_ok {
($func:expr, $($p:expr),*) => {
if let Err(_) = $func.call(&[$($p.into()),*]) {
bail!("> Error on result, expected return");
}
}
}
macro_rules! check_trap {
($func:expr, $($p:expr),*) => {
if let Ok(_) = $func.call(&[$($p.into()),*]) {
bail!("> Error on result, expected trap");
}
}
}
macro_rules! call {
($func:expr, $($p:expr),*) => {
match $func.call(&[$($p.into()),*]) {
Ok(result) => {
let result: i32 = result[0].unwrap_i32();
result
}
Err(_) => { bail!("> Error on result, expected return"); }
}
}
}
fn main() -> Result<(), Error> {
// Initialize.
println!("Initializing...");
let store = Store::default();
// Load binary.
println!("Loading binary...");
let wat = r#"
(module
(memory (export "memory") 2 3)
(func (export "size") (result i32) (memory.size))
(func (export "load") (param i32) (result i32)
(i32.load8_s (local.get 0))
)
(func (export "store") (param i32 i32)
(i32.store8 (local.get 0) (local.get 1))
)
(data (i32.const 0x1000) "\01\02\03\04")
)
"#;
// Compile.
println!("Compiling module...");
let module = Module::new(&store, &wat).context("> Error compiling module!")?;
// Instantiate.
println!("Instantiating module...");
let instance = Instance::new(&module, &[]).context("> Error instantiating module!")?;
// Extract export.
println!("Extracting export...");
let exports = instance.exports();
ensure!(!exports.is_empty(), "> Error accessing exports!");
let memory = get_export_memory(&exports, 0)?;
let size_func = get_export_func(&exports, 1)?;
let load_func = get_export_func(&exports, 2)?;
let store_func = get_export_func(&exports, 3)?;
// Check initial memory.
println!("Checking memory...");
check!(memory.size(), 2u32);
check!(memory.data_size(), 0x20000usize);
check!(unsafe { memory.data_unchecked_mut()[0] }, 0);
check!(unsafe { memory.data_unchecked_mut()[0x1000] }, 1);
check!(unsafe { memory.data_unchecked_mut()[0x1003] }, 4);
check!(call!(size_func,), 2);
check!(call!(load_func, 0), 0);
check!(call!(load_func, 0x1000), 1);
check!(call!(load_func, 0x1003), 4);
check!(call!(load_func, 0x1ffff), 0);
check_trap!(load_func, 0x20000);
// Mutate memory.
println!("Mutating memory...");
unsafe {
memory.data_unchecked_mut()[0x1003] = 5;
}
check_ok!(store_func, 0x1002, 6);
check_trap!(store_func, 0x20000, 0);
check!(unsafe { memory.data_unchecked()[0x1002] }, 6);
check!(unsafe { memory.data_unchecked()[0x1003] }, 5);
check!(call!(load_func, 0x1002), 6);
check!(call!(load_func, 0x1003), 5);
// Grow memory.
println!("Growing memory...");
memory.grow(1)?;
check!(memory.size(), 3u32);
check!(memory.data_size(), 0x30000usize);
check!(call!(load_func, 0x20000), 0);
check_ok!(store_func, 0x20000, 0);
check_trap!(load_func, 0x30000);
check_trap!(store_func, 0x30000, 0);
memory.grow(1).unwrap_err();
memory.grow(0).unwrap();
// Create stand-alone memory.
// TODO(wasm+): Once Wasm allows multiple memories, turn this into import.
println!("Creating stand-alone memory...");
let memorytype = MemoryType::new(Limits::new(5, Some(5)));
let memory2 = Memory::new(&store, memorytype);
check!(memory2.size(), 5u32);
memory2.grow(1).unwrap_err();
memory2.grow(0).unwrap();
// Shut down.
println!("Shutting down...");
drop(store);
println!("Done.");
Ok(())
}

View File

@@ -1,129 +0,0 @@
//! Translation of multi example
use anyhow::{ensure, format_err, Context as _, Result};
use std::rc::Rc;
use wasmtime::*;
struct Callback;
impl Callable for Callback {
fn call(&self, args: &[Val], results: &mut [Val]) -> Result<(), Trap> {
println!("Calling back...");
println!("> {} {}", args[0].unwrap_i32(), args[1].unwrap_i64());
results[0] = Val::I64(args[1].unwrap_i64() + 1);
results[1] = Val::I32(args[0].unwrap_i32() + 1);
Ok(())
}
}
const WAT: &str = r#"
(module
(func $f (import "" "f") (param i32 i64) (result i64 i32))
(func $g (export "g") (param i32 i64) (result i64 i32)
(call $f (local.get 0) (local.get 1))
)
(func $round_trip_many
(export "round_trip_many")
(param i64 i64 i64 i64 i64 i64 i64 i64 i64 i64)
(result i64 i64 i64 i64 i64 i64 i64 i64 i64 i64)
local.get 0
local.get 1
local.get 2
local.get 3
local.get 4
local.get 5
local.get 6
local.get 7
local.get 8
local.get 9)
)
"#;
fn main() -> Result<()> {
// Initialize.
println!("Initializing...");
let engine = Engine::new(Config::new().wasm_multi_value(true));
let store = Store::new(&engine);
// Compile.
println!("Compiling module...");
let module = Module::new(&store, WAT).context("Error compiling module!")?;
// Create external print functions.
println!("Creating callback...");
let callback_type = FuncType::new(
Box::new([ValType::I32, ValType::I64]),
Box::new([ValType::I64, ValType::I32]),
);
let callback_func = Func::new(&store, callback_type, Rc::new(Callback));
// Instantiate.
println!("Instantiating module...");
let imports = vec![callback_func.into()];
let instance =
Instance::new(&module, imports.as_slice()).context("Error instantiating module!")?;
// Extract exports.
println!("Extracting export...");
let exports = instance.exports();
ensure!(!exports.is_empty(), "Error accessing exports!");
let g = exports[0].func().context("> Error accessing export $g!")?;
let round_trip_many = exports[1]
.func()
.context("> Error accessing export $round_trip_many")?;
// Call `$g`.
println!("Calling export \"g\"...");
let args = vec![Val::I32(1), Val::I64(3)];
let results = g
.call(&args)
.map_err(|e| format_err!("> Error calling g! {:?}", e))?;
println!("Printing result...");
println!("> {} {}", results[0].unwrap_i64(), results[1].unwrap_i32());
debug_assert_eq!(results[0].unwrap_i64(), 4);
debug_assert_eq!(results[1].unwrap_i32(), 2);
// Call `$round_trip_many`.
println!("Calling export \"round_trip_many\"...");
let args = vec![
Val::I64(0),
Val::I64(1),
Val::I64(2),
Val::I64(3),
Val::I64(4),
Val::I64(5),
Val::I64(6),
Val::I64(7),
Val::I64(8),
Val::I64(9),
];
let results = round_trip_many
.call(&args)
.map_err(|e| format_err!("> Error calling round_trip_many! {:?}", e))?;
println!("Printing result...");
print!(">");
for r in results.iter() {
print!(" {}", r.unwrap_i64());
}
println!();
debug_assert_eq!(results.len(), 10);
debug_assert!(args
.iter()
.zip(results.iter())
.all(|(a, r)| a.i64() == r.i64()));
// Shut down.
println!("Shutting down...");
drop(store);
// All done.
println!("Done.");
Ok(())
}

View File

@@ -1,38 +0,0 @@
use std::env;
use std::process::{Command, Stdio};
fn run_example(name: &'static str) {
let cargo = env::var("CARGO").unwrap_or("cargo".to_string());
let pkg_dir = env!("CARGO_MANIFEST_DIR");
assert!(
Command::new(cargo)
.current_dir(pkg_dir)
.stdout(Stdio::null())
.args(&["run", "-q", "--example", name])
.status()
.expect("success")
.success(),
"failed to execute the example '{}'",
name,
);
}
#[test]
fn test_run_hello_example() {
run_example("hello");
}
#[test]
fn test_run_gcd_example() {
run_example("gcd");
}
#[test]
fn test_run_memory_example() {
run_example("memory");
}
#[test]
fn test_run_multi_example() {
run_example("multi");
}