diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fc5bd1c468..7b9131355b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -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" diff --git a/.gitmodules b/.gitmodules index 59646f986c..6adfc85afe 100644 --- a/.gitmodules +++ b/.gitmodules @@ -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 diff --git a/Cargo.lock b/Cargo.lock index 368b48c393..cd71d45b25 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index 36cd893c8a..41f491ad3b 100644 --- a/Cargo.toml +++ b/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] diff --git a/ci/build-tarballs.sh b/ci/build-tarballs.sh index 44cdd0cd11..3aedcfa10b 100755 --- a/ci/build-tarballs.sh +++ b/ci/build-tarballs.sh @@ -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 diff --git a/crates/api/examples/gcd.rs b/crates/api/examples/gcd.rs deleted file mode 100644 index 5f68b87417..0000000000 --- a/crates/api/examples/gcd.rs +++ /dev/null @@ -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(()) -} diff --git a/crates/api/examples/memory.rs b/crates/api/examples/memory.rs deleted file mode 100644 index dd2908c005..0000000000 --- a/crates/api/examples/memory.rs +++ /dev/null @@ -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 { - 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 { - 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(()) -} diff --git a/crates/api/tests/examples.rs b/crates/api/tests/examples.rs deleted file mode 100644 index 24de77f3ed..0000000000 --- a/crates/api/tests/examples.rs +++ /dev/null @@ -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"); -} diff --git a/crates/c-api/examples/Makefile b/crates/c-api/examples/Makefile deleted file mode 100644 index b309e80484..0000000000 --- a/crates/c-api/examples/Makefile +++ /dev/null @@ -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 $@ diff --git a/crates/c-api/examples/fib-wasm.c b/crates/c-api/examples/fib-wasm.c deleted file mode 100644 index 20c06f5efa..0000000000 --- a/crates/c-api/examples/fib-wasm.c +++ /dev/null @@ -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; -} diff --git a/crates/c-api/examples/fib-wasm.wasm b/crates/c-api/examples/fib-wasm.wasm deleted file mode 100755 index 0a1ebac429..0000000000 Binary files a/crates/c-api/examples/fib-wasm.wasm and /dev/null differ diff --git a/crates/c-api/include/wasmtime.h b/crates/c-api/include/wasmtime.h index f5f2d4df54..5334d49bd3 100644 --- a/crates/c-api/include/wasmtime.h +++ b/crates/c-api/include/wasmtime.h @@ -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 diff --git a/crates/c-api/examples/wasm-c-api b/crates/c-api/wasm-c-api similarity index 100% rename from crates/c-api/examples/wasm-c-api rename to crates/c-api/wasm-c-api diff --git a/crates/misc/run-examples/Cargo.toml b/crates/misc/run-examples/Cargo.toml new file mode 100644 index 0000000000..bc9ffa3ac7 --- /dev/null +++ b/crates/misc/run-examples/Cargo.toml @@ -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" diff --git a/crates/misc/run-examples/build.rs b/crates/misc/run-examples/build.rs new file mode 100644 index 0000000000..81caa36d69 --- /dev/null +++ b/crates/misc/run-examples/build.rs @@ -0,0 +1,6 @@ +fn main() { + println!( + "cargo:rustc-env=TARGET={}", + std::env::var("TARGET").unwrap() + ); +} diff --git a/crates/misc/run-examples/src/main.rs b/crates/misc/run-examples/src/main.rs new file mode 100644 index 0000000000..c01a58a994 --- /dev/null +++ b/crates/misc/run-examples/src/main.rs @@ -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::>(); + + 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); + } +} diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000000..b888c322ac --- /dev/null +++ b/examples/README.md @@ -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! diff --git a/crates/c-api/examples/config-debug.c b/examples/fib-debug/main.c similarity index 76% rename from crates/c-api/examples/config-debug.c rename to examples/fib-debug/main.c index 650d9645c9..95960ab44c 100644 --- a/crates/c-api/examples/config-debug.c +++ b/examples/fib-debug/main.c @@ -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; } + diff --git a/examples/fib-debug/main.rs b/examples/fib-debug/main.rs new file mode 100644 index 0000000000..79a5841c4a --- /dev/null +++ b/examples/fib-debug/main.rs @@ -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::()?; + println!("fib(6) = {}", fib(6)?); + Ok(()) +} diff --git a/examples/fib-debug/wasm/Cargo.toml b/examples/fib-debug/wasm/Cargo.toml new file mode 100644 index 0000000000..bb712bed08 --- /dev/null +++ b/examples/fib-debug/wasm/Cargo.toml @@ -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" diff --git a/examples/fib-debug/wasm/fib.rs b/examples/fib-debug/wasm/fib.rs new file mode 100644 index 0000000000..ef59bab8e8 --- /dev/null +++ b/examples/fib-debug/wasm/fib.rs @@ -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; +} diff --git a/examples/gcd.c b/examples/gcd.c new file mode 100644 index 0000000000..3338f9f182 --- /dev/null +++ b/examples/gcd.c @@ -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 +#include +#include +#include +#include + +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); +} diff --git a/examples/gcd.rs b/examples/gcd.rs new file mode 100644 index 0000000000..f310672f3e --- /dev/null +++ b/examples/gcd.rs @@ -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::()?; + + println!("gcd(6, 27) = {}", gcd(6, 27)?); + Ok(()) +} diff --git a/examples/gcd.wat b/examples/gcd.wat new file mode 100644 index 0000000000..3c31742912 --- /dev/null +++ b/examples/gcd.wat @@ -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)) +) + diff --git a/examples/hello.c b/examples/hello.c new file mode 100644 index 0000000000..b8ba6894c0 --- /dev/null +++ b/examples/hello.c @@ -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 +#include +#include +#include +#include + +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); +} + diff --git a/crates/api/examples/hello.rs b/examples/hello.rs similarity index 66% rename from crates/api/examples/hello.rs rename to examples/hello.rs index 9a461569ba..8d3ddb04f6 100644 --- a/crates/api/examples/hello.rs +++ b/examples/hello.rs @@ -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(()) diff --git a/examples/hello.wat b/examples/hello.wat new file mode 100644 index 0000000000..1c56c55822 --- /dev/null +++ b/examples/hello.wat @@ -0,0 +1,4 @@ +(module + (func $hello (import "" "hello")) + (func (export "run") (call $hello)) +) diff --git a/examples/memory.c b/examples/memory.c new file mode 100644 index 0000000000..11361e0b69 --- /dev/null +++ b/examples/memory.c @@ -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 +#include +#include +#include +#include +#include + + +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; +} diff --git a/examples/memory.rs b/examples/memory.rs new file mode 100644 index 0000000000..b86ecc34bc --- /dev/null +++ b/examples/memory.rs @@ -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::()?; + let load = instance + .get_export("load") + .and_then(|e| e.func()) + .ok_or(anyhow::format_err!("failed to find `load` export"))? + .get1::()?; + let store = instance + .get_export("store") + .and_then(|e| e.func()) + .ok_or(anyhow::format_err!("failed to find `store` export"))? + .get2::()?; + + // 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(()) +} diff --git a/examples/memory.wat b/examples/memory.wat new file mode 100644 index 0000000000..3408eb0dc6 --- /dev/null +++ b/examples/memory.wat @@ -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") +) diff --git a/examples/multi.c b/examples/multi.c new file mode 100644 index 0000000000..d3d2605d52 --- /dev/null +++ b/examples/multi.c @@ -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 +#include +#include +#include +#include +#include + +// 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; +} + diff --git a/crates/api/examples/multi.rs b/examples/multi.rs similarity index 50% rename from crates/api/examples/multi.rs rename to examples/multi.rs index 663281eb09..8657447788 100644 --- a/crates/api/examples/multi.rs +++ b/examples/multi.rs @@ -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(()) } diff --git a/examples/multi.wat b/examples/multi.wat new file mode 100644 index 0000000000..16aa1cdec6 --- /dev/null +++ b/examples/multi.wat @@ -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) +)