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

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