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:
10
.github/workflows/main.yml
vendored
10
.github/workflows/main.yml
vendored
@@ -184,9 +184,10 @@ jobs:
|
||||
Get-Command clang.exe
|
||||
clang.exe --version
|
||||
|
||||
# Install wasm32-wasi target in order to build wasi-common's integration
|
||||
# tests
|
||||
# Install wasm32 targets in order to build various tests throughout the
|
||||
# repo
|
||||
- run: rustup target add wasm32-wasi
|
||||
- run: rustup target add wasm32-unknown-unknown
|
||||
|
||||
- run: cargo fetch --locked
|
||||
- run: cargo fetch --locked --manifest-path crates/test-programs/wasi-tests/Cargo.toml
|
||||
@@ -197,6 +198,9 @@ jobs:
|
||||
- run: cargo build --manifest-path crates/api/Cargo.toml --features lightbeam
|
||||
if: matrix.rust == 'nightly'
|
||||
|
||||
# Ensure all our examples build and execute
|
||||
- run: cargo run -p run-examples
|
||||
|
||||
# Build and test all features except for lightbeam
|
||||
- run: cargo test --features test_programs --all --exclude lightbeam --exclude wasmtime-c-api -- --nocapture
|
||||
env:
|
||||
@@ -205,7 +209,7 @@ jobs:
|
||||
|
||||
# Test debug (DWARF) related functionality.
|
||||
- run: cargo test test_debug_dwarf_ -- --ignored --nocapture --test-threads 1
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
RUSTFLAGS: "-D warnings"
|
||||
|
||||
2
.gitmodules
vendored
2
.gitmodules
vendored
@@ -2,7 +2,7 @@
|
||||
path = tests/spec_testsuite
|
||||
url = https://github.com/WebAssembly/testsuite
|
||||
[submodule "crates/c-api/examples/wasm-c-api"]
|
||||
path = crates/c-api/examples/wasm-c-api
|
||||
path = crates/c-api/wasm-c-api
|
||||
url = https://github.com/WebAssembly/wasm-c-api
|
||||
[submodule "crates/wasi-common/WASI"]
|
||||
path = crates/wasi-common/wig/WASI
|
||||
|
||||
11
Cargo.lock
generated
11
Cargo.lock
generated
@@ -837,6 +837,10 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "example-fib-debug-wasm"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "faerie"
|
||||
version = "0.14.0"
|
||||
@@ -1864,6 +1868,13 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "run-examples"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-argon2"
|
||||
version = "0.7.0"
|
||||
|
||||
10
Cargo.toml
10
Cargo.toml
@@ -63,12 +63,14 @@ opt-level = 0
|
||||
[workspace]
|
||||
members = [
|
||||
"cranelift",
|
||||
"crates/fuzzing",
|
||||
"crates/misc/rust",
|
||||
"crates/misc/py",
|
||||
"crates/c-api",
|
||||
"fuzz",
|
||||
"crates/fuzzing",
|
||||
"crates/misc/py",
|
||||
"crates/misc/run-examples",
|
||||
"crates/misc/rust",
|
||||
"crates/wiggle",
|
||||
"examples/fib-debug/wasm",
|
||||
"fuzz",
|
||||
]
|
||||
|
||||
[features]
|
||||
|
||||
@@ -48,7 +48,7 @@ mkdir tmp/$api_pkgname/lib
|
||||
mkdir tmp/$api_pkgname/include
|
||||
cp LICENSE README.md tmp/$api_pkgname
|
||||
mv bins-$src/* tmp/$api_pkgname/lib
|
||||
cp crates/c-api/examples/wasm-c-api/include/wasm.h tmp/$api_pkgname/include
|
||||
cp crates/c-api/wasm-c-api/include/wasm.h tmp/$api_pkgname/include
|
||||
cp crates/c-api/include/{wasmtime,wasi}.h tmp/$api_pkgname/include
|
||||
mktarball $api_pkgname
|
||||
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
###############################################################################
|
||||
# Configuration
|
||||
|
||||
# Inherited from wasm-c-api/Makefile to just run C examples
|
||||
|
||||
WASM_FLAGS = -DWASM_API_DEBUG # -DWASM_API_DEBUG_LOG
|
||||
C_FLAGS = ${WASM_FLAGS} -Wall -Werror -ggdb -O -fsanitize=address
|
||||
CC_FLAGS = -std=c++11 ${C_FLAGS}
|
||||
LD_FLAGS = -fsanitize-memory-track-origins -fsanitize-memory-use-after-dtor
|
||||
|
||||
C_COMP = clang
|
||||
|
||||
WASMTIME_API_MODE = debug
|
||||
|
||||
|
||||
# Base directories
|
||||
WASMTIME_API_DIR = ..
|
||||
WASM_DIR = wasm-c-api
|
||||
EXAMPLE_DIR = ${WASM_DIR}/example
|
||||
OUT_DIR = ${WASM_DIR}/out
|
||||
|
||||
# Example config
|
||||
EXAMPLE_OUT = ${OUT_DIR}/example
|
||||
EXAMPLES = \
|
||||
hello \
|
||||
callback \
|
||||
trap \
|
||||
start \
|
||||
reflect \
|
||||
global \
|
||||
table \
|
||||
memory \
|
||||
hostref \
|
||||
finalize \
|
||||
serialize \
|
||||
threads \
|
||||
# multi \
|
||||
|
||||
# Wasm config
|
||||
WASM_INCLUDE = ${WASM_DIR}/include
|
||||
WASM_SRC = ${WASM_DIR}/src
|
||||
WASM_OUT = ${OUT_DIR}
|
||||
WASM_C_LIBS = wasm-bin wasm-rust-api
|
||||
WASM_CC_LIBS = $(error unsupported C++)
|
||||
|
||||
|
||||
# Compiler config
|
||||
ifeq (${WASMTIME_API_MODE},release)
|
||||
CARGO_BUILD_FLAGS = --release
|
||||
else
|
||||
CARGO_BUILD_FLAGS =
|
||||
endif
|
||||
|
||||
ifeq (${C_COMP},clang)
|
||||
CC_COMP = clang++
|
||||
LD_GROUP_START =
|
||||
LD_GROUP_END =
|
||||
else ifeq (${C_COMP},gcc)
|
||||
CC_COMP = g++
|
||||
LD_GROUP_START = -Wl,--start-group
|
||||
LD_GROUP_END = -Wl,--end-group
|
||||
else
|
||||
$(error C_COMP set to unknown compiler, must be clang or gcc)
|
||||
endif
|
||||
|
||||
WASMTIME_BIN_DIR = ${WASMTIME_API_DIR}/../../target/${WASMTIME_API_MODE}
|
||||
WASMTIME_C_LIB_DIR = ${WASMTIME_BIN_DIR}
|
||||
WASMTIME_C_LIBS = wasmtime
|
||||
WASMTIME_CC_LIBS = $(error unsupported c++)
|
||||
|
||||
WASMTIME_C_BINS = ${WASMTIME_C_LIBS:%=${WASMTIME_C_LIB_DIR}/lib%.a}
|
||||
|
||||
###############################################################################
|
||||
# Examples
|
||||
#
|
||||
# To build Wasm APIs and run all examples:
|
||||
# make all
|
||||
#
|
||||
# To run only C examples:
|
||||
# make c
|
||||
#
|
||||
# To run only C++ examples:
|
||||
# make cc
|
||||
#
|
||||
# To run individual C example (e.g. hello):
|
||||
# make run-hello-c
|
||||
#
|
||||
# To run individual C++ example (e.g. hello):
|
||||
# make run-hello-cc
|
||||
#
|
||||
|
||||
.PHONY: all cc c
|
||||
all: cc c
|
||||
cc: ${EXAMPLES:%=run-%-cc}
|
||||
c: ${EXAMPLES:%=run-%-c}
|
||||
|
||||
# Running a C / C++ example
|
||||
run-%-c: ${EXAMPLE_OUT}/%-c ${EXAMPLE_OUT}/%.wasm
|
||||
@echo ==== C ${@:run-%-c=%} ====; \
|
||||
cd ${EXAMPLE_OUT}; ./${@:run-%=%}
|
||||
@echo ==== Done ====
|
||||
|
||||
run-%-cc: ${EXAMPLE_OUT}/%-cc ${EXAMPLE_OUT}/%.wasm
|
||||
@echo ==== C++ ${@:run-%-cc=%} ====; \
|
||||
cd ${EXAMPLE_OUT}; ./${@:run-%=%}
|
||||
@echo ==== Done ====
|
||||
|
||||
# Compiling C / C++ example
|
||||
${EXAMPLE_OUT}/%-c.o: ${EXAMPLE_DIR}/%.c ${WASM_INCLUDE}/wasm.h
|
||||
mkdir -p ${EXAMPLE_OUT}
|
||||
${C_COMP} -c ${C_FLAGS} -I. -I${WASM_INCLUDE} $< -o $@
|
||||
|
||||
${EXAMPLE_OUT}/%-cc.o: ${EXAMPLE_DIR}/%.cc ${WASM_INCLUDE}/wasm.hh
|
||||
mkdir -p ${EXAMPLE_OUT}
|
||||
${CC_COMP} -c ${CC_FLAGS} -I. -I${WASM_INCLUDE} $< -o $@
|
||||
|
||||
# Linking C / C++ example
|
||||
.PRECIOUS: ${EXAMPLES:%=${EXAMPLE_OUT}/%-c}
|
||||
${EXAMPLE_OUT}/%-c: ${EXAMPLE_OUT}/%-c.o ${WASMTIME_C_BINS}
|
||||
${CC_COMP} ${CC_FLAGS} ${LD_FLAGS} $< -o $@ \
|
||||
${LD_GROUP_START} \
|
||||
${WASMTIME_C_BINS} \
|
||||
${LD_GROUP_END} \
|
||||
-ldl -pthread
|
||||
|
||||
# Installing Wasm binaries
|
||||
.PRECIOUS: ${EXAMPLES:%=${EXAMPLE_OUT}/%.wasm}
|
||||
${EXAMPLE_OUT}/%.wasm: ${EXAMPLE_DIR}/%.wasm
|
||||
cp $< $@
|
||||
|
||||
###############################################################################
|
||||
# Wasm C / C++ API
|
||||
#
|
||||
# To build both C / C++ APIs:
|
||||
# make wasm
|
||||
|
||||
.PHONY: wasm wasm-c wasm-cc
|
||||
wasm: wasm-c wasm-cc
|
||||
wasm-c: ${WASMTIME_C_BIN}
|
||||
wasm-cc: ${WASMTIME_CC_BIN}
|
||||
|
||||
${WASMTIME_C_BINS}: CARGO_RUN
|
||||
cd ${WASMTIME_API_DIR}; cargo build --lib ${CARGO_BUILD_FLAGS}
|
||||
|
||||
.PHONY: CARGO_RUN
|
||||
CARGO_RUN:
|
||||
|
||||
|
||||
###############################################################################
|
||||
# Clean-up
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf ${OUT_DIR}
|
||||
|
||||
###############################################################################
|
||||
# Other examples
|
||||
|
||||
WASM_EXT_INCLUDE = ${WASMTIME_API_DIR}/include
|
||||
|
||||
run-config-debug-c: ${EXAMPLE_OUT}/config-debug-c ${EXAMPLE_OUT}/fib-wasm.wasm
|
||||
@echo ==== C config ====; \
|
||||
cd ${EXAMPLE_OUT}; ./config-debug-c
|
||||
@echo ==== Done ====
|
||||
|
||||
${EXAMPLE_OUT}/fib-wasm.wasm: fib-wasm.wasm
|
||||
cp $< $@
|
||||
|
||||
${EXAMPLE_OUT}/config-debug-c.o: config-debug.c ${WASM_INCLUDE}/wasm.h ${WASM_EXT_INCLUDE}/wasmtime.h
|
||||
mkdir -p ${EXAMPLE_OUT}
|
||||
${C_COMP} -c ${C_FLAGS} -I. -I${WASM_INCLUDE} -I${WASM_EXT_INCLUDE} $< -o $@
|
||||
@@ -1,13 +0,0 @@
|
||||
// Compile with:
|
||||
// clang --target=wasm32 fib-wasm.c -o fib-wasm.wasm -g \
|
||||
// -Wl,--no-entry,--export=fib -nostdlib -fdebug-prefix-map=$PWD=.
|
||||
|
||||
int fib(int n) {
|
||||
int i, t, a = 0, b = 1;
|
||||
for (i = 0; i < n; i++) {
|
||||
t = a;
|
||||
a = b;
|
||||
b += t;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
Binary file not shown.
@@ -9,6 +9,8 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define own
|
||||
|
||||
typedef uint8_t wasmtime_strategy_t;
|
||||
enum wasmtime_strategy_enum { // Strategy
|
||||
WASMTIME_STRATEGY_AUTO,
|
||||
@@ -51,13 +53,15 @@ WASMTIME_CONFIG_PROP(cranelift_opt_level, wasmtime_opt_level_t)
|
||||
// optional.
|
||||
//
|
||||
// Returns `true` if conversion succeeded, or `false` if it failed.
|
||||
bool wasmtime_wat2wasm(
|
||||
WASM_API_EXTERN bool wasmtime_wat2wasm(
|
||||
wasm_engine_t *engine,
|
||||
const wasm_byte_vec_t *wat,
|
||||
own wasm_byte_vec_t *ret,
|
||||
own wasm_byte_vec_t *error_message,
|
||||
own wasm_byte_vec_t *error_message
|
||||
);
|
||||
|
||||
#undef own
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
9
crates/misc/run-examples/Cargo.toml
Normal file
9
crates/misc/run-examples/Cargo.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "run-examples"
|
||||
version = "0.1.0"
|
||||
authors = ["The Wasmtime Project Developers"]
|
||||
edition = "2018"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
cc = "1.0"
|
||||
6
crates/misc/run-examples/build.rs
Normal file
6
crates/misc/run-examples/build.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
fn main() {
|
||||
println!(
|
||||
"cargo:rustc-env=TARGET={}",
|
||||
std::env::var("TARGET").unwrap()
|
||||
);
|
||||
}
|
||||
83
crates/misc/run-examples/src/main.rs
Normal file
83
crates/misc/run-examples/src/main.rs
Normal file
@@ -0,0 +1,83 @@
|
||||
use std::collections::BTreeSet;
|
||||
use std::process::Command;
|
||||
|
||||
fn main() {
|
||||
let examples = std::fs::read_dir("examples").unwrap();
|
||||
let examples = examples
|
||||
.map(|e| {
|
||||
let e = e.unwrap();
|
||||
let path = e.path();
|
||||
let dir = e.metadata().unwrap().is_dir();
|
||||
(path.file_stem().unwrap().to_str().unwrap().to_owned(), dir)
|
||||
})
|
||||
.collect::<BTreeSet<_>>();
|
||||
|
||||
println!("======== Building libwasmtime.a ===========");
|
||||
run(Command::new("cargo").args(&["build", "-p", "wasmtime-c-api"]));
|
||||
|
||||
for (example, is_dir) in examples {
|
||||
if example == "README" {
|
||||
continue;
|
||||
}
|
||||
if is_dir {
|
||||
println!("======== Rust wasm file `{}` ============", example);
|
||||
run(Command::new("cargo")
|
||||
.arg("build")
|
||||
.arg("-p")
|
||||
.arg(format!("example-{}-wasm", example))
|
||||
.arg("--target")
|
||||
.arg("wasm32-unknown-unknown"));
|
||||
}
|
||||
println!("======== Rust example `{}` ============", example);
|
||||
run(Command::new("cargo")
|
||||
.arg("run")
|
||||
.arg("--example")
|
||||
.arg(&example));
|
||||
|
||||
println!("======== C example `{}` ============", example);
|
||||
let mut cmd = cc::Build::new()
|
||||
.opt_level(0)
|
||||
.cargo_metadata(false)
|
||||
.target(env!("TARGET"))
|
||||
.host(env!("TARGET"))
|
||||
.include("crates/c-api/include")
|
||||
.include("crates/c-api/wasm-c-api/include")
|
||||
.define("WASM_API_EXTERN", Some("")) // static linkage, not dynamic
|
||||
.warnings(false)
|
||||
.get_compiler()
|
||||
.to_command();
|
||||
if is_dir {
|
||||
cmd.arg(format!("examples/{}/main.c", example));
|
||||
} else {
|
||||
cmd.arg(format!("examples/{}.c", example));
|
||||
}
|
||||
let exe = if cfg!(windows) {
|
||||
cmd.arg("target/debug/wasmtime.lib")
|
||||
.arg("ws2_32.lib")
|
||||
.arg("advapi32.lib")
|
||||
.arg("userenv.lib")
|
||||
.arg("ntdll.lib")
|
||||
.arg("shell32.lib")
|
||||
.arg("ole32.lib");
|
||||
"./main.exe"
|
||||
} else {
|
||||
cmd.arg("target/debug/libwasmtime.a").arg("-o").arg("foo");
|
||||
"./foo"
|
||||
};
|
||||
if cfg!(target_os = "linux") {
|
||||
cmd.arg("-lpthread").arg("-ldl").arg("-lm");
|
||||
}
|
||||
run(&mut cmd);
|
||||
|
||||
run(&mut Command::new(exe));
|
||||
}
|
||||
}
|
||||
|
||||
fn run(cmd: &mut Command) {
|
||||
let s = cmd.status().unwrap();
|
||||
if !s.success() {
|
||||
eprintln!("failed to run {:?}", cmd);
|
||||
eprintln!("status: {}", s);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
14
examples/README.md
Normal file
14
examples/README.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# Examples of the `wasmtime` API
|
||||
|
||||
This directory contains a number of examples of using the `wasmtime` API from
|
||||
different languages. Currently examples are all in Rust and C using the
|
||||
`wasmtime` crate or the wasmtime embedding API.
|
||||
|
||||
Each example is available in both C and in Rust. Examples are accompanied with a
|
||||
`*.wat` file which is the wasm input, or a Rust project in a `wasm` folder which
|
||||
is the source code for the original wasm file.
|
||||
|
||||
Rust examples can be executed with `cargo run --example $name`, and C examples
|
||||
need to be compiled using your system compiler and appropriate header files.
|
||||
|
||||
For more information see the examples themselves!
|
||||
@@ -22,9 +22,9 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
// Load binary.
|
||||
printf("Loading binary...\n");
|
||||
FILE* file = fopen("fib-wasm.wasm", "r");
|
||||
FILE* file = fopen("target/wasm32-unknown-unknown/debug/fib.wasm", "rb");
|
||||
if (!file) {
|
||||
printf("> Error loading module!\n");
|
||||
printf("> Error opening module!\n");
|
||||
return 1;
|
||||
}
|
||||
fseek(file, 0L, SEEK_END);
|
||||
@@ -33,7 +33,7 @@ int main(int argc, const char* argv[]) {
|
||||
wasm_byte_vec_t binary;
|
||||
wasm_byte_vec_new_uninitialized(&binary, file_size);
|
||||
if (fread(binary.data, file_size, 1, file) != 1) {
|
||||
printf("> Error loading module!\n");
|
||||
printf("> Error reading module!\n");
|
||||
return 1;
|
||||
}
|
||||
fclose(file);
|
||||
@@ -45,9 +45,27 @@ int main(int argc, const char* argv[]) {
|
||||
printf("> Error compiling module!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
wasm_byte_vec_delete(&binary);
|
||||
|
||||
// Figure out which export is the `fib` export
|
||||
wasm_exporttype_vec_t module_exports;
|
||||
wasm_module_exports(module, &module_exports);
|
||||
int fib_idx = -1;
|
||||
for (int i = 0; i < module_exports.size; i++) {
|
||||
const wasm_name_t *name = wasm_exporttype_name(module_exports.data[i]);
|
||||
if (name->size != 3)
|
||||
continue;
|
||||
if (strncmp("fib", name->data, 3) != 0)
|
||||
continue;
|
||||
fib_idx = i;
|
||||
break;
|
||||
}
|
||||
wasm_exporttype_vec_delete(&module_exports);
|
||||
if (fib_idx == -1) {
|
||||
printf("> Error finding `fib` export!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Instantiate.
|
||||
printf("Instantiating module...\n");
|
||||
own wasm_instance_t* instance =
|
||||
@@ -66,7 +84,7 @@ int main(int argc, const char* argv[]) {
|
||||
return 1;
|
||||
}
|
||||
// Getting second export (first is memory).
|
||||
const wasm_func_t* run_func = wasm_extern_as_func(exports.data[1]);
|
||||
const wasm_func_t* run_func = wasm_extern_as_func(exports.data[fib_idx]);
|
||||
if (run_func == NULL) {
|
||||
printf("> Error accessing export!\n");
|
||||
return 1;
|
||||
@@ -97,3 +115,4 @@ int main(int argc, const char* argv[]) {
|
||||
printf("Done.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
31
examples/fib-debug/main.rs
Normal file
31
examples/fib-debug/main.rs
Normal file
@@ -0,0 +1,31 @@
|
||||
//! Example of enabling debuginfo for wasm code which allows interactive
|
||||
//! debugging of the wasm code. When using recent versions of LLDB
|
||||
//! you can debug this executable and set breakpoints in wasm code and look at
|
||||
//! the rust source code as input.
|
||||
|
||||
// To execute this example you'll need to run two commands:
|
||||
//
|
||||
// cargo build -p example-fib-wasm --target wasm32-unknown-unknown
|
||||
// cargo run --example fib-debug
|
||||
|
||||
use anyhow::Result;
|
||||
use wasmtime::*;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// Load our previously compiled wasm file (built previously with Cargo) and
|
||||
// also ensure that we generate debuginfo so this executable can be
|
||||
// debugged in GDB.
|
||||
let engine = Engine::new(Config::new().debug_info(true));
|
||||
let store = Store::new(&engine);
|
||||
let module = Module::from_file(&store, "target/wasm32-unknown-unknown/debug/fib.wasm")?;
|
||||
let instance = Instance::new(&module, &[])?;
|
||||
|
||||
// Invoke `fib` export
|
||||
let fib = instance
|
||||
.get_export("fib")
|
||||
.and_then(|e| e.func())
|
||||
.ok_or(anyhow::format_err!("failed to find `fib` function export"))?
|
||||
.get1::<i32, i32>()?;
|
||||
println!("fib(6) = {}", fib(6)?);
|
||||
Ok(())
|
||||
}
|
||||
11
examples/fib-debug/wasm/Cargo.toml
Normal file
11
examples/fib-debug/wasm/Cargo.toml
Normal file
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "example-fib-debug-wasm"
|
||||
version = "0.1.0"
|
||||
authors = ["The Wasmtime Project Developers"]
|
||||
edition = "2018"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
path = "fib.rs"
|
||||
name = "fib"
|
||||
11
examples/fib-debug/wasm/fib.rs
Normal file
11
examples/fib-debug/wasm/fib.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
#[no_mangle]
|
||||
pub extern "C" fn fib(n: u32) -> u32 {
|
||||
let mut a = 1;
|
||||
let mut b = 1;
|
||||
for _ in 0..n {
|
||||
let t = a;
|
||||
a = b;
|
||||
b += t;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
118
examples/gcd.c
Normal file
118
examples/gcd.c
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
Example of instantiating of the WebAssembly module and invoking its exported
|
||||
function.
|
||||
|
||||
You can compile and run this example on Linux with:
|
||||
|
||||
cargo build --release -p wasmtime
|
||||
cc examples/gcd.c \
|
||||
-I crates/c-api/include \
|
||||
-I crates/c-api/wasm-c-api/include \
|
||||
target/release/libwasmtime.a \
|
||||
-lpthread -ldl -lm \
|
||||
-o gcd
|
||||
./gcd
|
||||
|
||||
Note that on Windows and macOS the command will be similar, but you'll need
|
||||
to tweak the `-lpthread` and such annotations.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <wasm.h>
|
||||
#include <wasmtime.h>
|
||||
|
||||
static void print_trap(wasm_trap_t *trap);
|
||||
|
||||
int main() {
|
||||
int ret = 0;
|
||||
// Set up our context
|
||||
wasm_engine_t *engine = wasm_engine_new();
|
||||
assert(engine != NULL);
|
||||
wasm_store_t *store = wasm_store_new(engine);
|
||||
assert(store != NULL);
|
||||
|
||||
// Load our input file to parse it next
|
||||
FILE* file = fopen("examples/gcd.wat", "r");
|
||||
if (!file) {
|
||||
printf("> Error loading file!\n");
|
||||
return 1;
|
||||
}
|
||||
fseek(file, 0L, SEEK_END);
|
||||
size_t file_size = ftell(file);
|
||||
fseek(file, 0L, SEEK_SET);
|
||||
wasm_byte_vec_t wat;
|
||||
wasm_byte_vec_new_uninitialized(&wat, file_size);
|
||||
if (fread(wat.data, file_size, 1, file) != 1) {
|
||||
printf("> Error loading module!\n");
|
||||
return 1;
|
||||
}
|
||||
fclose(file);
|
||||
|
||||
// Parse the wat into the binary wasm format
|
||||
wasm_byte_vec_t wasm, error;
|
||||
if (wasmtime_wat2wasm(engine, &wat, &wasm, &error) == 0) {
|
||||
fprintf(stderr, "failed to parse wat %.*s\n", (int) error.size, error.data);
|
||||
goto free_store;
|
||||
}
|
||||
wasm_byte_vec_delete(&wat);
|
||||
|
||||
// Compile and instantiate our module
|
||||
wasm_module_t *module = wasm_module_new(store, &wasm);
|
||||
wasm_byte_vec_delete(&wasm);
|
||||
assert(module != NULL);
|
||||
wasm_trap_t *trap = NULL;
|
||||
wasm_instance_t *instance = wasm_instance_new(store, module, NULL, &trap);
|
||||
if (instance == NULL) {
|
||||
print_trap(trap);
|
||||
goto free_module;
|
||||
}
|
||||
|
||||
// Lookup our `gcd` export function
|
||||
wasm_extern_vec_t externs;
|
||||
wasm_instance_exports(instance, &externs);
|
||||
assert(externs.size == 1);
|
||||
wasm_func_t *gcd = wasm_extern_as_func(externs.data[0]);
|
||||
assert(gcd != NULL);
|
||||
|
||||
// And call it!
|
||||
int a = 6;
|
||||
int b = 27;
|
||||
wasm_val_t params[2];
|
||||
wasm_val_t results[1];
|
||||
params[0].kind = WASM_I32;
|
||||
params[0].of.i32 = a;
|
||||
params[1].kind = WASM_I32;
|
||||
params[1].of.i32 = b;
|
||||
trap = wasm_func_call(gcd, params, results);
|
||||
if (trap != NULL) {
|
||||
print_trap(trap);
|
||||
goto free_instance;
|
||||
}
|
||||
assert(results[0].kind == WASM_I32);
|
||||
|
||||
printf("gcd(%d, %d) = %d\n", a, b, results[0].of.i32);
|
||||
|
||||
// Clean up after ourselves at this point
|
||||
ret = 0;
|
||||
|
||||
free_instance:
|
||||
wasm_extern_vec_delete(&externs);
|
||||
wasm_instance_delete(instance);
|
||||
free_module:
|
||||
wasm_module_delete(module);
|
||||
free_store:
|
||||
wasm_store_delete(store);
|
||||
wasm_engine_delete(engine);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void print_trap(wasm_trap_t *trap) {
|
||||
assert(trap != NULL);
|
||||
wasm_message_t message;
|
||||
wasm_trap_message(trap, &message);
|
||||
fprintf(stderr, "failed to instantiate module %.*s\n", (int) message.size, message.data);
|
||||
wasm_byte_vec_delete(&message);
|
||||
wasm_trap_delete(trap);
|
||||
}
|
||||
26
examples/gcd.rs
Normal file
26
examples/gcd.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
//! Example of instantiating of the WebAssembly module and invoking its exported
|
||||
//! function.
|
||||
|
||||
// You can execute this example with `cargo run --example gcd`
|
||||
|
||||
use anyhow::Result;
|
||||
use wasmtime::*;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// Load our WebAssembly (parsed WAT in our case), and then load it into a
|
||||
// `Module` which is attached to a `Store` cache. After we've got that we
|
||||
// can instantiate it.
|
||||
let store = Store::default();
|
||||
let module = Module::from_file(&store, "examples/gcd.wat")?;
|
||||
let instance = Instance::new(&module, &[])?;
|
||||
|
||||
// Invoke `gcd` export
|
||||
let gcd = instance
|
||||
.get_export("gcd")
|
||||
.and_then(|e| e.func())
|
||||
.ok_or(anyhow::format_err!("failed to find `gcd` function export"))?
|
||||
.get2::<i32, i32, i32>()?;
|
||||
|
||||
println!("gcd(6, 27) = {}", gcd(6, 27)?);
|
||||
Ok(())
|
||||
}
|
||||
28
examples/gcd.wat
Normal file
28
examples/gcd.wat
Normal file
@@ -0,0 +1,28 @@
|
||||
(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))
|
||||
)
|
||||
|
||||
133
examples/hello.c
Normal file
133
examples/hello.c
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
Example of instantiating of the WebAssembly module and invoking its exported
|
||||
function.
|
||||
|
||||
You can compile and run this example on Linux with:
|
||||
|
||||
cargo build --release -p wasmtime
|
||||
cc examples/hello.c \
|
||||
-I crates/c-api/include \
|
||||
-I crates/c-api/wasm-c-api/include \
|
||||
target/release/libwasmtime.a \
|
||||
-lpthread -ldl -lm \
|
||||
-o hello
|
||||
./hello
|
||||
|
||||
Note that on Windows and macOS the command will be similar, but you'll need
|
||||
to tweak the `-lpthread` and such annotations as well as the name of the
|
||||
`libwasmtime.a` file on Windows.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <wasm.h>
|
||||
#include <wasmtime.h>
|
||||
|
||||
static void print_trap(wasm_trap_t *trap);
|
||||
|
||||
static wasm_trap_t* hello_callback(const wasm_val_t args[], wasm_val_t results[]) {
|
||||
printf("Calling back...\n");
|
||||
printf("> Hello World!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int ret = 0;
|
||||
// Set up our compilation context. Note that we could also work with a
|
||||
// `wasm_config_t` here to configure what feature are enabled and various
|
||||
// compilation settings.
|
||||
printf("Initializing...\n");
|
||||
wasm_engine_t *engine = wasm_engine_new();
|
||||
assert(engine != NULL);
|
||||
|
||||
// With an engine we can create a *store* which is a long-lived group of wasm
|
||||
// modules.
|
||||
wasm_store_t *store = wasm_store_new(engine);
|
||||
assert(store != NULL);
|
||||
|
||||
// Read our input file, which in this case is a wasm text file.
|
||||
FILE* file = fopen("examples/hello.wat", "r");
|
||||
assert(file != NULL);
|
||||
fseek(file, 0L, SEEK_END);
|
||||
size_t file_size = ftell(file);
|
||||
fseek(file, 0L, SEEK_SET);
|
||||
wasm_byte_vec_t wat;
|
||||
wasm_byte_vec_new_uninitialized(&wat, file_size);
|
||||
assert(fread(wat.data, file_size, 1, file) == 1);
|
||||
fclose(file);
|
||||
|
||||
// Parse the wat into the binary wasm format
|
||||
wasm_byte_vec_t wasm, error;
|
||||
if (wasmtime_wat2wasm(engine, &wat, &wasm, &error) == 0) {
|
||||
fprintf(stderr, "failed to parse wat %.*s\n", (int) error.size, error.data);
|
||||
goto free_store;
|
||||
}
|
||||
wasm_byte_vec_delete(&wat);
|
||||
|
||||
// Now that we've got our binary webassembly we can compile our module.
|
||||
printf("Compiling module...\n");
|
||||
wasm_module_t *module = wasm_module_new(store, &wasm);
|
||||
wasm_byte_vec_delete(&wasm);
|
||||
assert(module != NULL);
|
||||
|
||||
// Next up we need to create the function that the wasm module imports. Here
|
||||
// we'll be hooking up a thunk function to the `hello_callback` native
|
||||
// function above.
|
||||
printf("Creating callback...\n");
|
||||
wasm_functype_t *hello_ty = wasm_functype_new_0_0();
|
||||
wasm_func_t *hello = wasm_func_new(store, hello_ty, hello_callback);
|
||||
|
||||
// With our callback function we can now instantiate the compiled module,
|
||||
// giving us an instance we can then execute exports from. Note that
|
||||
// instantiation can trap due to execution of the `start` function, so we need
|
||||
// to handle that here too.
|
||||
printf("Instantiating module...\n");
|
||||
wasm_trap_t *trap = NULL;
|
||||
const wasm_extern_t *imports[] = { wasm_func_as_extern(hello) };
|
||||
wasm_instance_t *instance = wasm_instance_new(store, module, imports, &trap);
|
||||
if (instance == NULL) {
|
||||
print_trap(trap);
|
||||
goto free_module;
|
||||
}
|
||||
|
||||
// Lookup our `run` export function
|
||||
printf("Extracting export...\n");
|
||||
wasm_extern_vec_t externs;
|
||||
wasm_instance_exports(instance, &externs);
|
||||
assert(externs.size == 1);
|
||||
wasm_func_t *run = wasm_extern_as_func(externs.data[0]);
|
||||
assert(run != NULL);
|
||||
|
||||
// And call it!
|
||||
printf("Calling export...\n");
|
||||
trap = wasm_func_call(run, NULL, NULL);
|
||||
if (trap != NULL) {
|
||||
print_trap(trap);
|
||||
goto free_instance;
|
||||
}
|
||||
|
||||
// Clean up after ourselves at this point
|
||||
printf("All finished!\n");
|
||||
ret = 0;
|
||||
|
||||
free_instance:
|
||||
wasm_extern_vec_delete(&externs);
|
||||
wasm_instance_delete(instance);
|
||||
free_module:
|
||||
wasm_module_delete(module);
|
||||
free_store:
|
||||
wasm_store_delete(store);
|
||||
wasm_engine_delete(engine);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void print_trap(wasm_trap_t *trap) {
|
||||
assert(trap != NULL);
|
||||
wasm_message_t message;
|
||||
wasm_trap_message(trap, &message);
|
||||
fprintf(stderr, "failed to instantiate module %.*s\n", (int) message.size, message.data);
|
||||
wasm_byte_vec_delete(&message);
|
||||
wasm_trap_delete(trap);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
//! Translation of hello example
|
||||
//! Small example of how to instantiate a wasm module that imports one function,
|
||||
//! showing how you can fill in host functionality for a wasm module.
|
||||
|
||||
use anyhow::{ensure, Context as _, Result};
|
||||
// You can execute this example with `cargo run --example hello`
|
||||
|
||||
use anyhow::Result;
|
||||
use wasmtime::*;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
@@ -12,13 +15,7 @@ fn main() -> Result<()> {
|
||||
|
||||
// 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!")?;
|
||||
let module = Module::from_file(&store, "examples/hello.wat")?;
|
||||
|
||||
// Here we handle the imports of the module, which in this case is our
|
||||
// `HelloCallback` type and its associated implementation of `Callback.
|
||||
@@ -32,18 +29,20 @@ fn main() -> Result<()> {
|
||||
// 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!")?;
|
||||
let imports = [hello_func.into()];
|
||||
let instance = Instance::new(&module, &imports)?;
|
||||
|
||||
// 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!")?;
|
||||
let run = instance
|
||||
.get_export("run")
|
||||
.and_then(|e| e.func())
|
||||
.ok_or(anyhow::format_err!("failed to find `run` function export"))?
|
||||
.get0::<()>()?;
|
||||
|
||||
// And last but not least we can call it!
|
||||
println!("Calling export...");
|
||||
run_func.call(&[])?;
|
||||
run()?;
|
||||
|
||||
println!("Done.");
|
||||
Ok(())
|
||||
4
examples/hello.wat
Normal file
4
examples/hello.wat
Normal file
@@ -0,0 +1,4 @@
|
||||
(module
|
||||
(func $hello (import "" "hello"))
|
||||
(func (export "run") (call $hello))
|
||||
)
|
||||
248
examples/memory.c
Normal file
248
examples/memory.c
Normal file
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
Example of instantiating of the WebAssembly module and invoking its exported
|
||||
function.
|
||||
|
||||
You can compile and run this example on Linux with:
|
||||
|
||||
cargo build --release -p wasmtime
|
||||
cc examples/memory.c \
|
||||
-I crates/c-api/include \
|
||||
-I crates/c-api/wasm-c-api/include \
|
||||
target/release/libwasmtime.a \
|
||||
-lpthread -ldl -lm \
|
||||
-o memory
|
||||
./memory
|
||||
|
||||
Note that on Windows and macOS the command will be similar, but you'll need
|
||||
to tweak the `-lpthread` and such annotations.
|
||||
|
||||
Also note that this example was taken from
|
||||
https://github.com/WebAssembly/wasm-c-api/blob/master/example/memory.c
|
||||
originally
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wasm.h>
|
||||
#include <wasmtime.h>
|
||||
|
||||
|
||||
wasm_memory_t* get_export_memory(const wasm_extern_vec_t* exports, size_t i) {
|
||||
if (exports->size <= i || !wasm_extern_as_memory(exports->data[i])) {
|
||||
printf("> Error accessing memory export %zu!\n", i);
|
||||
exit(1);
|
||||
}
|
||||
return wasm_extern_as_memory(exports->data[i]);
|
||||
}
|
||||
|
||||
wasm_func_t* get_export_func(const wasm_extern_vec_t* exports, size_t i) {
|
||||
if (exports->size <= i || !wasm_extern_as_func(exports->data[i])) {
|
||||
printf("> Error accessing function export %zu!\n", i);
|
||||
exit(1);
|
||||
}
|
||||
return wasm_extern_as_func(exports->data[i]);
|
||||
}
|
||||
|
||||
|
||||
void check(bool success) {
|
||||
if (!success) {
|
||||
printf("> Error, expected success\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void check_call(wasm_func_t* func, wasm_val_t args[], int32_t expected) {
|
||||
wasm_val_t results[1];
|
||||
if (wasm_func_call(func, args, results) || results[0].of.i32 != expected) {
|
||||
printf("> Error on result\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void check_call0(wasm_func_t* func, int32_t expected) {
|
||||
check_call(func, NULL, expected);
|
||||
}
|
||||
|
||||
void check_call1(wasm_func_t* func, int32_t arg, int32_t expected) {
|
||||
wasm_val_t args[] = { {.kind = WASM_I32, .of = {.i32 = arg}} };
|
||||
check_call(func, args, expected);
|
||||
}
|
||||
|
||||
void check_call2(wasm_func_t* func, int32_t arg1, int32_t arg2, int32_t expected) {
|
||||
wasm_val_t args[2] = {
|
||||
{.kind = WASM_I32, .of = {.i32 = arg1}},
|
||||
{.kind = WASM_I32, .of = {.i32 = arg2}}
|
||||
};
|
||||
check_call(func, args, expected);
|
||||
}
|
||||
|
||||
void check_ok(wasm_func_t* func, wasm_val_t args[]) {
|
||||
if (wasm_func_call(func, args, NULL)) {
|
||||
printf("> Error on result, expected empty\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void check_ok2(wasm_func_t* func, int32_t arg1, int32_t arg2) {
|
||||
wasm_val_t args[2] = {
|
||||
{.kind = WASM_I32, .of = {.i32 = arg1}},
|
||||
{.kind = WASM_I32, .of = {.i32 = arg2}}
|
||||
};
|
||||
check_ok(func, args);
|
||||
}
|
||||
|
||||
void check_trap(wasm_func_t* func, wasm_val_t args[]) {
|
||||
wasm_val_t results[1];
|
||||
wasm_trap_t* trap = wasm_func_call(func, args, results);
|
||||
if (! trap) {
|
||||
printf("> Error on result, expected trap\n");
|
||||
exit(1);
|
||||
}
|
||||
wasm_trap_delete(trap);
|
||||
}
|
||||
|
||||
void check_trap1(wasm_func_t* func, int32_t arg) {
|
||||
wasm_val_t args[1] = { {.kind = WASM_I32, .of = {.i32 = arg}} };
|
||||
check_trap(func, args);
|
||||
}
|
||||
|
||||
void check_trap2(wasm_func_t* func, int32_t arg1, int32_t arg2) {
|
||||
wasm_val_t args[2] = {
|
||||
{.kind = WASM_I32, .of = {.i32 = arg1}},
|
||||
{.kind = WASM_I32, .of = {.i32 = arg2}}
|
||||
};
|
||||
check_trap(func, args);
|
||||
}
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
// Initialize.
|
||||
printf("Initializing...\n");
|
||||
wasm_engine_t* engine = wasm_engine_new();
|
||||
wasm_store_t* store = wasm_store_new(engine);
|
||||
|
||||
// Load our input file to parse it next
|
||||
FILE* file = fopen("examples/memory.wat", "r");
|
||||
if (!file) {
|
||||
printf("> Error loading file!\n");
|
||||
return 1;
|
||||
}
|
||||
fseek(file, 0L, SEEK_END);
|
||||
size_t file_size = ftell(file);
|
||||
fseek(file, 0L, SEEK_SET);
|
||||
wasm_byte_vec_t wat;
|
||||
wasm_byte_vec_new_uninitialized(&wat, file_size);
|
||||
if (fread(wat.data, file_size, 1, file) != 1) {
|
||||
printf("> Error loading module!\n");
|
||||
return 1;
|
||||
}
|
||||
fclose(file);
|
||||
|
||||
// Parse the wat into the binary wasm format
|
||||
wasm_byte_vec_t binary, error;
|
||||
if (wasmtime_wat2wasm(engine, &wat, &binary, &error) == 0) {
|
||||
fprintf(stderr, "failed to parse wat %.*s\n", (int) error.size, error.data);
|
||||
return 1;
|
||||
}
|
||||
wasm_byte_vec_delete(&wat);
|
||||
|
||||
// Compile.
|
||||
printf("Compiling module...\n");
|
||||
wasm_module_t* module = wasm_module_new(store, &binary);
|
||||
if (!module) {
|
||||
printf("> Error compiling module!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
wasm_byte_vec_delete(&binary);
|
||||
|
||||
// Instantiate.
|
||||
printf("Instantiating module...\n");
|
||||
wasm_instance_t* instance = wasm_instance_new(store, module, NULL, NULL);
|
||||
if (!instance) {
|
||||
printf("> Error instantiating module!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Extract export.
|
||||
printf("Extracting exports...\n");
|
||||
wasm_extern_vec_t exports;
|
||||
wasm_instance_exports(instance, &exports);
|
||||
size_t i = 0;
|
||||
wasm_memory_t* memory = get_export_memory(&exports, i++);
|
||||
wasm_func_t* size_func = get_export_func(&exports, i++);
|
||||
wasm_func_t* load_func = get_export_func(&exports, i++);
|
||||
wasm_func_t* store_func = get_export_func(&exports, i++);
|
||||
|
||||
wasm_module_delete(module);
|
||||
|
||||
// Try cloning.
|
||||
wasm_memory_t* copy = wasm_memory_copy(memory);
|
||||
assert(wasm_memory_same(memory, copy));
|
||||
wasm_memory_delete(copy);
|
||||
|
||||
// Check initial memory.
|
||||
printf("Checking memory...\n");
|
||||
check(wasm_memory_size(memory) == 2);
|
||||
check(wasm_memory_data_size(memory) == 0x20000);
|
||||
check(wasm_memory_data(memory)[0] == 0);
|
||||
check(wasm_memory_data(memory)[0x1000] == 1);
|
||||
check(wasm_memory_data(memory)[0x1003] == 4);
|
||||
|
||||
check_call0(size_func, 2);
|
||||
check_call1(load_func, 0, 0);
|
||||
check_call1(load_func, 0x1000, 1);
|
||||
check_call1(load_func, 0x1003, 4);
|
||||
check_call1(load_func, 0x1ffff, 0);
|
||||
check_trap1(load_func, 0x20000);
|
||||
|
||||
// Mutate memory.
|
||||
printf("Mutating memory...\n");
|
||||
wasm_memory_data(memory)[0x1003] = 5;
|
||||
check_ok2(store_func, 0x1002, 6);
|
||||
check_trap2(store_func, 0x20000, 0);
|
||||
|
||||
check(wasm_memory_data(memory)[0x1002] == 6);
|
||||
check(wasm_memory_data(memory)[0x1003] == 5);
|
||||
check_call1(load_func, 0x1002, 6);
|
||||
check_call1(load_func, 0x1003, 5);
|
||||
|
||||
// Grow memory.
|
||||
printf("Growing memory...\n");
|
||||
check(wasm_memory_grow(memory, 1));
|
||||
check(wasm_memory_size(memory) == 3);
|
||||
check(wasm_memory_data_size(memory) == 0x30000);
|
||||
|
||||
check_call1(load_func, 0x20000, 0);
|
||||
check_ok2(store_func, 0x20000, 0);
|
||||
check_trap1(load_func, 0x30000);
|
||||
check_trap2(store_func, 0x30000, 0);
|
||||
|
||||
check(! wasm_memory_grow(memory, 1));
|
||||
check(wasm_memory_grow(memory, 0));
|
||||
|
||||
wasm_extern_vec_delete(&exports);
|
||||
wasm_instance_delete(instance);
|
||||
|
||||
// Create stand-alone memory.
|
||||
printf("Creating stand-alone memory...\n");
|
||||
wasm_limits_t limits = {5, 5};
|
||||
wasm_memorytype_t* memorytype = wasm_memorytype_new(&limits);
|
||||
wasm_memory_t* memory2 = wasm_memory_new(store, memorytype);
|
||||
check(wasm_memory_size(memory2) == 5);
|
||||
check(! wasm_memory_grow(memory2, 1));
|
||||
check(wasm_memory_grow(memory2, 0));
|
||||
|
||||
wasm_memorytype_delete(memorytype);
|
||||
wasm_memory_delete(memory2);
|
||||
|
||||
// Shut down.
|
||||
printf("Shutting down...\n");
|
||||
wasm_store_delete(store);
|
||||
wasm_engine_delete(engine);
|
||||
|
||||
// All done.
|
||||
printf("Done.\n");
|
||||
return 0;
|
||||
}
|
||||
97
examples/memory.rs
Normal file
97
examples/memory.rs
Normal file
@@ -0,0 +1,97 @@
|
||||
//! An example of how to interact with wasm memory.
|
||||
//!
|
||||
//! Here a small wasm module is used to show how memory is initialized, how to
|
||||
//! read and write memory through the `Memory` object, and how wasm functions
|
||||
//! can trap when dealing with out-of-bounds addresses.
|
||||
|
||||
// You can execute this example with `cargo run --example example`
|
||||
|
||||
use anyhow::Result;
|
||||
use wasmtime::*;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// Create our `Store` context and then compile a module and create an
|
||||
// instance from the compiled module all in one go.
|
||||
let wasmtime_store = Store::default();
|
||||
let module = Module::from_file(&wasmtime_store, "examples/memory.wat")?;
|
||||
let instance = Instance::new(&module, &[])?;
|
||||
|
||||
// Load up our exports from the instance
|
||||
let memory = instance
|
||||
.get_export("memory")
|
||||
.and_then(|e| e.memory())
|
||||
.ok_or(anyhow::format_err!("failed to find `memory` export"))?;
|
||||
let size = instance
|
||||
.get_export("size")
|
||||
.and_then(|e| e.func())
|
||||
.ok_or(anyhow::format_err!("failed to find `size` export"))?
|
||||
.get0::<i32>()?;
|
||||
let load = instance
|
||||
.get_export("load")
|
||||
.and_then(|e| e.func())
|
||||
.ok_or(anyhow::format_err!("failed to find `load` export"))?
|
||||
.get1::<i32, i32>()?;
|
||||
let store = instance
|
||||
.get_export("store")
|
||||
.and_then(|e| e.func())
|
||||
.ok_or(anyhow::format_err!("failed to find `store` export"))?
|
||||
.get2::<i32, i32, ()>()?;
|
||||
|
||||
// Note that these memory reads are *unsafe* due to unknown knowledge about
|
||||
// aliasing with wasm memory. For more information about the safety
|
||||
// guarantees here and how to use `Memory` safely, see the API
|
||||
// documentation.
|
||||
println!("Checking memory...");
|
||||
assert_eq!(memory.size(), 2);
|
||||
assert_eq!(memory.data_size(), 0x20000);
|
||||
unsafe {
|
||||
assert_eq!(memory.data_unchecked_mut()[0], 0);
|
||||
assert_eq!(memory.data_unchecked_mut()[0x1000], 1);
|
||||
assert_eq!(memory.data_unchecked_mut()[0x1003], 4);
|
||||
}
|
||||
|
||||
assert_eq!(size()?, 2);
|
||||
assert_eq!(load(0)?, 0);
|
||||
assert_eq!(load(0x1000)?, 1);
|
||||
assert_eq!(load(0x1003)?, 4);
|
||||
assert_eq!(load(0x1ffff)?, 0);
|
||||
assert!(load(0x20000).is_err()); // out of bounds trap
|
||||
|
||||
println!("Mutating memory...");
|
||||
unsafe {
|
||||
memory.data_unchecked_mut()[0x1003] = 5;
|
||||
}
|
||||
|
||||
store(0x1002, 6)?;
|
||||
assert!(store(0x20000, 0).is_err()); // out of bounds trap
|
||||
|
||||
unsafe {
|
||||
assert_eq!(memory.data_unchecked_mut()[0x1002], 6);
|
||||
assert_eq!(memory.data_unchecked_mut()[0x1003], 5);
|
||||
}
|
||||
assert_eq!(load(0x1002)?, 6);
|
||||
assert_eq!(load(0x1003)?, 5);
|
||||
|
||||
// Grow memory.
|
||||
println!("Growing memory...");
|
||||
memory.grow(1)?;
|
||||
assert_eq!(memory.size(), 3);
|
||||
assert_eq!(memory.data_size(), 0x30000);
|
||||
|
||||
assert_eq!(load(0x20000)?, 0);
|
||||
store(0x20000, 0)?;
|
||||
assert!(load(0x30000).is_err());
|
||||
assert!(store(0x30000, 0).is_err());
|
||||
|
||||
assert!(memory.grow(1).is_err());
|
||||
assert!(memory.grow(0).is_ok());
|
||||
|
||||
println!("Creating stand-alone memory...");
|
||||
let memorytype = MemoryType::new(Limits::new(5, Some(5)));
|
||||
let memory2 = Memory::new(&wasmtime_store, memorytype);
|
||||
assert_eq!(memory2.size(), 5);
|
||||
assert!(memory2.grow(1).is_err());
|
||||
assert!(memory2.grow(0).is_ok());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
13
examples/memory.wat
Normal file
13
examples/memory.wat
Normal file
@@ -0,0 +1,13 @@
|
||||
(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")
|
||||
)
|
||||
179
examples/multi.c
Normal file
179
examples/multi.c
Normal file
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
Example of instantiating of the WebAssembly module and invoking its exported
|
||||
function.
|
||||
|
||||
You can compile and run this example on Linux with:
|
||||
|
||||
cargo build --release -p wasmtime
|
||||
cc examples/multi.c \
|
||||
-I crates/c-api/include \
|
||||
-I crates/c-api/wasm-c-api/include \
|
||||
target/release/libwasmtime.a \
|
||||
-lpthread -ldl -lm \
|
||||
-o multi
|
||||
./multi
|
||||
|
||||
Note that on Windows and macOS the command will be similar, but you'll need
|
||||
to tweak the `-lpthread` and such annotations.
|
||||
|
||||
Also note that this example was taken from
|
||||
https://github.com/WebAssembly/wasm-c-api/blob/master/example/multi.c
|
||||
originally
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <wasm.h>
|
||||
#include <wasmtime.h>
|
||||
|
||||
// A function to be called from Wasm code.
|
||||
wasm_trap_t* callback(
|
||||
const wasm_val_t args[], wasm_val_t results[]
|
||||
) {
|
||||
printf("Calling back...\n");
|
||||
printf("> %"PRIu32" %"PRIu64"\n", args[0].of.i32, args[1].of.i64);
|
||||
printf("\n");
|
||||
|
||||
wasm_val_copy(&results[0], &args[1]);
|
||||
wasm_val_copy(&results[1], &args[0]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// A function closure.
|
||||
wasm_trap_t* closure_callback(
|
||||
void* env, const wasm_val_t args[], wasm_val_t results[]
|
||||
) {
|
||||
int i = *(int*)env;
|
||||
printf("Calling back closure...\n");
|
||||
printf("> %d\n", i);
|
||||
|
||||
results[0].kind = WASM_I32;
|
||||
results[0].of.i32 = (int32_t)i;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
// Initialize.
|
||||
printf("Initializing...\n");
|
||||
wasm_config_t *config = wasm_config_new();
|
||||
assert(config != NULL);
|
||||
wasmtime_config_wasm_multi_value_set(config, true);
|
||||
wasm_engine_t* engine = wasm_engine_new_with_config(config);
|
||||
wasm_store_t* store = wasm_store_new(engine);
|
||||
|
||||
// Load our input file to parse it next
|
||||
FILE* file = fopen("examples/multi.wat", "r");
|
||||
if (!file) {
|
||||
printf("> Error loading file!\n");
|
||||
return 1;
|
||||
}
|
||||
fseek(file, 0L, SEEK_END);
|
||||
size_t file_size = ftell(file);
|
||||
fseek(file, 0L, SEEK_SET);
|
||||
wasm_byte_vec_t wat;
|
||||
wasm_byte_vec_new_uninitialized(&wat, file_size);
|
||||
if (fread(wat.data, file_size, 1, file) != 1) {
|
||||
printf("> Error loading module!\n");
|
||||
return 1;
|
||||
}
|
||||
fclose(file);
|
||||
|
||||
// Parse the wat into the binary wasm format
|
||||
wasm_byte_vec_t binary, error;
|
||||
if (wasmtime_wat2wasm(engine, &wat, &binary, &error) == 0) {
|
||||
fprintf(stderr, "failed to parse wat %.*s\n", (int) error.size, error.data);
|
||||
return 1;
|
||||
}
|
||||
wasm_byte_vec_delete(&wat);
|
||||
|
||||
// Compile.
|
||||
printf("Compiling module...\n");
|
||||
wasm_module_t* module = wasm_module_new(store, &binary);
|
||||
if (!module) {
|
||||
printf("> Error compiling module!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
wasm_byte_vec_delete(&binary);
|
||||
|
||||
// Create external print functions.
|
||||
printf("Creating callback...\n");
|
||||
wasm_functype_t* callback_type = wasm_functype_new_2_2(
|
||||
wasm_valtype_new_i32(),
|
||||
wasm_valtype_new_i64(),
|
||||
wasm_valtype_new_i64(),
|
||||
wasm_valtype_new_i32()
|
||||
);
|
||||
wasm_func_t* callback_func =
|
||||
wasm_func_new(store, callback_type, callback);
|
||||
|
||||
wasm_functype_delete(callback_type);
|
||||
|
||||
// Instantiate.
|
||||
printf("Instantiating module...\n");
|
||||
const wasm_extern_t* imports[] = {wasm_func_as_extern(callback_func)};
|
||||
wasm_instance_t* instance =
|
||||
wasm_instance_new(store, module, imports, NULL);
|
||||
if (!instance) {
|
||||
printf("> Error instantiating module!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
wasm_func_delete(callback_func);
|
||||
|
||||
// Extract export.
|
||||
printf("Extracting export...\n");
|
||||
wasm_extern_vec_t exports;
|
||||
wasm_instance_exports(instance, &exports);
|
||||
if (exports.size == 0) {
|
||||
printf("> Error accessing exports!\n");
|
||||
return 1;
|
||||
}
|
||||
const wasm_func_t* run_func = wasm_extern_as_func(exports.data[0]);
|
||||
if (run_func == NULL) {
|
||||
printf("> Error accessing export!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
wasm_module_delete(module);
|
||||
wasm_instance_delete(instance);
|
||||
|
||||
// Call.
|
||||
printf("Calling export...\n");
|
||||
wasm_val_t args[4];
|
||||
args[0].kind = WASM_I32;
|
||||
args[0].of.i32 = 1;
|
||||
args[1].kind = WASM_I64;
|
||||
args[1].of.i64 = 2;
|
||||
wasm_val_t results[2];
|
||||
if (wasm_func_call(run_func, args, results)) {
|
||||
printf("> Error calling function!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
wasm_extern_vec_delete(&exports);
|
||||
|
||||
// Print result.
|
||||
printf("Printing result...\n");
|
||||
printf("> %"PRIu64" %"PRIu32"\n",
|
||||
results[0].of.i64, results[1].of.i32);
|
||||
|
||||
assert(results[0].kind == WASM_I64);
|
||||
assert(results[0].of.i64 == 2);
|
||||
assert(results[1].kind == WASM_I32);
|
||||
assert(results[1].of.i32 == 1);
|
||||
|
||||
// Shut down.
|
||||
printf("Shutting down...\n");
|
||||
wasm_store_delete(store);
|
||||
wasm_engine_delete(engine);
|
||||
|
||||
// All done.
|
||||
printf("Done.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
//! Translation of multi example
|
||||
//! This is an example of working with mulit-value modules and dealing with
|
||||
//! multi-value functions.
|
||||
//!
|
||||
//! Note that the `Func::wrap*` interfaces cannot be used to return multiple
|
||||
//! values just yet, so we need to use the more dynamic `Func::new` and
|
||||
//! `Func::call` methods.
|
||||
|
||||
use anyhow::{ensure, format_err, Context as _, Result};
|
||||
// You can execute this example with `cargo run --example multi`
|
||||
|
||||
use anyhow::{format_err, Result};
|
||||
use std::rc::Rc;
|
||||
use wasmtime::*;
|
||||
|
||||
@@ -17,40 +24,16 @@ impl Callable for Callback {
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
// Configure our `Store`, but be sure to use a `Config` that enables the
|
||||
// wasm multi-value feature since it's not stable yet.
|
||||
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!")?;
|
||||
let module = Module::from_file(&store, "examples/multi.wat")?;
|
||||
|
||||
// Create external print functions.
|
||||
println!("Creating callback...");
|
||||
@@ -62,34 +45,31 @@ fn main() -> Result<()> {
|
||||
|
||||
// Instantiate.
|
||||
println!("Instantiating module...");
|
||||
let imports = vec![callback_func.into()];
|
||||
let instance =
|
||||
Instance::new(&module, imports.as_slice()).context("Error instantiating module!")?;
|
||||
let instance = Instance::new(&module, &[callback_func.into()])?;
|
||||
|
||||
// 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")?;
|
||||
let g = instance
|
||||
.get_export("g")
|
||||
.and_then(|e| e.func())
|
||||
.ok_or(format_err!("failed to find export `g`"))?;
|
||||
|
||||
// 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))?;
|
||||
let results = g.call(&[Val::I32(1), Val::I64(3)])?;
|
||||
|
||||
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);
|
||||
assert_eq!(results[0].unwrap_i64(), 4);
|
||||
assert_eq!(results[1].unwrap_i32(), 2);
|
||||
|
||||
// Call `$round_trip_many`.
|
||||
println!("Calling export \"round_trip_many\"...");
|
||||
let round_trip_many = instance
|
||||
.get_export("round_trip_many")
|
||||
.and_then(|e| e.func())
|
||||
.ok_or(format_err!("failed to find export `round_trip_many`"))?;
|
||||
let args = vec![
|
||||
Val::I64(0),
|
||||
Val::I64(1),
|
||||
@@ -102,9 +82,7 @@ fn main() -> Result<()> {
|
||||
Val::I64(8),
|
||||
Val::I64(9),
|
||||
];
|
||||
let results = round_trip_many
|
||||
.call(&args)
|
||||
.map_err(|e| format_err!("> Error calling round_trip_many! {:?}", e))?;
|
||||
let results = round_trip_many.call(&args)?;
|
||||
|
||||
println!("Printing result...");
|
||||
print!(">");
|
||||
@@ -113,17 +91,11 @@ fn main() -> Result<()> {
|
||||
}
|
||||
println!();
|
||||
|
||||
debug_assert_eq!(results.len(), 10);
|
||||
debug_assert!(args
|
||||
assert_eq!(results.len(), 10);
|
||||
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(())
|
||||
}
|
||||
23
examples/multi.wat
Normal file
23
examples/multi.wat
Normal file
@@ -0,0 +1,23 @@
|
||||
(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)
|
||||
)
|
||||
Reference in New Issue
Block a user