Implement RFC 11: Redesigning Wasmtime's APIs (#2897)

Implement Wasmtime's new API as designed by RFC 11. This is quite a large commit which has had lots of discussion externally, so for more information it's best to read the RFC thread and the PR thread.
This commit is contained in:
Alex Crichton
2021-06-03 09:10:53 -05:00
committed by GitHub
parent a5a28b1c5b
commit 7a1b7cdf92
233 changed files with 13349 additions and 11997 deletions

View File

@@ -42,8 +42,9 @@ int main() {
// 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);
wasmtime_store_t *store = wasmtime_store_new(engine, NULL, NULL);
assert(store != NULL);
wasmtime_context_t *context = wasmtime_store_context(store);
// Read our input file, which in this case is a wasm text file.
FILE* file = fopen("examples/externref.wat", "r");
@@ -58,15 +59,15 @@ int main() {
// Parse the wat into the binary wasm format
wasm_byte_vec_t wasm;
wasmtime_error_t *error = wasmtime_wat2wasm(&wat, &wasm);
wasmtime_error_t *error = wasmtime_wat2wasm(wat.data, wat.size, &wasm);
if (error != NULL)
exit_with_error("failed to parse wat", error, NULL);
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 = NULL;
error = wasmtime_module_new(engine, &wasm, &module);
wasmtime_module_t *module = NULL;
error = wasmtime_module_new(engine, (uint8_t*) wasm.data, wasm.size, &module);
wasm_byte_vec_delete(&wasm);
if (error != NULL)
exit_with_error("failed to compile module", error, NULL);
@@ -74,104 +75,99 @@ int main() {
// Instantiate the module.
printf("Instantiating module...\n");
wasm_trap_t *trap = NULL;
wasm_instance_t *instance = NULL;
wasm_extern_vec_t imports = WASM_EMPTY_VEC;
error = wasmtime_instance_new(store, module, &imports, &instance, &trap);
if (instance == NULL)
wasmtime_instance_t instance;
error = wasmtime_instance_new(context, module, NULL, 0, &instance, &trap);
if (error != NULL || trap != NULL)
exit_with_error("failed to instantiate", error, trap);
printf("Creating new `externref`...\n");
// Create a new `externref` value.
//
// Note that if you need clean up for after the externref is reclaimed, you
// can use `wasmtime_externref_new_with_finalizer`.
wasm_val_t externref;
wasmtime_externref_new("Hello, World!", &externref);
assert(externref.kind == WASM_ANYREF);
// Note that the NULL here is a finalizer callback, but we don't need one for
// this example.
wasmtime_externref_t *externref = wasmtime_externref_new("Hello, World!", NULL);
// The `externref`'s wrapped data should be the string "Hello, World!".
void* data = NULL;
ok = wasmtime_externref_data(&externref, &data);
assert(ok);
void* data = wasmtime_externref_data(externref);
assert(strcmp((char*)data, "Hello, World!") == 0);
printf("Touching `externref` table...\n");
wasmtime_extern_t item;
// Lookup the `table` export.
wasm_extern_vec_t externs;
wasm_instance_exports(instance, &externs);
assert(externs.size == 3);
wasm_table_t *table = wasm_extern_as_table(externs.data[0]);
assert(table != NULL);
ok = wasmtime_instance_export_get(context, &instance, "table", strlen("table"), &item);
assert(ok);
assert(item.kind == WASMTIME_EXTERN_TABLE);
// Set `table[3]` to our `externref`.
wasm_val_t elem;
wasm_val_copy(&elem, &externref);
assert(elem.kind == WASM_ANYREF);
ok = wasm_table_set(table, 3, elem.of.ref);
assert(ok);
wasmtime_val_t externref_val;
externref_val.kind = WASMTIME_EXTERNREF;
externref_val.of.externref = externref;
error = wasmtime_table_set(context, &item.of.table, 3, &externref_val);
if (error != NULL)
exit_with_error("failed to set table", error, NULL);
// `table[3]` should now be our `externref`.
wasm_ref_delete(elem.of.ref);
elem.of.ref = wasm_table_get(table, 3);
assert(elem.of.ref != NULL);
assert(wasm_ref_same(elem.of.ref, externref.of.ref));
wasmtime_val_t elem;
ok = wasmtime_table_get(context, &item.of.table, 3, &elem);
assert(ok);
assert(elem.kind == WASMTIME_EXTERNREF);
assert(strcmp((char*)wasmtime_externref_data(elem.of.externref), "Hello, World!") == 0);
wasmtime_val_delete(&elem);
printf("Touching `externref` global...\n");
// Lookup the `global` export.
wasm_global_t *global = wasm_extern_as_global(externs.data[1]);
assert(global != NULL);
ok = wasmtime_instance_export_get(context, &instance, "global", strlen("global"), &item);
assert(ok);
assert(item.kind == WASMTIME_EXTERN_GLOBAL);
// Set the global to our `externref`.
wasm_global_set(global, &externref);
error = wasmtime_global_set(context, &item.of.global, &externref_val);
if (error != NULL)
exit_with_error("failed to set global", error, NULL);
// Get the global, and it should return our `externref` again.
wasm_val_t global_val;
wasm_global_get(global, &global_val);
assert(global_val.kind == WASM_ANYREF);
assert(wasm_ref_same(global_val.of.ref, externref.of.ref));
wasmtime_val_t global_val;
wasmtime_global_get(context, &item.of.global, &global_val);
assert(global_val.kind == WASMTIME_EXTERNREF);
assert(strcmp((char*)wasmtime_externref_data(elem.of.externref), "Hello, World!") == 0);
wasmtime_val_delete(&global_val);
printf("Calling `externref` func...\n");
// Lookup the `func` export.
wasm_func_t *func = wasm_extern_as_func(externs.data[2]);
assert(func != NULL);
ok = wasmtime_instance_export_get(context, &instance, "func", strlen("func"), &item);
assert(ok);
assert(item.kind == WASMTIME_EXTERN_FUNC);
// And call it!
wasm_val_t args[1] = { externref };
wasm_val_t results[1];
wasm_val_vec_t args_vec = WASM_ARRAY_VEC(args);
wasm_val_vec_t results_vec = WASM_ARRAY_VEC(results);
error = wasmtime_func_call(func, &args_vec, &results_vec, &trap);
wasmtime_val_t results[1];
error = wasmtime_func_call(context, &item.of.func, &externref_val, 1, results, 1, &trap);
if (error != NULL || trap != NULL)
exit_with_error("failed to call function", error, trap);
// `func` returns the same reference we gave it, so `results[0]` should be our
// `externref`.
assert(results[0].kind == WASM_ANYREF);
assert(wasm_ref_same(results[0].of.ref, externref.of.ref));
assert(results[0].kind == WASMTIME_EXTERNREF);
assert(strcmp((char*)wasmtime_externref_data(results[0].of.externref), "Hello, World!") == 0);
wasmtime_val_delete(&results[0]);
// We can GC any now-unused references to our externref that the store is
// holding.
printf("GCing within the store...\n");
wasmtime_store_gc(store);
wasmtime_context_gc(context);
// Clean up after ourselves at this point
printf("All finished!\n");
ret = 0;
wasm_val_delete(&results[0]);
wasm_val_delete(&global_val);
wasm_val_delete(&elem);
wasm_extern_vec_delete(&externs);
wasm_val_delete(&externref);
wasm_instance_delete(instance);
wasm_module_delete(module);
wasm_store_delete(store);
wasmtime_store_delete(store);
wasmtime_module_delete(module);
wasm_engine_delete(engine);
return ret;
return 0;
}
static void exit_with_error(const char *message, wasmtime_error_t *error, wasm_trap_t *trap) {

View File

@@ -10,14 +10,13 @@ fn main() -> Result<()> {
let mut config = Config::new();
config.wasm_reference_types(true);
let engine = Engine::new(&config)?;
let store = Store::new(&engine);
let mut store = Store::new(&engine, ());
println!("Compiling module...");
let module = Module::from_file(&engine, "examples/externref.wat")?;
println!("Instantiating module...");
let imports = [];
let instance = Instance::new(&store, &module, &imports)?;
let instance = Instance::new(&mut store, &module, &[])?;
println!("Creating new `externref`...");
let externref = ExternRef::new("Hello, World!");
@@ -28,20 +27,25 @@ fn main() -> Result<()> {
);
println!("Touching `externref` table...");
let table = instance.get_table("table").unwrap();
table.set(3, Some(externref.clone()).into())?;
let elem = table.get(3).unwrap().unwrap_externref().unwrap();
let table = instance.get_table(&mut store, "table").unwrap();
table.set(&mut store, 3, Some(externref.clone()).into())?;
let elem = table
.get(&mut store, 3)
.unwrap() // assert in bounds
.unwrap_externref() // assert it's an externref table
.unwrap(); // assert the externref isn't null
assert!(elem.ptr_eq(&externref));
println!("Touching `externref` global...");
let global = instance.get_global("global").unwrap();
global.set(Some(externref.clone()).into())?;
let global_val = global.get().unwrap_externref().unwrap();
let global = instance.get_global(&mut store, "global").unwrap();
global.set(&mut store, Some(externref.clone()).into())?;
let global_val = global.get(&mut store).unwrap_externref().unwrap();
assert!(global_val.ptr_eq(&externref));
println!("Calling `externref` func...");
let func = instance.get_typed_func::<Option<ExternRef>, Option<ExternRef>>("func")?;
let ret = func.call(Some(externref.clone()))?;
let func =
instance.get_typed_func::<Option<ExternRef>, Option<ExternRef>, _>(&mut store, "func")?;
let ret = func.call(&mut store, Some(externref.clone()))?;
assert!(ret.is_some());
assert!(ret.unwrap().ptr_eq(&externref));

View File

@@ -2,9 +2,8 @@
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <wasm.h>
#include "wasmtime.h"
#include <wasmtime.h>
#define own
@@ -20,7 +19,8 @@ int main(int argc, const char* argv[]) {
// Initialize.
printf("Initializing...\n");
wasm_engine_t* engine = wasm_engine_new_with_config(config);
wasm_store_t* store = wasm_store_new(engine);
wasmtime_store_t* store = wasmtime_store_new(engine, NULL, NULL);
wasmtime_context_t* context = wasmtime_store_context(store);
// Load binary.
printf("Loading binary...\n");
@@ -42,75 +42,42 @@ int main(int argc, const char* argv[]) {
// Compile.
printf("Compiling module...\n");
wasm_module_t *module = NULL;
wasmtime_error_t* error = wasmtime_module_new(engine, &binary, &module);
wasmtime_module_t *module = NULL;
wasmtime_error_t* error = wasmtime_module_new(engine, (uint8_t*) binary.data, binary.size, &module);
if (!module)
exit_with_error("failed to compile module", error, NULL);
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");
wasm_instance_t* instance = NULL;
wasmtime_instance_t instance;
wasm_trap_t *trap = NULL;
wasm_extern_vec_t imports = WASM_EMPTY_VEC;
error = wasmtime_instance_new(store, module, &imports, &instance, &trap);
error = wasmtime_instance_new(context, module, NULL, 0, &instance, &trap);
if (error != NULL || trap != NULL)
exit_with_error("failed to instantiate", error, trap);
wasm_module_delete(module);
wasmtime_module_delete(module);
// Extract export.
printf("Extracting export...\n");
own wasm_extern_vec_t exports;
wasm_instance_exports(instance, &exports);
if (exports.size == 0) {
printf("> Error accessing exports!\n");
return 1;
}
// Getting second export (first is memory).
wasm_func_t* run_func = wasm_extern_as_func(exports.data[fib_idx]);
if (run_func == NULL) {
printf("> Error accessing export!\n");
return 1;
}
wasm_instance_delete(instance);
wasmtime_extern_t fib;
bool ok = wasmtime_instance_export_get(context, &instance, "fib", 3, &fib);
assert(ok);
// Call.
printf("Calling fib...\n");
wasm_val_t params[1] = { WASM_I32_VAL(6) };
wasm_val_t results[1];
wasm_val_vec_t params_vec = WASM_ARRAY_VEC(params);
wasm_val_vec_t results_vec = WASM_ARRAY_VEC(results);
error = wasmtime_func_call(run_func, &params_vec, &results_vec, &trap);
wasmtime_val_t params[1];
params[0].kind = WASMTIME_I32;
params[0].of.i32 = 6;
wasmtime_val_t results[1];
error = wasmtime_func_call(context, &fib.of.func, params, 1, results, 1, &trap);
if (error != NULL || trap != NULL)
exit_with_error("failed to call function", error, trap);
wasm_extern_vec_delete(&exports);
assert(results[0].kind == WASMTIME_I32);
printf("> fib(6) = %d\n", results[0].of.i32);
// Shut down.
printf("Shutting down...\n");
wasm_store_delete(store);
wasmtime_store_delete(store);
wasm_engine_delete(engine);
// All done.

View File

@@ -16,12 +16,12 @@ fn main() -> Result<()> {
// 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 mut store = Store::new(&engine, ());
let module = Module::from_file(&engine, "target/wasm32-unknown-unknown/debug/fib.wasm")?;
let instance = Instance::new(&store, &module, &[])?;
let instance = Instance::new(&mut store, &module, &[])?;
// Invoke `fib` export
let fib = instance.get_typed_func::<i32, i32>("fib")?;
println!("fib(6) = {}", fib.call(6)?);
let fib = instance.get_typed_func::<i32, i32, _>(&mut store, "fib")?;
println!("fib(6) = {}", fib.call(&mut store, 6)?);
Ok(())
}

View File

@@ -35,9 +35,11 @@ int main() {
// Create an *engine*, which is a compilation context, with our configured options.
wasm_engine_t *engine = wasm_engine_new_with_config(config);
assert(engine != NULL);
wasm_store_t *store = wasm_store_new(engine);
wasmtime_store_t *store = wasmtime_store_new(engine, NULL, NULL);
assert(store != NULL);
error = wasmtime_store_add_fuel(store, 10000);
wasmtime_context_t *context = wasmtime_store_context(store);
error = wasmtime_context_add_fuel(context, 10000);
if (error != NULL)
exit_with_error("failed to add fuel", error, NULL);
@@ -60,60 +62,57 @@ int main() {
// Parse the wat into the binary wasm format
wasm_byte_vec_t wasm;
error = wasmtime_wat2wasm(&wat, &wasm);
error = wasmtime_wat2wasm(wat.data, wat.size, &wasm);
if (error != NULL)
exit_with_error("failed to parse wat", error, NULL);
wasm_byte_vec_delete(&wat);
// Compile and instantiate our module
wasm_module_t *module = NULL;
error = wasmtime_module_new(engine, &wasm, &module);
wasmtime_module_t *module = NULL;
error = wasmtime_module_new(engine, (uint8_t*) wasm.data, wasm.size, &module);
if (module == NULL)
exit_with_error("failed to compile module", error, NULL);
wasm_byte_vec_delete(&wasm);
wasm_trap_t *trap = NULL;
wasm_instance_t *instance = NULL;
wasm_extern_vec_t imports = WASM_EMPTY_VEC;
error = wasmtime_instance_new(store, module, &imports, &instance, &trap);
if (instance == NULL)
wasmtime_instance_t instance;
error = wasmtime_instance_new(context, module, NULL, 0, &instance, &trap);
if (error != NULL || trap != NULL)
exit_with_error("failed to instantiate", error, trap);
// Lookup our `fibonacci` export function
wasm_extern_vec_t externs;
wasm_instance_exports(instance, &externs);
assert(externs.size == 1);
wasm_func_t *fibonacci = wasm_extern_as_func(externs.data[0]);
assert(fibonacci != NULL);
wasmtime_extern_t fib;
bool ok = wasmtime_instance_export_get(context, &instance, "fibonacci", strlen("fibonacci"), &fib);
assert(ok);
assert(fib.kind == WASMTIME_EXTERN_FUNC);
// Call it repeatedly until it fails
for (int n = 1; ; n++) {
uint64_t fuel_before;
wasmtime_store_fuel_consumed(store, &fuel_before);
wasm_val_t params[1] = { WASM_I32_VAL(n) };
wasm_val_t results[1];
wasm_val_vec_t params_vec = WASM_ARRAY_VEC(params);
wasm_val_vec_t results_vec = WASM_ARRAY_VEC(results);
error = wasmtime_func_call(fibonacci, &params_vec, &results_vec, &trap);
wasmtime_context_fuel_consumed(context, &fuel_before);
wasmtime_val_t params[1];
params[0].kind = WASMTIME_I32;
params[0].of.i32 = n;
wasmtime_val_t results[1];
error = wasmtime_func_call(context, &fib.of.func, params, 1, results, 1, &trap);
if (error != NULL || trap != NULL) {
printf("Exhausted fuel computing fib(%d)\n", n);
break;
}
uint64_t fuel_after;
wasmtime_store_fuel_consumed(store, &fuel_after);
assert(results[0].kind == WASM_I32);
wasmtime_context_fuel_consumed(context, &fuel_after);
assert(results[0].kind == WASMTIME_I32);
printf("fib(%d) = %d [consumed %lld fuel]\n", n, results[0].of.i32, fuel_after - fuel_before);
error = wasmtime_store_add_fuel(store, fuel_after - fuel_before);
error = wasmtime_context_add_fuel(context, fuel_after - fuel_before);
if (error != NULL)
exit_with_error("failed to add fuel", error, NULL);
}
// Clean up after ourselves at this point
wasm_extern_vec_delete(&externs);
wasm_instance_delete(instance);
wasm_module_delete(module);
wasm_store_delete(store);
wasmtime_module_delete(module);
wasmtime_store_delete(store);
wasm_engine_delete(engine);
return 0;
}

View File

@@ -9,16 +9,16 @@ fn main() -> Result<()> {
let mut config = Config::new();
config.consume_fuel(true);
let engine = Engine::new(&config)?;
let store = Store::new(&engine);
let mut store = Store::new(&engine, ());
store.add_fuel(10_000)?;
let module = Module::from_file(store.engine(), "examples/fuel.wat")?;
let instance = Instance::new(&store, &module, &[])?;
let instance = Instance::new(&mut store, &module, &[])?;
// Invoke `fibonacci` export with higher and higher numbers until we exhaust our fuel.
let fibonacci = instance.get_typed_func::<i32, i32>("fibonacci")?;
let fibonacci = instance.get_typed_func::<i32, i32, _>(&mut store, "fibonacci")?;
for n in 1.. {
let fuel_before = store.fuel_consumed().unwrap();
let output = match fibonacci.call(n) {
let output = match fibonacci.call(&mut store, n) {
Ok(v) => v,
Err(_) => {
println!("Exhausted fuel computing fib({})", n);

View File

@@ -30,8 +30,9 @@ int main() {
// Set up our context
wasm_engine_t *engine = wasm_engine_new();
assert(engine != NULL);
wasm_store_t *store = wasm_store_new(engine);
wasmtime_store_t *store = wasmtime_store_new(engine, NULL, NULL);
assert(store != NULL);
wasmtime_context_t *context = wasmtime_store_context(store);
// Load our input file to parse it next
FILE* file = fopen("examples/gcd.wat", "r");
@@ -52,52 +53,51 @@ int main() {
// Parse the wat into the binary wasm format
wasm_byte_vec_t wasm;
wasmtime_error_t *error = wasmtime_wat2wasm(&wat, &wasm);
wasmtime_error_t *error = wasmtime_wat2wasm(wat.data, wat.size, &wasm);
if (error != NULL)
exit_with_error("failed to parse wat", error, NULL);
wasm_byte_vec_delete(&wat);
// Compile and instantiate our module
wasm_module_t *module = NULL;
error = wasmtime_module_new(engine, &wasm, &module);
wasmtime_module_t *module = NULL;
error = wasmtime_module_new(engine, (uint8_t*) wasm.data, wasm.size, &module);
if (module == NULL)
exit_with_error("failed to compile module", error, NULL);
wasm_byte_vec_delete(&wasm);
wasm_trap_t *trap = NULL;
wasm_instance_t *instance = NULL;
wasm_extern_vec_t imports = WASM_EMPTY_VEC;
error = wasmtime_instance_new(store, module, &imports, &instance, &trap);
if (instance == NULL)
wasmtime_instance_t instance;
error = wasmtime_instance_new(context, module, NULL, 0, &instance, &trap);
if (error != NULL || trap != NULL)
exit_with_error("failed to instantiate", error, trap);
// 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);
wasmtime_extern_t gcd;
bool ok = wasmtime_instance_export_get(context, &instance, "gcd", 3, &gcd);
assert(ok);
assert(gcd.kind == WASMTIME_EXTERN_FUNC);
// And call it!
int a = 6;
int b = 27;
wasm_val_t params[2] = { WASM_I32_VAL(a), WASM_I32_VAL(b) };
wasm_val_t results[1];
wasm_val_vec_t params_vec = WASM_ARRAY_VEC(params);
wasm_val_vec_t results_vec = WASM_ARRAY_VEC(results);
error = wasmtime_func_call(gcd, &params_vec, &results_vec, &trap);
wasmtime_val_t params[2];
params[0].kind = WASMTIME_I32;
params[0].of.i32 = a;
params[1].kind = WASMTIME_I32;
params[1].of.i32 = b;
wasmtime_val_t results[1];
error = wasmtime_func_call(context, &gcd.of.func, params, 2, results, 1, &trap);
if (error != NULL || trap != NULL)
exit_with_error("failed to call gcd", error, trap);
assert(results[0].kind == WASM_I32);
assert(results[0].kind == WASMTIME_I32);
printf("gcd(%d, %d) = %d\n", a, b, results[0].of.i32);
// Clean up after ourselves at this point
ret = 0;
wasm_extern_vec_delete(&externs);
wasm_instance_delete(instance);
wasm_module_delete(module);
wasm_store_delete(store);
wasmtime_module_delete(module);
wasmtime_store_delete(store);
wasm_engine_delete(engine);
return ret;
}

View File

@@ -10,13 +10,13 @@ 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 mut store = Store::<()>::default();
let module = Module::from_file(store.engine(), "examples/gcd.wat")?;
let instance = Instance::new(&store, &module, &[])?;
let instance = Instance::new(&mut store, &module, &[])?;
// Invoke `gcd` export
let gcd = instance.get_typed_func::<(i32, i32), i32>("gcd")?;
let gcd = instance.get_typed_func::<(i32, i32), i32, _>(&mut store, "gcd")?;
println!("gcd(6, 27) = {}", gcd.call((6, 27))?);
println!("gcd(6, 27) = {}", gcd.call(&mut store, (6, 27))?);
Ok(())
}

View File

@@ -26,7 +26,14 @@ to tweak the `-lpthread` and such annotations as well as the name of the
static void exit_with_error(const char *message, wasmtime_error_t *error, wasm_trap_t *trap);
static wasm_trap_t* hello_callback(const wasm_val_vec_t* args, wasm_val_vec_t* results) {
static wasm_trap_t* hello_callback(
void *env,
wasmtime_caller_t *caller,
const wasmtime_val_t *args,
size_t nargs,
wasmtime_val_t *results,
size_t nresults
) {
printf("Calling back...\n");
printf("> Hello World!\n");
return NULL;
@@ -42,9 +49,11 @@ int main() {
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);
// modules. Note that we allocate some custom data here to live in the store,
// but here we skip that and specify NULL.
wasmtime_store_t *store = wasmtime_store_new(engine, NULL, NULL);
assert(store != NULL);
wasmtime_context_t *context = wasmtime_store_context(store);
// Read our input file, which in this case is a wasm text file.
FILE* file = fopen("examples/hello.wat", "r");
@@ -59,25 +68,27 @@ int main() {
// Parse the wat into the binary wasm format
wasm_byte_vec_t wasm;
wasmtime_error_t *error = wasmtime_wat2wasm(&wat, &wasm);
wasmtime_error_t *error = wasmtime_wat2wasm(wat.data, wat.size, &wasm);
if (error != NULL)
exit_with_error("failed to parse wat", error, NULL);
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 = NULL;
error = wasmtime_module_new(engine, &wasm, &module);
wasmtime_module_t *module = NULL;
error = wasmtime_module_new(engine, (uint8_t*) wasm.data, wasm.size, &module);
wasm_byte_vec_delete(&wasm);
if (error != NULL)
exit_with_error("failed to compile module", error, 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.
// function above. Note that we can assign custom data, but we just use NULL
// for now).
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);
wasmtime_func_t hello;
wasmtime_func_new(context, hello_ty, hello_callback, NULL, NULL, &hello);
// With our callback function we can now instantiate the compiled module,
// giving us an instance we can then execute exports from. Note that
@@ -85,26 +96,24 @@ int main() {
// to handle that here too.
printf("Instantiating module...\n");
wasm_trap_t *trap = NULL;
wasm_instance_t *instance = NULL;
wasm_extern_t* imports[] = { wasm_func_as_extern(hello) };
wasm_extern_vec_t imports_vec = WASM_ARRAY_VEC(imports);
error = wasmtime_instance_new(store, module, &imports_vec, &instance, &trap);
if (instance == NULL)
wasmtime_instance_t instance;
wasmtime_extern_t import;
import.kind = WASMTIME_EXTERN_FUNC;
import.of.func = hello;
error = wasmtime_instance_new(context, module, &import, 1, &instance, &trap);
if (error != NULL || trap != NULL)
exit_with_error("failed to instantiate", error, trap);
// 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);
wasmtime_extern_t run;
bool ok = wasmtime_instance_export_get(context, &instance, "run", 3, &run);
assert(ok);
assert(run.kind == WASMTIME_EXTERN_FUNC);
// And call it!
printf("Calling export...\n");
wasm_val_vec_t args_vec = WASM_EMPTY_VEC;
wasm_val_vec_t results_vec = WASM_EMPTY_VEC;
error = wasmtime_func_call(run, &args_vec, &results_vec, &trap);
error = wasmtime_func_call(context, &run.of.func, NULL, 0, NULL, 0, &trap);
if (error != NULL || trap != NULL)
exit_with_error("failed to call function", error, trap);
@@ -112,10 +121,8 @@ int main() {
printf("All finished!\n");
ret = 0;
wasm_extern_vec_delete(&externs);
wasm_instance_delete(instance);
wasm_module_delete(module);
wasm_store_delete(store);
wasmtime_module_delete(module);
wasmtime_store_delete(store);
wasm_engine_delete(engine);
return ret;
}

View File

@@ -1,136 +0,0 @@
/*
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-c-api
cc examples/hello.cc \
-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 exit_with_error(const char *message, wasmtime_error_t *error, wasm_trap_t *trap);
static wasm_trap_t* hello_callback(const wasm_val_vec_t* args, wasm_val_vec_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;
wasmtime_error_t *error = wasmtime_wat2wasm(&wat, &wasm);
if (error != NULL)
exit_with_error("failed to parse wat", error, NULL);
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 = NULL;
error = wasmtime_module_new(engine, &wasm, &module);
wasm_byte_vec_delete(&wasm);
if (error != NULL)
exit_with_error("failed to compile module", error, 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;
wasm_instance_t *instance = NULL;
wasm_extern_t* imports[] = { wasm_func_as_extern(hello) };
wasm_extern_vec_t imports_vec = WASM_ARRAY_VEC(imports);
error = wasmtime_instance_new(store, module, &imports_vec, &instance, &trap);
if (instance == NULL)
exit_with_error("failed to instantiate", error, trap);
// 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");
wasm_val_vec_t args_vec = WASM_EMPTY_VEC;
wasm_val_vec_t results_vec = WASM_EMPTY_VEC;
error = wasmtime_func_call(run, &args_vec, &results_vec, &trap);
if (error != NULL || trap != NULL)
exit_with_error("failed to call function", error, trap);
// Clean up after ourselves at this point
printf("All finished!\n");
ret = 0;
wasm_extern_vec_delete(&externs);
wasm_instance_delete(instance);
wasm_module_delete(module);
wasm_store_delete(store);
wasm_engine_delete(engine);
return ret;
}
static void exit_with_error(const char *message, wasmtime_error_t *error, wasm_trap_t *trap) {
fprintf(stderr, "error: %s\n", message);
wasm_byte_vec_t error_message;
if (error != NULL) {
wasmtime_error_message(error, &error_message);
wasmtime_error_delete(error);
} else {
wasm_trap_message(trap, &error_message);
wasm_trap_delete(trap);
}
fprintf(stderr, "%.*s\n", (int) error_message.size, error_message.data);
wasm_byte_vec_delete(&error_message);
exit(1);
}

View File

@@ -6,23 +6,42 @@
use anyhow::Result;
use wasmtime::*;
struct MyState {
name: String,
count: usize,
}
fn main() -> Result<()> {
// Configure the initial compilation environment, creating the global
// `Store` structure. Note that you can also tweak configuration settings
// with a `Config` and an `Engine` if desired.
println!("Initializing...");
let store = Store::default();
// Compile the wasm binary into an in-memory instance of a `Module`.
// First the wasm module needs to be compiled. This is done with a global
// "compilation environment" within an `Engine`. Note that engines can be
// further configured through `Config` if desired instead of using the
// default like this is here.
println!("Compiling module...");
let module = Module::from_file(store.engine(), "examples/hello.wat")?;
let engine = Engine::default();
let module = Module::from_file(&engine, "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.
// After a module is compiled we create a `Store` which will contain
// instantiated modules and other items like host functions. A Store
// contains an arbitrary piece of host information, and we use `MyState`
// here.
println!("Initializing...");
let mut store = Store::new(
&engine,
MyState {
name: "hello, world!".to_string(),
count: 0,
},
);
// Our wasm module we'll be instantiating requires one imported function.
// the function takes no parameters and returns no results. We create a host
// implementation of that function here, and the `caller` parameter here is
// used to get access to our original `MyState` value.
println!("Creating callback...");
let hello_func = Func::wrap(&store, || {
let hello_func = Func::wrap(&mut store, |mut caller: Caller<'_, MyState>| {
println!("Calling back...");
println!("> Hello World!");
println!("> {}", caller.data().name);
caller.data_mut().count += 1;
});
// Once we've got that all set up we can then move to the instantiation
@@ -30,15 +49,15 @@ fn main() -> Result<()> {
// Note that this is where the wasm `start` function, if any, would run.
println!("Instantiating module...");
let imports = [hello_func.into()];
let instance = Instance::new(&store, &module, &imports)?;
let instance = Instance::new(&mut store, &module, &imports)?;
// Next we poke around a bit to extract the `run` function from the module.
println!("Extracting export...");
let run = instance.get_typed_func::<(), ()>("run")?;
let run = instance.get_typed_func::<(), (), _>(&mut store, "run")?;
// And last but not least we can call it!
println!("Calling export...");
run.call(())?;
run.call(&mut store, ())?;
println!("Done.");
Ok(())

View File

@@ -61,11 +61,12 @@ int main() {
wasmtime_config_interruptable_set(config, true);
wasm_engine_t *engine = wasm_engine_new_with_config(config);
assert(engine != NULL);
wasm_store_t *store = wasm_store_new(engine);
wasmtime_store_t *store = wasmtime_store_new(engine, NULL, NULL);
assert(store != NULL);
wasmtime_context_t *context = wasmtime_store_context(store);
// Create our interrupt handle we'll use later
wasmtime_interrupt_handle_t *handle = wasmtime_interrupt_handle_new(store);
wasmtime_interrupt_handle_t *handle = wasmtime_interrupt_handle_new(context);
assert(handle != NULL);
// Read our input file, which in this case is a wasm text file.
@@ -81,50 +82,45 @@ int main() {
// Parse the wat into the binary wasm format
wasm_byte_vec_t wasm;
wasmtime_error_t *error = wasmtime_wat2wasm(&wat, &wasm);
wasmtime_error_t *error = wasmtime_wat2wasm(wat.data, wat.size, &wasm);
if (error != NULL)
exit_with_error("failed to parse wat", error, NULL);
wasm_byte_vec_delete(&wat);
// Now that we've got our binary webassembly we can compile our module.
wasm_module_t *module = NULL;
wasm_trap_t *trap = NULL;
wasm_instance_t *instance = NULL;
wasm_extern_vec_t imports = WASM_EMPTY_VEC;
error = wasmtime_module_new(engine, &wasm, &module);
wasmtime_module_t *module = NULL;
error = wasmtime_module_new(engine, (uint8_t*) wasm.data, wasm.size, &module);
wasm_byte_vec_delete(&wasm);
if (error != NULL)
exit_with_error("failed to compile module", error, NULL);
error = wasmtime_instance_new(store, module, &imports, &instance, &trap);
if (instance == NULL)
wasm_trap_t *trap = NULL;
wasmtime_instance_t instance;
error = wasmtime_instance_new(context, module, NULL, 0, &instance, &trap);
if (error != NULL || trap != NULL)
exit_with_error("failed to instantiate", error, trap);
wasmtime_module_delete(module);
// Lookup our `run` export function
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);
wasmtime_extern_t run;
bool ok = wasmtime_instance_export_get(context, &instance, "run", 3, &run);
assert(ok);
assert(run.kind == WASMTIME_EXTERN_FUNC);
// Spawn a thread to send us an interrupt after a period of time.
spawn_interrupt(handle);
// And call it!
printf("Entering infinite loop...\n");
wasm_val_vec_t args_vec = WASM_EMPTY_VEC;
wasm_val_vec_t results_vec = WASM_EMPTY_VEC;
error = wasmtime_func_call(run, &args_vec, &results_vec, &trap);
error = wasmtime_func_call(context, &run.of.func, NULL, 0, NULL, 0, &trap);
assert(error == NULL);
assert(trap != NULL);
printf("Got a trap!...\n");
// `trap` can be inspected here to see the trap message has an interrupt in it
wasm_trap_delete(trap);
wasm_extern_vec_delete(&externs);
wasm_instance_delete(instance);
wasm_module_delete(module);
wasm_store_delete(store);
wasmtime_store_delete(store);
wasm_engine_delete(engine);
return 0;
}

View File

@@ -10,13 +10,13 @@ fn main() -> Result<()> {
// Enable interruptable code via `Config` and then create an interrupt
// handle which we'll use later to interrupt running code.
let engine = Engine::new(Config::new().interruptable(true))?;
let store = Store::new(&engine);
let mut store = Store::new(&engine, ());
let interrupt_handle = store.interrupt_handle()?;
// Compile and instantiate a small example with an infinite loop.
let module = Module::from_file(&engine, "examples/interrupt.wat")?;
let instance = Instance::new(&store, &module, &[])?;
let run = instance.get_typed_func::<(), ()>("run")?;
let instance = Instance::new(&mut store, &module, &[])?;
let run = instance.get_typed_func::<(), (), _>(&mut store, "run")?;
// Spin up a thread to send us an interrupt in a second
std::thread::spawn(move || {
@@ -26,7 +26,7 @@ fn main() -> Result<()> {
});
println!("Entering infinite loop ...");
let trap = run.call(()).unwrap_err();
let trap = run.call(&mut store, ()).unwrap_err();
println!("trap received...");
assert!(trap.to_string().contains("wasm trap: interrupt"));

View File

@@ -30,12 +30,12 @@ static void exit_with_error(const char *message, wasmtime_error_t *error, wasm_t
static void read_wat_file(wasm_engine_t *engine, wasm_byte_vec_t *bytes, const char *file);
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);
wasmtime_store_t *store = wasmtime_store_new(engine, NULL, NULL);
assert(store != NULL);
wasmtime_context_t *context = wasmtime_store_context(store);
wasm_byte_vec_t linking1_wasm, linking2_wasm;
read_wat_file(engine, &linking1_wasm, "examples/linking1.wat");
@@ -43,18 +43,18 @@ int main() {
// Compile our two modules
wasmtime_error_t *error;
wasm_module_t *linking1_module = NULL;
wasm_module_t *linking2_module = NULL;
error = wasmtime_module_new(engine, &linking1_wasm, &linking1_module);
wasmtime_module_t *linking1_module = NULL;
wasmtime_module_t *linking2_module = NULL;
error = wasmtime_module_new(engine, (uint8_t*) linking1_wasm.data, linking1_wasm.size, &linking1_module);
if (error != NULL)
exit_with_error("failed to compile linking1", error, NULL);
error = wasmtime_module_new(engine, &linking2_wasm, &linking2_module);
error = wasmtime_module_new(engine, (uint8_t*) linking2_wasm.data, linking2_wasm.size, &linking2_module);
if (error != NULL)
exit_with_error("failed to compile linking2", error, NULL);
wasm_byte_vec_delete(&linking1_wasm);
wasm_byte_vec_delete(&linking2_wasm);
// Instantiate wasi
// Configure WASI and store it within our `wasmtime_store_t`
wasi_config_t *wasi_config = wasi_config_new();
assert(wasi_config);
wasi_config_inherit_argv(wasi_config);
@@ -63,57 +63,48 @@ int main() {
wasi_config_inherit_stdout(wasi_config);
wasi_config_inherit_stderr(wasi_config);
wasm_trap_t *trap = NULL;
wasi_instance_t *wasi = wasi_instance_new(store, "wasi_snapshot_preview1", wasi_config, &trap);
if (wasi == NULL)
error = wasmtime_context_set_wasi(context, wasi_config);
if (error != NULL)
exit_with_error("failed to instantiate wasi", NULL, trap);
// Create our linker which will be linking our modules together, and then add
// our WASI instance to it.
wasmtime_linker_t *linker = wasmtime_linker_new(store);
error = wasmtime_linker_define_wasi(linker, wasi);
wasmtime_linker_t *linker = wasmtime_linker_new(engine);
error = wasmtime_linker_define_wasi(linker);
if (error != NULL)
exit_with_error("failed to link wasi", error, NULL);
// Instantiate `linking2` with our linker.
wasm_instance_t *linking2;
error = wasmtime_linker_instantiate(linker, linking2_module, &linking2, &trap);
wasmtime_instance_t linking2;
error = wasmtime_linker_instantiate(linker, context, linking2_module, &linking2, &trap);
if (error != NULL || trap != NULL)
exit_with_error("failed to instantiate linking2", error, trap);
// Register our new `linking2` instance with the linker
wasm_name_t linking2_name;
linking2_name.data = "linking2";
linking2_name.size = strlen(linking2_name.data);
error = wasmtime_linker_define_instance(linker, &linking2_name, linking2);
error = wasmtime_linker_define_instance(linker, context, "linking2", strlen("linking2"), &linking2);
if (error != NULL)
exit_with_error("failed to link linking2", error, NULL);
// Instantiate `linking1` with the linker now that `linking2` is defined
wasm_instance_t *linking1;
error = wasmtime_linker_instantiate(linker, linking1_module, &linking1, &trap);
wasmtime_instance_t linking1;
error = wasmtime_linker_instantiate(linker, context, linking1_module, &linking1, &trap);
if (error != NULL || trap != NULL)
exit_with_error("failed to instantiate linking1", error, trap);
// Lookup our `run` export function
wasm_extern_vec_t linking1_externs;
wasm_instance_exports(linking1, &linking1_externs);
assert(linking1_externs.size == 1);
wasm_func_t *run = wasm_extern_as_func(linking1_externs.data[0]);
assert(run != NULL);
wasm_val_vec_t args_vec = WASM_EMPTY_VEC;
wasm_val_vec_t results_vec = WASM_EMPTY_VEC;
error = wasmtime_func_call(run, &args_vec, &results_vec, &trap);
wasmtime_extern_t run;
bool ok = wasmtime_instance_export_get(context, &linking1, "run", 3, &run);
assert(ok);
assert(run.kind == WASMTIME_EXTERN_FUNC);
error = wasmtime_func_call(context, &run.of.func, NULL, 0, NULL, 0, &trap);
if (error != NULL || trap != NULL)
exit_with_error("failed to call run", error, trap);
// Clean up after ourselves at this point
wasm_extern_vec_delete(&linking1_externs);
wasm_instance_delete(linking1);
wasm_instance_delete(linking2);
wasmtime_linker_delete(linker);
wasm_module_delete(linking1_module);
wasm_module_delete(linking2_module);
wasm_store_delete(store);
wasmtime_module_delete(linking1_module);
wasmtime_module_delete(linking2_module);
wasmtime_store_delete(store);
wasm_engine_delete(engine);
return 0;
}
@@ -141,7 +132,7 @@ static void read_wat_file(
fclose(file);
// Parse the wat into the binary wasm format
wasmtime_error_t *error = wasmtime_wat2wasm(&wat, bytes);
wasmtime_error_t *error = wasmtime_wat2wasm(wat.data, wat.size, bytes);
if (error != NULL)
exit_with_error("failed to parse wat", error, NULL);
wasm_byte_vec_delete(&wat);

View File

@@ -4,36 +4,35 @@
use anyhow::Result;
use wasmtime::*;
use wasmtime_wasi::sync::{Wasi, WasiCtxBuilder};
use wasmtime_wasi::sync::WasiCtxBuilder;
fn main() -> Result<()> {
let engine = Engine::default();
let store = Store::new(&engine);
// First set up our linker which is going to be linking modules together. We
// want our linker to have wasi available, so we set that up here as well.
let mut linker = Linker::new(&store);
let wasi = Wasi::new(
&store,
WasiCtxBuilder::new()
.inherit_stdio()
.inherit_args()?
.build(),
);
wasi.add_to_linker(&mut linker)?;
let mut linker = Linker::new(&engine);
wasmtime_wasi::add_to_linker(&mut linker, |s| s)?;
// Load and compile our two modules
let linking1 = Module::from_file(&engine, "examples/linking1.wat")?;
let linking2 = Module::from_file(&engine, "examples/linking2.wat")?;
// Configure WASI and insert it into a `Store`
let wasi = WasiCtxBuilder::new()
.inherit_stdio()
.inherit_args()?
.build();
let mut store = Store::new(&engine, wasi);
// Instantiate our first module which only uses WASI, then register that
// instance with the linker since the next linking will use it.
let linking2 = linker.instantiate(&linking2)?;
linker.instance("linking2", &linking2)?;
let linking2 = linker.instantiate(&mut store, &linking2)?;
linker.instance(&mut store, "linking2", linking2)?;
// And with that we can perform the final link and the execute the module.
let linking1 = linker.instantiate(&linking1)?;
let run = linking1.get_typed_func::<(), ()>("run")?;
run.call(())?;
let linking1 = linker.instantiate(&mut store, &linking1)?;
let run = linking1.get_typed_func::<(), (), _>(&mut store, "run")?;
run.call(&mut store, ())?;
Ok(())
}

View File

@@ -30,23 +30,6 @@ originally
static void exit_with_error(const char *message, wasmtime_error_t *error, wasm_trap_t *trap);
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");
@@ -54,11 +37,16 @@ void check(bool success) {
}
}
void check_call(wasm_func_t* func, const wasm_val_vec_t* args_vec, int32_t expected) {
wasm_val_t results[1];
wasm_val_vec_t results_vec = WASM_ARRAY_VEC(results);
void check_call(wasmtime_context_t *store,
wasmtime_func_t *func,
const wasmtime_val_t* args,
size_t nargs,
int32_t expected) {
wasmtime_val_t results[1];
wasm_trap_t *trap = NULL;
wasmtime_error_t *error = wasmtime_func_call(func, args_vec, &results_vec, &trap);
wasmtime_error_t *error = wasmtime_func_call(
store, func, args, nargs, results, 1, &trap
);
if (error != NULL || trap != NULL)
exit_with_error("failed to call function", error, trap);
if (results[0].of.i32 != expected) {
@@ -67,45 +55,51 @@ void check_call(wasm_func_t* func, const wasm_val_vec_t* args_vec, int32_t expec
}
}
void check_call0(wasm_func_t* func, int32_t expected) {
wasm_val_vec_t args_vec = WASM_EMPTY_VEC;
check_call(func, &args_vec, expected);
void check_call0(wasmtime_context_t *store, wasmtime_func_t *func, int32_t expected) {
check_call(store, func, NULL, 0, expected);
}
void check_call1(wasm_func_t* func, int32_t arg, int32_t expected) {
wasm_val_t args[] = { WASM_I32_VAL(arg) };
wasm_val_vec_t args_vec = WASM_ARRAY_VEC(args);
check_call(func, &args_vec, expected);
void check_call1(wasmtime_context_t *store, wasmtime_func_t *func, int32_t arg, int32_t expected) {
wasmtime_val_t args[1];
args[0].kind = WASMTIME_I32;
args[0].of.i32 = arg;
check_call(store, func, args, 1, expected);
}
void check_call2(wasm_func_t* func, int32_t arg1, int32_t arg2, int32_t expected) {
wasm_val_t args[] = { WASM_I32_VAL(arg1), WASM_I32_VAL(arg2) };
wasm_val_vec_t args_vec = WASM_ARRAY_VEC(args);
check_call(func, &args_vec, expected);
void check_call2(wasmtime_context_t *store, wasmtime_func_t *func, int32_t arg1, int32_t arg2, int32_t expected) {
wasmtime_val_t args[2];
args[0].kind = WASMTIME_I32;
args[0].of.i32 = arg1;
args[1].kind = WASMTIME_I32;
args[1].of.i32 = arg2;
check_call(store, func, args, 2, expected);
}
void check_ok(wasm_func_t* func, const wasm_val_vec_t* args_vec) {
void check_ok(wasmtime_context_t *store, wasmtime_func_t *func, const wasmtime_val_t* args, size_t nargs) {
wasm_trap_t *trap = NULL;
wasm_val_vec_t results_vec = WASM_EMPTY_VEC;
wasmtime_error_t *error = wasmtime_func_call(func, args_vec, &results_vec, &trap);
wasmtime_error_t *error = wasmtime_func_call(store, func, args, nargs, NULL, 0, &trap);
if (error != NULL || trap != NULL)
exit_with_error("failed to call function", error, trap);
}
void check_ok2(wasm_func_t* func, int32_t arg1, int32_t arg2) {
wasm_val_t args[] = { WASM_I32_VAL(arg1), WASM_I32_VAL(arg2) };
wasm_val_vec_t args_vec = WASM_ARRAY_VEC(args);
check_ok(func, &args_vec);
void check_ok2(wasmtime_context_t *store, wasmtime_func_t *func, int32_t arg1, int32_t arg2) {
wasmtime_val_t args[2];
args[0].kind = WASMTIME_I32;
args[0].of.i32 = arg1;
args[1].kind = WASMTIME_I32;
args[1].of.i32 = arg2;
check_ok(store, func, args, 2);
}
void check_trap(wasm_func_t* func, const wasm_val_vec_t* args_vec, size_t num_results) {
void check_trap(wasmtime_context_t *store,
wasmtime_func_t *func,
const wasmtime_val_t *args,
size_t nargs,
size_t num_results) {
assert(num_results <= 1);
wasm_val_t results[1];
wasm_val_vec_t results_vec;
results_vec.data = results;
results_vec.size = num_results;
wasmtime_val_t results[1];
wasm_trap_t *trap = NULL;
wasmtime_error_t *error = wasmtime_func_call(func, args_vec, &results_vec, &trap);
wasmtime_error_t *error = wasmtime_func_call(store, func, args, nargs, results, num_results, &trap);
if (error != NULL)
exit_with_error("failed to call function", error, NULL);
if (trap == NULL) {
@@ -115,23 +109,28 @@ void check_trap(wasm_func_t* func, const wasm_val_vec_t* args_vec, size_t num_re
wasm_trap_delete(trap);
}
void check_trap1(wasm_func_t* func, int32_t arg) {
wasm_val_t args[] = { WASM_I32_VAL(arg) };
wasm_val_vec_t args_vec = WASM_ARRAY_VEC(args);
check_trap(func, &args_vec, 1);
void check_trap1(wasmtime_context_t *store, wasmtime_func_t *func, int32_t arg) {
wasmtime_val_t args[1];
args[0].kind = WASMTIME_I32;
args[0].of.i32 = arg;
check_trap(store, func, args, 1, 1);
}
void check_trap2(wasm_func_t* func, int32_t arg1, int32_t arg2) {
wasm_val_t args[] = { WASM_I32_VAL(arg1), WASM_I32_VAL(arg2) };
wasm_val_vec_t args_vec = WASM_ARRAY_VEC(args);
check_trap(func, &args_vec, 0);
void check_trap2(wasmtime_context_t *store, wasmtime_func_t *func, int32_t arg1, int32_t arg2) {
wasmtime_val_t args[2];
args[0].kind = WASMTIME_I32;
args[0].of.i32 = arg1;
args[1].kind = WASMTIME_I32;
args[1].of.i32 = arg2;
check_trap(store, func, args, 2, 0);
}
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);
wasmtime_store_t* store = wasmtime_store_new(engine, NULL, NULL);
wasmtime_context_t *context = wasmtime_store_context(store);
// Load our input file to parse it next
FILE* file = fopen("examples/memory.wat", "r");
@@ -152,102 +151,108 @@ int main(int argc, const char* argv[]) {
// Parse the wat into the binary wasm format
wasm_byte_vec_t binary;
wasmtime_error_t *error = wasmtime_wat2wasm(&wat, &binary);
wasmtime_error_t *error = wasmtime_wat2wasm(wat.data, wat.size, &binary);
if (error != NULL)
exit_with_error("failed to parse wat", error, NULL);
wasm_byte_vec_delete(&wat);
// Compile.
printf("Compiling module...\n");
wasm_module_t* module = NULL;
error = wasmtime_module_new(engine, &binary, &module);
wasmtime_module_t* module = NULL;
error = wasmtime_module_new(engine, (uint8_t*) binary.data, binary.size, &module);
if (error)
exit_with_error("failed to compile module", error, NULL);
wasm_byte_vec_delete(&binary);
// Instantiate.
printf("Instantiating module...\n");
wasm_instance_t* instance = NULL;
wasmtime_instance_t instance;
wasm_trap_t *trap = NULL;
wasm_extern_vec_t imports = WASM_EMPTY_VEC;
error = wasmtime_instance_new(store, module, &imports, &instance, &trap);
if (!instance)
error = wasmtime_instance_new(context, module, NULL, 0, &instance, &trap);
if (error != NULL || trap != NULL)
exit_with_error("failed to instantiate", error, trap);
wasmtime_module_delete(module);
// 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);
wasm_memory_delete(copy);
wasmtime_memory_t memory;
wasmtime_func_t size_func, load_func, store_func;
wasmtime_extern_t item;
bool ok;
ok = wasmtime_instance_export_get(context, &instance, "memory", strlen("memory"), &item);
assert(ok && item.kind == WASMTIME_EXTERN_MEMORY);
memory = item.of.memory;
ok = wasmtime_instance_export_get(context, &instance, "size", strlen("size"), &item);
assert(ok && item.kind == WASMTIME_EXTERN_FUNC);
size_func = item.of.func;
ok = wasmtime_instance_export_get(context, &instance, "load", strlen("load"), &item);
assert(ok && item.kind == WASMTIME_EXTERN_FUNC);
load_func = item.of.func;
ok = wasmtime_instance_export_get(context, &instance, "store", strlen("store"), &item);
assert(ok && item.kind == WASMTIME_EXTERN_FUNC);
store_func = item.of.func;
// 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(wasmtime_memory_size(context, &memory) == 2);
check(wasmtime_memory_data_size(context, &memory) == 0x20000);
check(wasmtime_memory_data(context, &memory)[0] == 0);
check(wasmtime_memory_data(context, &memory)[0x1000] == 1);
check(wasmtime_memory_data(context, &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);
check_call0(context, &size_func, 2);
check_call1(context, &load_func, 0, 0);
check_call1(context, &load_func, 0x1000, 1);
check_call1(context, &load_func, 0x1003, 4);
check_call1(context, &load_func, 0x1ffff, 0);
check_trap1(context, &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);
wasmtime_memory_data(context, &memory)[0x1003] = 5;
check_ok2(context, &store_func, 0x1002, 6);
check_trap2(context, &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);
check(wasmtime_memory_data(context, &memory)[0x1002] == 6);
check(wasmtime_memory_data(context, &memory)[0x1003] == 5);
check_call1(context, &load_func, 0x1002, 6);
check_call1(context, &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);
uint32_t old_size;
error = wasmtime_memory_grow(context, &memory, 1, &old_size);
if (error != NULL)
exit_with_error("failed to grow memory", error, trap);
check(wasmtime_memory_size(context, &memory) == 3);
check(wasmtime_memory_data_size(context, &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_call1(context, &load_func, 0x20000, 0);
check_ok2(context, &store_func, 0x20000, 0);
check_trap1(context, &load_func, 0x30000);
check_trap2(context, &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);
error = wasmtime_memory_grow(context, &memory, 1, &old_size);
assert(error != NULL);
wasmtime_error_delete(error);
error = wasmtime_memory_grow(context, &memory, 0, &old_size);
if (error != NULL)
exit_with_error("failed to grow memory", error, trap);
// 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));
wasmtime_memory_t memory2;
error = wasmtime_memory_new(context, memorytype, &memory2);
if (error != NULL)
exit_with_error("failed to create memory", error, trap);
wasm_memorytype_delete(memorytype);
wasm_memory_delete(memory2);
check(wasmtime_memory_size(context, &memory2) == 5);
// Shut down.
printf("Shutting down...\n");
wasm_store_delete(store);
wasmtime_store_delete(store);
wasm_engine_delete(engine);
// All done.

View File

@@ -10,75 +10,65 @@ use anyhow::Result;
use wasmtime::*;
fn main() -> Result<()> {
// Create our `Store` context and then compile a module and create an
// Create our `store_fn` 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.engine(), "examples/memory.wat")?;
let instance = Instance::new(&wasmtime_store, &module, &[])?;
let mut store: Store<()> = Store::default();
let module = Module::from_file(store.engine(), "examples/memory.wat")?;
let instance = Instance::new(&mut store, &module, &[])?;
// Load up our exports from the instance
// load_fn up our exports from the instance
let memory = instance
.get_memory("memory")
.get_memory(&mut store, "memory")
.ok_or(anyhow::format_err!("failed to find `memory` export"))?;
let size = instance.get_typed_func::<(), i32>("size")?;
let load = instance.get_typed_func::<i32, i32>("load")?;
let store = instance.get_typed_func::<(i32, i32), ()>("store")?;
let size = instance.get_typed_func::<(), i32, _>(&mut store, "size")?;
let load_fn = instance.get_typed_func::<i32, i32, _>(&mut store, "load")?;
let store_fn = instance.get_typed_func::<(i32, i32), (), _>(&mut store, "store")?;
// 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!(memory.size(&store), 2);
assert_eq!(memory.data_size(&store), 0x20000);
assert_eq!(memory.data_mut(&mut store)[0], 0);
assert_eq!(memory.data_mut(&mut store)[0x1000], 1);
assert_eq!(memory.data_mut(&mut store)[0x1003], 4);
assert_eq!(size.call(())?, 2);
assert_eq!(load.call(0)?, 0);
assert_eq!(load.call(0x1000)?, 1);
assert_eq!(load.call(0x1003)?, 4);
assert_eq!(load.call(0x1ffff)?, 0);
assert!(load.call(0x20000).is_err()); // out of bounds trap
assert_eq!(size.call(&mut store, ())?, 2);
assert_eq!(load_fn.call(&mut store, 0)?, 0);
assert_eq!(load_fn.call(&mut store, 0x1000)?, 1);
assert_eq!(load_fn.call(&mut store, 0x1003)?, 4);
assert_eq!(load_fn.call(&mut store, 0x1ffff)?, 0);
assert!(load_fn.call(&mut store, 0x20000).is_err()); // out of bounds trap
println!("Mutating memory...");
unsafe {
memory.data_unchecked_mut()[0x1003] = 5;
}
memory.data_mut(&mut store)[0x1003] = 5;
store.call((0x1002, 6))?;
assert!(store.call((0x20000, 0)).is_err()); // out of bounds trap
store_fn.call(&mut store, (0x1002, 6))?;
assert!(store_fn.call(&mut 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.call(0x1002)?, 6);
assert_eq!(load.call(0x1003)?, 5);
assert_eq!(memory.data(&store)[0x1002], 6);
assert_eq!(memory.data(&store)[0x1003], 5);
assert_eq!(load_fn.call(&mut store, 0x1002)?, 6);
assert_eq!(load_fn.call(&mut store, 0x1003)?, 5);
// Grow memory.
println!("Growing memory...");
memory.grow(1)?;
assert_eq!(memory.size(), 3);
assert_eq!(memory.data_size(), 0x30000);
memory.grow(&mut store, 1)?;
assert_eq!(memory.size(&store), 3);
assert_eq!(memory.data_size(&store), 0x30000);
assert_eq!(load.call(0x20000)?, 0);
store.call((0x20000, 0))?;
assert!(load.call(0x30000).is_err());
assert!(store.call((0x30000, 0)).is_err());
assert_eq!(load_fn.call(&mut store, 0x20000)?, 0);
store_fn.call(&mut store, (0x20000, 0))?;
assert!(load_fn.call(&mut store, 0x30000).is_err());
assert!(store_fn.call(&mut store, (0x30000, 0)).is_err());
assert!(memory.grow(1).is_err());
assert!(memory.grow(0).is_ok());
assert!(memory.grow(&mut store, 1).is_err());
assert!(memory.grow(&mut store, 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());
let memory2 = Memory::new(&mut store, memorytype)?;
assert_eq!(memory2.size(&store), 5);
assert!(memory2.grow(&mut store, 1).is_err());
assert!(memory2.grow(&mut store, 0).is_ok());
Ok(())
}

View File

@@ -32,27 +32,37 @@ static void exit_with_error(const char *message, wasmtime_error_t *error, wasm_t
// A function to be called from Wasm code.
wasm_trap_t* callback(
const wasm_val_vec_t* args, wasm_val_vec_t* results
void *env,
wasmtime_caller_t *caller,
const wasmtime_val_t* args,
size_t nargs,
wasmtime_val_t* results,
size_t nresults
) {
printf("Calling back...\n");
printf("> %"PRIu32" %"PRIu64"\n", args->data[0].of.i32, args->data[1].of.i64);
printf("> %"PRIu32" %"PRIu64"\n", args[0].of.i32, args[1].of.i64);
printf("\n");
wasm_val_copy(&results->data[0], &args->data[1]);
wasm_val_copy(&results->data[1], &args->data[0]);
results[0] = args[1];
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[]
void* env,
wasmtime_caller_t *caller,
const wasmtime_val_t* args,
size_t nargs,
wasmtime_val_t* results,
size_t nresults
) {
int i = *(int*)env;
printf("Calling back closure...\n");
printf("> %d\n", i);
results[0].kind = WASM_I32;
results[0].kind = WASMTIME_I32;
results[0].of.i32 = (int32_t)i;
return NULL;
}
@@ -62,7 +72,8 @@ 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);
wasmtime_store_t* store = wasmtime_store_new(engine, NULL, NULL);
wasmtime_context_t *context = wasmtime_store_context(store);
// Load our input file to parse it next
FILE* file = fopen("examples/multi.wat", "r");
@@ -83,18 +94,17 @@ int main(int argc, const char* argv[]) {
// Parse the wat into the binary wasm format
wasm_byte_vec_t binary;
wasmtime_error_t *error = wasmtime_wat2wasm(&wat, &binary);
wasmtime_error_t *error = wasmtime_wat2wasm(wat.data, wat.size, &binary);
if (error != NULL)
exit_with_error("failed to parse wat", error, NULL);
wasm_byte_vec_delete(&wat);
// Compile.
printf("Compiling module...\n");
wasm_module_t* module = NULL;
error = wasmtime_module_new(engine, &binary, &module);
wasmtime_module_t* module = NULL;
error = wasmtime_module_new(engine, (uint8_t*) binary.data, binary.size, &module);
if (error)
exit_with_error("failed to compile module", error, NULL);
wasm_byte_vec_delete(&binary);
// Create external print functions.
@@ -105,65 +115,54 @@ int main(int argc, const char* argv[]) {
wasm_valtype_new_i64(),
wasm_valtype_new_i32()
);
wasm_func_t* callback_func =
wasm_func_new(store, callback_type, callback);
wasmtime_func_t callback_func;
wasmtime_func_new(context, callback_type, callback, NULL, NULL, &callback_func);
wasm_functype_delete(callback_type);
// Instantiate.
printf("Instantiating module...\n");
wasm_extern_t* imports[] = {wasm_func_as_extern(callback_func)};
wasm_extern_vec_t imports_vec = WASM_ARRAY_VEC(imports);
wasm_instance_t* instance = NULL;
wasmtime_extern_t imports[1];
imports[0].kind = WASMTIME_EXTERN_FUNC;
imports[0].of.func = callback_func;
wasmtime_instance_t instance;
wasm_trap_t* trap = NULL;
error = wasmtime_instance_new(store, module, &imports_vec, &instance, &trap);
if (!instance)
error = wasmtime_instance_new(context, module, imports, 1, &instance, &trap);
if (error != NULL || trap != NULL)
exit_with_error("failed to instantiate", error, trap);
wasm_func_delete(callback_func);
wasmtime_module_delete(module);
// 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;
}
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);
wasmtime_extern_t run;
bool ok = wasmtime_instance_export_get(context, &instance, "g", 1, &run);
assert(ok);
assert(run.kind == WASMTIME_EXTERN_FUNC);
// Call.
printf("Calling export...\n");
wasm_val_t args[2] = { WASM_I32_VAL(1), WASM_I64_VAL(2) };
wasm_val_t results[2];
wasm_val_vec_t args_vec = WASM_ARRAY_VEC(args);
wasm_val_vec_t results_vec = WASM_ARRAY_VEC(results);
error = wasmtime_func_call(run_func, &args_vec, &results_vec, &trap);
wasmtime_val_t args[2];
args[0].kind = WASMTIME_I32;
args[0].of.i32 = 1;
args[1].kind = WASMTIME_I64;
args[1].of.i64 = 2;
wasmtime_val_t results[2];
error = wasmtime_func_call(context, &run.of.func, args, 2, results, 2, &trap);
if (error != NULL || trap != NULL)
exit_with_error("failed to call run", error, trap);
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].kind == WASMTIME_I64);
assert(results[0].of.i64 == 2);
assert(results[1].kind == WASM_I32);
assert(results[1].kind == WASMTIME_I32);
assert(results[1].of.i32 == 1);
// Shut down.
printf("Shutting down...\n");
wasm_store_delete(store);
wasmtime_store_delete(store);
wasm_engine_delete(engine);
// All done.

View File

@@ -15,38 +15,30 @@ fn main() -> Result<()> {
println!("Initializing...");
let engine = Engine::default();
let store = Store::new(&engine);
let mut store = Store::new(&engine, ());
// Compile.
println!("Compiling module...");
let module = Module::from_file(&engine, "examples/multi.wat")?;
// Create external print functions.
// Create a host function which takes multiple parameters and returns
// multiple results.
println!("Creating callback...");
let callback_type = FuncType::new(
[ValType::I32, ValType::I64].iter().cloned(),
[ValType::I64, ValType::I32].iter().cloned(),
);
let callback_func = Func::new(&store, callback_type, |_, args, results| {
println!("Calling back...");
println!("> {} {}", args[0].unwrap_i32(), args[1].unwrap_i64());
results[0] = Val::I64(args[1].unwrap_i64() + 1);
results[1] = Val::I32(args[0].unwrap_i32() + 1);
Ok(())
let callback_func = Func::wrap(&mut store, |a: i32, b: i64| -> (i64, i32) {
(b + 1, a + 1)
});
// Instantiate.
println!("Instantiating module...");
let instance = Instance::new(&store, &module, &[callback_func.into()])?;
let instance = Instance::new(&mut store, &module, &[callback_func.into()])?;
// Extract exports.
println!("Extracting export...");
let g = instance.get_typed_func::<(i32, i64), (i64, i32)>("g")?;
let g = instance.get_typed_func::<(i32, i64), (i64, i32), _>(&mut store, "g")?;
// Call `$g`.
println!("Calling export \"g\"...");
let (a, b) = g.call((1, 3))?;
let (a, b) = g.call(&mut store, (1, 3))?;
println!("Printing result...");
println!("> {} {}", a, b);
@@ -60,9 +52,10 @@ fn main() -> Result<()> {
.get_typed_func::<
(i64, i64, i64, i64, i64, i64, i64, i64, i64, i64),
(i64, i64, i64, i64, i64, i64, i64, i64, i64, i64),
_,
>
("round_trip_many")?;
let results = round_trip_many.call((0, 1, 2, 3, 4, 5, 6, 7, 8, 9))?;
(&mut store, "round_trip_many")?;
let results = round_trip_many.call(&mut store, (0, 1, 2, 3, 4, 5, 6, 7, 8, 9))?;
println!("Printing result...");
println!("> {:?}", results);
@@ -71,6 +64,9 @@ fn main() -> Result<()> {
Ok(())
}
// Note that this example is not supported in the off-by-default feature of the
// old x86 compiler backend for Cranelift. Wasmtime's default configuration
// supports this example, however.
#[cfg(feature = "old-x86-backend")]
fn main() -> Result<()> {
Ok(())

View File

@@ -26,7 +26,13 @@ to tweak the `-lpthread` and such annotations as well as the name of the
static void exit_with_error(const char *message, wasmtime_error_t *error, wasm_trap_t *trap);
static wasm_trap_t* hello_callback(const wasm_val_vec_t* args, wasm_val_vec_t* results) {
static wasm_trap_t* hello_callback(
void *env,
wasmtime_caller_t *caller,
const wasmtime_val_t* args,
size_t nargs,
wasmtime_val_t* results,
size_t nresults) {
printf("Calling back...\n");
printf("> Hello World!\n");
return NULL;
@@ -53,7 +59,7 @@ int serialize(wasm_byte_vec_t* buffer) {
// Parse the wat into the binary wasm format
wasm_byte_vec_t wasm;
wasmtime_error_t *error = wasmtime_wat2wasm(&wat, &wasm);
wasmtime_error_t *error = wasmtime_wat2wasm(wat.data, wat.size, &wasm);
if (error != NULL)
exit_with_error("failed to parse wat", error, NULL);
wasm_byte_vec_delete(&wat);
@@ -61,13 +67,13 @@ int serialize(wasm_byte_vec_t* buffer) {
// Now that we've got our binary webassembly we can compile our module
// and serialize into buffer.
printf("Compiling and serializing module...\n");
wasm_module_t *module = NULL;
error = wasmtime_module_new(engine, &wasm, &module);
wasmtime_module_t *module = NULL;
error = wasmtime_module_new(engine, (uint8_t*)wasm.data, wasm.size, &module);
wasm_byte_vec_delete(&wasm);
if (error != NULL)
exit_with_error("failed to compile module", error, NULL);
error = wasmtime_module_serialize(module, buffer);
wasm_module_delete(module);
wasmtime_module_delete(module);
if (error != NULL)
exit_with_error("failed to serialize module", error, NULL);
@@ -87,13 +93,14 @@ int deserialize(wasm_byte_vec_t* buffer) {
// 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);
wasmtime_store_t *store = wasmtime_store_new(engine, NULL, NULL);
assert(store != NULL);
wasmtime_context_t *context = wasmtime_store_context(store);
// Deserialize compiled module.
printf("Deserialize module...\n");
wasm_module_t *module = NULL;
wasmtime_error_t *error = wasmtime_module_deserialize(engine, buffer, &module);
wasmtime_module_t *module = NULL;
wasmtime_error_t *error = wasmtime_module_deserialize(engine, (uint8_t*) buffer->data, buffer->size, &module);
if (error != NULL)
exit_with_error("failed to compile module", error, NULL);
@@ -102,7 +109,8 @@ int deserialize(wasm_byte_vec_t* buffer) {
// 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);
wasmtime_func_t hello;
wasmtime_func_new(context, hello_ty, hello_callback, NULL, NULL, &hello);
// With our callback function we can now instantiate the compiled module,
// giving us an instance we can then execute exports from. Note that
@@ -110,36 +118,31 @@ int deserialize(wasm_byte_vec_t* buffer) {
// to handle that here too.
printf("Instantiating module...\n");
wasm_trap_t *trap = NULL;
wasm_instance_t *instance = NULL;
wasm_extern_t *imports[] = { wasm_func_as_extern(hello) };
wasm_extern_vec_t imports_vec = WASM_ARRAY_VEC(imports);
error = wasmtime_instance_new(store, module, &imports_vec, &instance, &trap);
if (instance == NULL)
wasmtime_instance_t instance;
wasmtime_extern_t imports[1];
imports[0].kind = WASMTIME_EXTERN_FUNC;
imports[0].of.func = hello;
error = wasmtime_instance_new(context, module, imports, 1, &instance, &trap);
if (error != NULL || trap != NULL)
exit_with_error("failed to instantiate", error, trap);
wasmtime_module_delete(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);
wasmtime_extern_t run;
bool ok = wasmtime_instance_export_get(context, &instance, "run", 3, &run);
assert(ok);
assert(run.kind == WASMTIME_EXTERN_FUNC);
// And call it!
printf("Calling export...\n");
wasm_val_vec_t args_vec = WASM_EMPTY_VEC;
wasm_val_vec_t results_vec = WASM_EMPTY_VEC;
error = wasmtime_func_call(run, &args_vec, &results_vec, &trap);
error = wasmtime_func_call(context, &run.of.func, NULL, 0, NULL, 0, &trap);
if (error != NULL || trap != NULL)
exit_with_error("failed to call function", error, trap);
// Clean up after ourselves at this point
printf("All finished!\n");
wasm_extern_vec_delete(&externs);
wasm_instance_delete(instance);
wasm_module_delete(module);
wasm_store_delete(store);
wasmtime_store_delete(store);
wasm_engine_delete(engine);
return 0;
}

View File

@@ -27,7 +27,7 @@ fn deserialize(buffer: &[u8]) -> Result<()> {
// `Store` structure. Note that you can also tweak configuration settings
// with a `Config` and an `Engine` if desired.
println!("Initializing...");
let store = Store::default();
let mut store: Store<()> = Store::default();
// Compile the wasm binary into an in-memory instance of a `Module`. Note
// that this is `unsafe` because it is our responsibility for guaranteeing
@@ -39,7 +39,7 @@ fn deserialize(buffer: &[u8]) -> Result<()> {
// Here we handle the imports of the module, which in this case is our
// `HelloCallback` type and its associated implementation of `Callback.
println!("Creating callback...");
let hello_func = Func::wrap(&store, || {
let hello_func = Func::wrap(&mut store, || {
println!("Calling back...");
println!("> Hello World!");
});
@@ -49,15 +49,15 @@ fn deserialize(buffer: &[u8]) -> Result<()> {
// Note that this is where the wasm `start` function, if any, would run.
println!("Instantiating module...");
let imports = [hello_func.into()];
let instance = Instance::new(&store, &module, &imports)?;
let instance = Instance::new(&mut store, &module, &imports)?;
// Next we poke around a bit to extract the `run` function from the module.
println!("Extracting export...");
let run = instance.get_typed_func::<(), ()>("run")?;
let run = instance.get_typed_func::<(), (), _>(&mut store, "run")?;
// And last but not least we can call it!
println!("Calling export...");
run.call(())?;
run.call(&mut store, ())?;
println!("Done.");
Ok(())

View File

@@ -124,11 +124,11 @@ int main(int argc, const char *argv[]) {
// Parse the wat into the binary wasm format
wasm_byte_vec_t binary;
wasmtime_error_t *error = wasmtime_wat2wasm(&wat, &binary);
wasmtime_error_t *error = wasmtime_wat2wasm(wat.data, wat.size, &binary);
if (error != NULL)
exit_with_error("failed to parse wat", error, NULL);
wasm_byte_vec_delete(&wat);
// Compile and share.
own wasm_store_t* store = wasm_store_new(engine);
own wasm_module_t* module = wasm_module_new(store, &binary);

View File

@@ -1,6 +1,10 @@
//! This program is an example of how Wasmtime can be used with multithreaded
//! runtimes and how various types and structures can be shared across threads.
// You can execute this example with `cargo run --example threads`
use anyhow::Result;
use std::sync::Arc;
use std::thread;
use std::time;
use wasmtime::*;
@@ -8,57 +12,59 @@ use wasmtime::*;
const N_THREADS: i32 = 10;
const N_REPS: i32 = 3;
fn run(engine: &Engine, module: Module, id: i32) -> Result<()> {
let store = Store::new(&engine);
// Create external print functions.
println!("Creating callback...");
let callback_func = Func::wrap(&store, |arg: i32| {
println!("> Thread {} is running", arg);
});
let id_type = GlobalType::new(ValType::I32, Mutability::Const);
let id_global = Global::new(&store, id_type, Val::I32(id))?;
// Instantiate.
println!("Instantiating module...");
let instance = Instance::new(&store, &module, &[callback_func.into(), id_global.into()])?;
// Extract exports.
println!("Extracting export...");
let g = instance.get_typed_func::<(), ()>("run")?;
for _ in 0..N_REPS {
thread::sleep(time::Duration::from_millis(100));
// Call `$run`.
drop(g.call(())?);
}
Ok(())
}
fn main() -> Result<()> {
println!("Initializing...");
// Initialize global per-process state. This state will be shared amonst all
// threads. Notably this includes the compiled module as well as a `Linker`,
// which contains all our host functions we want to define.
let engine = Engine::default();
// Compile.
println!("Compiling module...");
let module = Module::from_file(&engine, "examples/threads.wat")?;
let mut linker = Linker::new(&engine);
linker.func_wrap("global", "hello", || {
println!("> Hello from {:?}", thread::current().id());
})?;
let linker = Arc::new(linker); // "finalize" the linker
let mut children = Vec::new();
for id in 0..N_THREADS {
let engine = engine.clone();
let module = module.clone();
children.push(thread::spawn(move || {
run(&engine, module, id).expect("Success");
}));
}
// Share this global state amongst a set of threads, each of which will
// create stores and execute instances.
let children = (0..N_THREADS)
.map(|_| {
let engine = engine.clone();
let module = module.clone();
let linker = linker.clone();
thread::spawn(move || {
run(&engine, &module, &linker).expect("Success");
})
})
.collect::<Vec<_>>();
for (i, child) in children.into_iter().enumerate() {
if let Err(_) = child.join() {
println!("Thread #{} errors", i);
}
for child in children {
child.join().unwrap();
}
Ok(())
}
fn run(engine: &Engine, module: &Module, linker: &Linker<()>) -> Result<()> {
// Each sub-thread we have starting out by instantiating the `module`
// provided into a fresh `Store`.
println!("Instantiating module...");
let mut store = Store::new(&engine, ());
let instance = linker.instantiate(&mut store, module)?;
let run = instance.get_typed_func::<(), (), _>(&mut store, "run")?;
println!("Executing...");
for _ in 0..N_REPS {
run.call(&mut store, ())?;
thread::sleep(time::Duration::from_millis(100));
}
// Also note that that a `Store` can also move between threads:
println!("> Moving {:?} to a new thread", thread::current().id());
let child = thread::spawn(move || run.call(&mut store, ()));
child.join().unwrap()?;
Ok(())
}

View File

@@ -1,5 +1,4 @@
(module
(func $message (import "" "hello") (param i32))
(global $id (import "" "id") i32)
(func (export "run") (call $message (global.get $id)))
(func $hello (import "global" "hello"))
(func (export "run") (call $hello))
)

View File

@@ -1,11 +1,11 @@
use anyhow::{anyhow, Error};
use std::future::Future;
use anyhow::Error;
use std::sync::Arc;
use tokio::time::Duration;
use wasmtime::{Config, Engine, Linker, Module, Store};
// For this example we want to use the async version of wasmtime_wasi.
// Notably, this version of wasi uses a scheduler that will async yield
// when sleeping in `poll_oneoff`.
use wasmtime_wasi::tokio::{Wasi, WasiCtxBuilder};
use wasmtime_wasi::{tokio::WasiCtxBuilder, WasiCtx};
#[tokio::main]
async fn main() -> Result<(), Error> {
@@ -42,6 +42,7 @@ async fn main() -> Result<(), Error> {
struct Environment {
engine: Engine,
module: Module,
linker: Arc<Linker<WasiCtx>>,
}
impl Environment {
@@ -52,13 +53,21 @@ impl Environment {
config.async_support(true);
config.consume_fuel(true);
// Install the host functions for `Wasi`.
Wasi::add_to_config(&mut config);
let engine = Engine::new(&config)?;
let module = Module::from_file(&engine, "target/wasm32-wasi/debug/tokio-wasi.wasm")?;
Ok(Self { engine, module })
// A `Linker` is shared in the environment amongst all stores, and this
// linker is used to instantiate the `module` above. This example only
// adds WASI functions to the linker, notably the async versions built
// on tokio.
let mut linker = Linker::new(&engine);
wasmtime_wasi::tokio::add_to_linker(&mut linker, |cx| cx)?;
Ok(Self {
engine,
module,
linker: Arc::new(linker),
})
}
}
@@ -76,87 +85,29 @@ impl Inputs {
}
}
fn run_wasm(inputs: Inputs) -> impl Future<Output = Result<(), Error>> {
use std::pin::Pin;
use std::task::{Context, Poll};
// IMPORTANT: The current wasmtime API is very challenging to use safely
// on an async runtime. This RFC describes a redesign of the API that will
// resolve these safety issues:
// https://github.com/alexcrichton/rfcs-2/blob/new-api/accepted/new-api.md
// This is a "marker type future" which simply wraps some other future and
// the only purpose it serves is to forward the implementation of `Future`
// as well as have `unsafe impl Send` for itself, regardless of the
// underlying type.
//
// Note that the qctual safety of this relies on the fact that the inputs
// here are `Send`, the outputs (just () in this case) are `Send`, and the
// future itself is safe tu resume on other threads.
//
// For an in-depth discussion of the safety of moving Wasmtime's `Store`
// between threads, see
// https://docs.wasmtime.dev/examples-rust-multithreading.html.
struct UnsafeSend<T>(T);
// Note the `where` cause specifically ensures the output of the future to
// be `Send` is required. We specifically dont require `T` to be `Send`
// since that's the whole point of this function, but we require that
// everything used to construct `T` is `Send` below.
unsafe impl<T> Send for UnsafeSend<T>
where
T: Future,
T::Output: Send,
{
}
impl<T: Future> Future for UnsafeSend<T> {
type Output = T::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T::Output> {
// Note that this `unsafe` is unrelated to `Send`, it only has to do with "pin
// projection" and should be safe since it's all we do with the `Pin`.
unsafe { self.map_unchecked_mut(|p| &mut p.0).poll(cx) }
}
}
// This is a crucial assertion that needs to be here. The compiler
// typically checks this for us, but do to our `UnsafeSend` type the
// compiler isn't automatically checking this. The assertion here must
// assert that all arguments to this function are indeed `Send` because
// we're closing over them and sending them to other threads. It's only
// everything *internal* to the computation of this function which doesn't
// have to be `Send`.
fn assert_send<T: Send>(_t: &T) {}
assert_send(&inputs);
// Wrap up the `_run_wasm` function, which is *not* `Send`, but is safe to
// resume on other threads.
UnsafeSend(_run_wasm(inputs))
}
async fn _run_wasm(inputs: Inputs) -> Result<(), Error> {
let store = Store::new(&inputs.env.engine);
async fn run_wasm(inputs: Inputs) -> Result<(), Error> {
let wasi = WasiCtxBuilder::new()
// Let wasi print to this process's stdout.
.inherit_stdout()
// Set an environment variable so the wasm knows its name.
.env("NAME", &inputs.name)?
.build();
let mut store = Store::new(&inputs.env.engine, wasi);
// WebAssembly execution will be paused for an async yield every time it
// consumes 10000 fuel. Fuel will be refilled u32::MAX times.
store.out_of_fuel_async_yield(u32::MAX, 10000);
Wasi::set_context(
&store,
WasiCtxBuilder::new()
// Let wasi print to this process's stdout.
.inherit_stdout()
// Set an environment variable so the wasm knows its name.
.env("NAME", &inputs.name)?
.build(),
)
.map_err(|_| anyhow!("setting wasi context"))?;
let linker = Linker::new(&store);
// Instantiate
let instance = linker.instantiate_async(&inputs.env.module).await?;
// Instantiate into our own unique store using the shared linker, afterwards
// acquiring the `_start` function for the module and executing it.
let instance = inputs
.env
.linker
.instantiate_async(&mut store, &inputs.env.module)
.await?;
instance
.get_typed_func::<(), ()>("_start")?
.call_async(())
.get_typed_func::<(), (), _>(&mut store, "_start")?
.call_async(&mut store, ())
.await?;
Ok(())

View File

@@ -28,12 +28,18 @@ to tweak the `-lpthread` and such annotations.
static void exit_with_error(const char *message, wasmtime_error_t *error, 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);
wasmtime_store_t *store = wasmtime_store_new(engine, NULL, NULL);
assert(store != NULL);
wasmtime_context_t *context = wasmtime_store_context(store);
// Create a linker with WASI functions defined
wasmtime_linker_t *linker = wasmtime_linker_new(engine);
wasmtime_error_t *error = wasmtime_linker_define_wasi(linker);
if (error != NULL)
exit_with_error("failed to link wasi", error, NULL);
wasm_byte_vec_t wasm;
// Load our input file to parse it next
@@ -53,8 +59,8 @@ int main() {
fclose(file);
// Compile our modules
wasm_module_t *module = NULL;
wasmtime_error_t *error = wasmtime_module_new(engine, &wasm, &module);
wasmtime_module_t *module = NULL;
error = wasmtime_module_new(engine, (uint8_t*)wasm.data, wasm.size, &module);
if (!module)
exit_with_error("failed to compile module", error, NULL);
wasm_byte_vec_delete(&wasm);
@@ -68,39 +74,28 @@ int main() {
wasi_config_inherit_stdout(wasi_config);
wasi_config_inherit_stderr(wasi_config);
wasm_trap_t *trap = NULL;
wasi_instance_t *wasi = wasi_instance_new(store, "wasi_snapshot_preview1", wasi_config, &trap);
if (wasi == NULL)
exit_with_error("failed to instantiate WASI", NULL, trap);
wasmtime_linker_t *linker = wasmtime_linker_new(store);
error = wasmtime_linker_define_wasi(linker, wasi);
error = wasmtime_context_set_wasi(context, wasi_config);
if (error != NULL)
exit_with_error("failed to link wasi", error, NULL);
exit_with_error("failed to instantiate WASI", error, NULL);
// Instantiate the module
wasm_name_t empty;
wasm_name_new_from_string(&empty, "");
wasm_instance_t *instance = NULL;
error = wasmtime_linker_module(linker, &empty, module);
error = wasmtime_linker_module(linker, context, "", 0, module);
if (error != NULL)
exit_with_error("failed to instantiate module", error, NULL);
// Run it.
wasm_func_t* func;
wasmtime_linker_get_default(linker, &empty, &func);
wasmtime_func_t func;
error = wasmtime_linker_get_default(linker, context, "", 0, &func);
if (error != NULL)
exit_with_error("failed to locate default export for module", error, NULL);
wasm_val_vec_t args_vec = WASM_EMPTY_VEC;
wasm_val_vec_t results_vec = WASM_EMPTY_VEC;
error = wasmtime_func_call(func, &args_vec, &results_vec, &trap);
if (error != NULL)
error = wasmtime_func_call(context, &func, NULL, 0, NULL, 0, &trap);
if (error != NULL || trap != NULL)
exit_with_error("error calling default export", error, trap);
// Clean up after ourselves at this point
wasm_name_delete(&empty);
wasm_module_delete(module);
wasm_store_delete(store);
wasmtime_module_delete(module);
wasmtime_store_delete(store);
wasm_engine_delete(engine);
return 0;
}

View File

@@ -5,38 +5,30 @@
use anyhow::Result;
use wasmtime::*;
use wasmtime_wasi::sync::{Wasi, WasiCtxBuilder};
use wasmtime_wasi::sync::WasiCtxBuilder;
fn main() -> Result<()> {
tracing_subscriber::FmtSubscriber::builder()
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
.with_ansi(true)
.init();
// Define the WASI functions globally on the `Config`.
let mut config = Config::default();
Wasi::add_to_config(&mut config);
let engine = Engine::default();
let mut linker = Linker::new(&engine);
wasmtime_wasi::add_to_linker(&mut linker, |s| s)?;
let store = Store::new(&Engine::new(&config)?);
// Set the WASI context in the store; all instances in the store share this context.
// `WasiCtxBuilder` provides a number of ways to configure what the target program
// will have access to.
assert!(Wasi::set_context(
&store,
WasiCtxBuilder::new()
.inherit_stdio()
.inherit_args()?
.build()
)
.is_ok());
let mut linker = Linker::new(&store);
// Create a WASI context and put it in a Store; all instances in the store
// share this context. `WasiCtxBuilder` provides a number of ways to
// configure what the target program will have access to.
let wasi = WasiCtxBuilder::new()
.inherit_stdio()
.inherit_args()?
.build();
let mut store = Store::new(&engine, wasi);
// Instantiate our module with the imports we've created, and run it.
let module = Module::from_file(store.engine(), "target/wasm32-wasi/debug/wasi.wasm")?;
linker.module("", &module)?;
linker.get_default("")?.typed::<(), ()>()?.call(())?;
let module = Module::from_file(&engine, "target/wasm32-wasi/debug/wasi.wasm")?;
linker.module(&mut store, "", &module)?;
linker
.get_default(&mut store, "")?
.typed::<(), (), _>(&store)?
.call(&mut store, ())?;
Ok(())
}