diff --git a/Makefile b/Makefile index 764f411a..15505db0 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ OBJ := $(SRC:.c=.o) # define default flags, and override to append mandatory flags CFLAGS ?= -O3 -Wall -Wextra -Werror -override CFLAGS += -std=gnu99 -fPIC -Ilib/src -Ilib/include +override CFLAGS += -std=gnu99 -fPIC -Ilib/src -Ilib/src/wasm -Ilib/include # ABI versioning SONAME_MAJOR := 0 diff --git a/lib/binding_rust/bindings.rs b/lib/binding_rust/bindings.rs index de986671..464aa8d7 100644 --- a/lib/binding_rust/bindings.rs +++ b/lib/binding_rust/bindings.rs @@ -864,40 +864,29 @@ pub type TSWasmEngine = wasm_engine_t; pub struct TSWasmStore { _unused: [u8; 0], } -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct TSWasmLanguage { - _unused: [u8; 0], -} extern "C" { pub fn ts_wasm_store_new(engine: *mut TSWasmEngine) -> *mut TSWasmStore; } extern "C" { pub fn ts_wasm_store_delete(arg1: *mut TSWasmStore); } -extern "C" { - pub fn ts_wasm_language_delete(arg1: *const TSWasmLanguage); -} extern "C" { pub fn ts_wasm_store_load_language( arg1: *mut TSWasmStore, name: *const ::std::os::raw::c_char, wasm: *const ::std::os::raw::c_char, wasm_len: u32, - ) -> *mut TSWasmLanguage; + ) -> *const TSLanguage; } extern "C" { - pub fn ts_wasm_store_add_language(arg1: *mut TSWasmStore, arg2: *const TSWasmLanguage); -} -extern "C" { - pub fn ts_parser_set_wasm_language(arg1: *mut TSParser, arg2: *const TSWasmLanguage); -} -extern "C" { - pub fn ts_parser_wasm_language(arg1: *mut TSParser) -> *const TSWasmLanguage; + pub fn ts_language_is_wasm(arg1: *const TSLanguage) -> bool; } extern "C" { pub fn ts_parser_set_wasm_store(arg1: *mut TSParser, arg2: *mut TSWasmStore); } +extern "C" { + pub fn ts_parser_wasm_store(arg1: *mut TSParser) -> *mut TSWasmStore; +} extern "C" { #[doc = " Set the allocation functions used by the library."] #[doc = ""] diff --git a/lib/binding_rust/build.rs b/lib/binding_rust/build.rs index 247ec04c..eb16a29d 100644 --- a/lib/binding_rust/build.rs +++ b/lib/binding_rust/build.rs @@ -35,6 +35,7 @@ fn main() { .flag_if_supported("-std=c99") .flag_if_supported("-Wno-unused-parameter") .include(src_path) + .include(src_path.join("wasm")) .include("include") .file(src_path.join("lib.c")) .compile("tree-sitter"); diff --git a/lib/binding_rust/wasm_language.rs b/lib/binding_rust/wasm_language.rs index e5314302..1e7f21d7 100644 --- a/lib/binding_rust/wasm_language.rs +++ b/lib/binding_rust/wasm_language.rs @@ -1,7 +1,5 @@ -use crate::{LanguageError, Parser}; - -use super::ffi; -use std::{ffi::CString, os::raw::c_char}; +use crate::{ffi, Language, LanguageError, Parser}; +use std::{ffi::CString, mem, os::raw::c_char}; pub use wasmtime; #[cfg(feature = "wasm")] @@ -16,18 +14,22 @@ pub struct wasm_engine_t { } pub struct WasmStore(*mut ffi::TSWasmStore); -pub struct WasmLanguage(*const ffi::TSWasmLanguage); impl WasmStore { pub fn new(engine: wasmtime::Engine) -> Self { - let mut c_engine = wasm_engine_t { engine }; - let c_engine = &mut c_engine as *mut _; - WasmStore(unsafe { ffi::ts_wasm_store_new(c_engine as *mut _) }) + let mut c_engine = Box::new(wasm_engine_t { + engine: engine.clone(), + }); + let result = WasmStore(unsafe { + ffi::ts_wasm_store_new(c_engine.as_mut() as *mut wasm_engine_t as *mut _) + }); + mem::forget(c_engine); + result } - pub fn load_language(&mut self, name: &str, bytes: &[u8]) -> WasmLanguage { + pub fn load_language(&mut self, name: &str, bytes: &[u8]) -> Language { let name = CString::new(name).unwrap(); - WasmLanguage(unsafe { + Language(unsafe { ffi::ts_wasm_store_load_language( self.0, name.as_ptr(), @@ -38,34 +40,19 @@ impl WasmStore { } } -impl Parser { - pub fn wasm_language(&self) -> Option { - let language = unsafe { ffi::ts_parser_wasm_language(self.0.as_ptr()) }; - if language.is_null() { - None - } else { - Some(WasmLanguage(language)) - } - } - - pub fn set_wasm_language(&mut self, language: WasmLanguage) -> Result<(), LanguageError> { - unsafe { - ffi::ts_parser_set_wasm_language(self.0.as_ptr(), language.0); - } - Ok(()) - } - - pub fn set_wasm_store(&mut self, language: WasmStore) -> Result<(), LanguageError> { - unsafe { - ffi::ts_parser_set_wasm_store(self.0.as_ptr(), language.0); - } - Ok(()) +impl Language { + pub fn is_wasm(&self) -> bool { + unsafe { ffi::ts_language_is_wasm(self.0) } } } -impl Drop for WasmLanguage { - fn drop(&mut self) { - unsafe { ffi::ts_wasm_language_delete(self.0) }; +impl Parser { + pub fn set_wasm_store(&mut self, store: WasmStore) -> Result<(), LanguageError> { + unsafe { + ffi::ts_parser_set_wasm_store(self.0.as_ptr(), store.0); + } + mem::forget(store); + Ok(()) } } diff --git a/lib/compile_flags.txt b/lib/compile_flags.txt index e6043ca3..f6a5774a 100644 --- a/lib/compile_flags.txt +++ b/lib/compile_flags.txt @@ -1,3 +1,4 @@ -std=c99 --Isrc --Iinclude \ No newline at end of file +-Isrc/wasm +-Iinclude +-D TREE_SITTER_FEATURE_WASM \ No newline at end of file diff --git a/lib/include/tree_sitter/api.h b/lib/include/tree_sitter/api.h index d3ed8738..6824f032 100644 --- a/lib/include/tree_sitter/api.h +++ b/lib/include/tree_sitter/api.h @@ -943,23 +943,24 @@ uint32_t ts_language_version(const TSLanguage *); typedef struct wasm_engine_t TSWasmEngine; typedef struct TSWasmStore TSWasmStore; -typedef struct TSWasmLanguage TSWasmLanguage; TSWasmStore *ts_wasm_store_new(TSWasmEngine *engine); -void ts_wasm_store_delete(TSWasmStore *); -void ts_wasm_language_delete(const TSWasmLanguage *); -TSWasmLanguage *ts_wasm_store_load_language( +void ts_wasm_store_delete(TSWasmStore *); + +const TSLanguage *ts_wasm_store_load_language( TSWasmStore *, const char *name, const char *wasm, uint32_t wasm_len ); -void ts_wasm_store_add_language(TSWasmStore *, const TSWasmLanguage *); -void ts_parser_set_wasm_language(TSParser *, const TSWasmLanguage *); -const TSWasmLanguage *ts_parser_wasm_language(TSParser *); + +bool ts_language_is_wasm(const TSLanguage *); + void ts_parser_set_wasm_store(TSParser *, TSWasmStore *); +TSWasmStore *ts_parser_wasm_store(TSParser *); + /**********************************/ /* Section - Global Configuration */ /**********************************/ diff --git a/lib/src/parser.c b/lib/src/parser.c index 9a8b5fc1..d6b0a654 100644 --- a/lib/src/parser.c +++ b/lib/src/parser.c @@ -89,7 +89,6 @@ struct TSParser { Stack *stack; SubtreePool tree_pool; const TSLanguage *language; - const TSWasmLanguage *wasm_language; TSWasmStore *wasm_store; ReduceActionSet reduce_actions; Subtree finished_tree; @@ -487,7 +486,7 @@ static Subtree ts_parser__lex( ); ts_lexer_start(&self->lexer); found_token = false; - if (self->wasm_language) { + if (ts_language_is_wasm(self->language)) { found_token = ts_wasm_store_run_main_lex_function(self->wasm_store, lex_mode.lex_state); } else { found_token = self->language->lex_fn(&self->lexer.data, lex_mode.lex_state); @@ -553,7 +552,7 @@ static Subtree ts_parser__lex( ts_lexer_reset(&self->lexer, self->lexer.token_start_position); ts_lexer_start(&self->lexer); - if (self->wasm_language) { + if (ts_language_is_wasm(self->language)) { is_keyword = ts_wasm_store_run_keyword_lex_function(self->wasm_store, 0); } else { is_keyword = self->language->keyword_lex_fn(&self->lexer.data, 0); @@ -1812,14 +1811,6 @@ bool ts_parser_set_language(TSParser *self, const TSLanguage *language) { } self->language = language; - - #ifdef TREE_SITTER_FEATURE_WASM - if (self->wasm_language) { - ts_wasm_language_delete(self->wasm_language); - self->wasm_language = NULL; - } - #endif - ts_parser_reset(self); return true; } @@ -1900,8 +1891,12 @@ TSTree *ts_parser_parse( ) { if (!self->language || !input.read) return NULL; - if (self->wasm_store) { - ts_wasm_store_start(self->wasm_store, &self->lexer.data, self->wasm_language); + if (ts_language_is_wasm(self->language)) { + if (self->wasm_store) { + ts_wasm_store_start(self->wasm_store, &self->lexer.data, self->language); + } else { + return NULL; + } } ts_lexer_set_input(&self->lexer, input); @@ -2023,28 +2018,8 @@ TSTree *ts_parser_parse_string_encoding( }); } -#ifdef TREE_SITTER_FEATURE_WASM - -void ts_parser_set_wasm_language(TSParser *self, const TSWasmLanguage *language) { - if (language) { - self->language = language->language; - ts_wasm_language_retain(language); - } - self->wasm_language = language; - ts_parser_reset(self); -} - -const TSWasmLanguage *ts_parser_wasm_language(TSParser *self) { - if (self->wasm_language) { - ts_wasm_language_retain(self->wasm_language); - } - return self->wasm_language; -} - void ts_parser_set_wasm_store(TSParser *self, TSWasmStore *store) { self->wasm_store = store; } -#endif // TREE_SITTER_FEATURE_WASM - #undef LOG diff --git a/lib/src/wasm.c b/lib/src/wasm.c index 2d728765..ab7aded6 100644 --- a/lib/src/wasm.c +++ b/lib/src/wasm.c @@ -1,13 +1,41 @@ +#include +#include #include "tree_sitter/api.h" -#include "./wasm/wasm.h" #include "./alloc.h" #include "./language.h" #include "./array.h" #include "./atomic.h" #include "./lexer.h" #include "./wasm.h" +#include "./lexer.h" -static volatile uint32_t NEXT_LANGUAGE_ID; +typedef struct { + wasmtime_module_t *module; + uint32_t language_id; + const char *name; + char *symbol_name_buffer; + char *field_name_buffer; +} LanguageWasmModule; + +typedef struct { + uint32_t language_id; + wasmtime_instance_t instance; + uint32_t main_lex_fn_index; + uint32_t keyword_lex_fn_index; + uint32_t external_scan_index; +} LanguageWasmInstance; + +struct TSWasmStore { + wasm_engine_t *engine; + wasmtime_store_t *store; + wasmtime_table_t function_table; + wasmtime_memory_t memory; + TSLexer *current_lexer; + LanguageWasmInstance *current_instance; + Array(LanguageWasmInstance) language_instances; + uint32_t current_memory_offset; + uint32_t current_function_table_offset; +}; typedef Array(char) StringData; @@ -58,36 +86,48 @@ typedef struct { int32_t get_column; int32_t is_at_included_range_start; int32_t eof; - void *native_lexer_address; } LexerInWasmMemory; +static volatile uint32_t NEXT_LANGUAGE_ID; +static const uint32_t LEXER_ADDRESS = 32; +static const uint32_t LEXER_END_ADDRESS = LEXER_ADDRESS + sizeof(LexerInWasmMemory); + static wasm_trap_t *advance_callback( void *env, - const wasm_val_vec_t *args, - wasm_val_vec_t *results + wasmtime_caller_t *caller, + const wasmtime_val_t *args, + size_t arg_count, + wasmtime_val_t *results, + size_t result_count ) { - assert(args->size == 2); - assert(args->data[0].kind == WASM_I32); - assert(args->data[1].kind == WASM_I32); + wasmtime_context_t *context = wasmtime_caller_context(caller); + assert(arg_count == 2); + assert(args[0].kind == WASMTIME_I32); + assert(args[1].kind == WASMTIME_I32); + int32_t lexer_address = args[0].of.i32; TSWasmStore *store = env; TSLexer *lexer = store->current_lexer; - int32_t lexer_address = args->data[0].of.i32; - bool skip = args->data[1].of.i32; - + bool skip = args[1].of.i32; lexer->advance(lexer, skip); - char *memory = wasm_memory_data(store->memory); + + uint8_t *memory = wasmtime_memory_data(context, &store->memory); memcpy(&memory[lexer_address], &lexer->lookahead, sizeof(lexer->lookahead)); return NULL; } static wasm_trap_t *mark_end_callback( void *env, - const wasm_val_vec_t *args, - wasm_val_vec_t *results + wasmtime_caller_t *caller, + const wasmtime_val_t *args, + size_t arg_count, + wasmtime_val_t *results, + size_t result_count ) { - assert(args->size == 1); - assert(args->data[0].kind == WASM_I32); + assert(arg_count == 1); + assert(args[0].kind == WASM_I32); + int32_t lexer_address = args[0].of.i32; + TSWasmStore *store = env; TSLexer *lexer = store->current_lexer; lexer->mark_end(lexer); @@ -96,48 +136,73 @@ static wasm_trap_t *mark_end_callback( static wasm_trap_t *get_column_callback( void *env, - const wasm_val_vec_t *args, - wasm_val_vec_t *results + wasmtime_caller_t *caller, + const wasmtime_val_t *args, + size_t arg_count, + wasmtime_val_t *results, + size_t result_count ) { - assert(args->size == 1); - assert(args->data[0].kind == WASM_I32); + assert(arg_count == 1); + assert(args[0].kind == WASM_I32); + int32_t lexer_address = args[0].of.i32; + TSWasmStore *store = env; TSLexer *lexer = store->current_lexer; - wasm_val_t result = WASM_I32_VAL(lexer->get_column(lexer)); - results->data[0] = result; + uint32_t result = lexer->get_column(lexer); + results[0] = (wasmtime_val_t) { + .kind = WASMTIME_I32, + .of.i32 = result + }; return NULL; } static wasm_trap_t *is_at_included_range_start_callback( void *env, - const wasm_val_vec_t *args, - wasm_val_vec_t *results + wasmtime_caller_t *caller, + const wasmtime_val_t *args, + size_t arg_count, + wasmtime_val_t *results, + size_t result_count ) { - assert(args->size == 1); - assert(args->data[0].kind == WASM_I32); + assert(arg_count == 1); + assert(args[0].kind == WASM_I32); + int32_t lexer_address = args[0].of.i32; + TSWasmStore *store = env; TSLexer *lexer = store->current_lexer; - wasm_val_t result = WASM_I32_VAL(lexer->is_at_included_range_start(lexer)); - results->data[0] = result; + bool result = lexer->is_at_included_range_start(lexer); + results[0] = (wasmtime_val_t) { + .kind = WASMTIME_I32, + .of.i32 = result + }; return NULL; } static wasm_trap_t *eof_callback( void *env, - const wasm_val_vec_t *args, - wasm_val_vec_t *results + wasmtime_caller_t *caller, + const wasmtime_val_t *args, + size_t arg_count, + wasmtime_val_t *results, + size_t result_count ) { - assert(args->size == 1); - assert(args->data[0].kind == WASM_I32); + assert(arg_count == 1); + assert(args[0].kind == WASM_I32); + int32_t lexer_address = args[0].of.i32; + TSWasmStore *store = env; TSLexer *lexer = store->current_lexer; - wasm_val_t result = WASM_I32_VAL(lexer->eof(lexer)); - results->data[0] = result; + bool result = lexer->eof(lexer); + + results[0] = (wasmtime_val_t) { + .kind = WASMTIME_I32, + .of.i32 = result + }; return NULL; } typedef struct { - wasm_func_callback_with_env_t callback; + wasmtime_func_callback_t callback; wasm_functype_t *type; } FunctionDefinition; @@ -150,7 +215,7 @@ static void *copy(const void *data, size_t size) { } static void *copy_strings( - const char *data, + const uint8_t *data, int32_t array_address, size_t count, StringData *string_data @@ -159,13 +224,22 @@ static void *copy_strings( for (unsigned i = 0; i < count; i++) { int32_t address; memcpy(&address, &data[array_address + i * sizeof(address)], sizeof(address)); - const char *string = &data[address]; - uint32_t len = strlen(string); - result[i] = (const char *)(uintptr_t)string_data->size; - array_extend(string_data, len + 1, string); + if (address == 0) { + result[i] = (const char *)-1; + } else { + const uint8_t *string = &data[address]; + uint32_t len = strlen((const char *)string); + result[i] = (const char *)(uintptr_t)string_data->size; + array_extend(string_data, len + 1, string); + } } for (unsigned i = 0; i < count; i++) { - result[i] = string_data->contents + (uintptr_t)result[i]; + if (result[i] == (const char *)-1) { + result[i] = NULL; + } else { + result[i] = string_data->contents + (uintptr_t)result[i]; + } + printf(" string %u: %s\n", i, result[i]); } return result; } @@ -176,15 +250,32 @@ static bool name_eq(const wasm_name_t *name, const char *string) { TSWasmStore *ts_wasm_store_new(TSWasmEngine *engine) { TSWasmStore *self = ts_malloc(sizeof(TSWasmStore)); - wasm_store_t *store = wasm_store_new(engine); + wasmtime_store_t *store = wasmtime_store_new(engine, self, NULL); + wasmtime_context_t *context = wasmtime_store_context(store); + wasmtime_error_t *error = NULL; - // Memory - wasm_limits_t memory_limits = {.min = 64, .max = wasm_limits_max_default}; + // Initialize store's memory + wasm_limits_t memory_limits = {.min = LEXER_END_ADDRESS, .max = wasm_limits_max_default}; wasm_memorytype_t *memory_type = wasm_memorytype_new(&memory_limits); - wasm_memory_t *memory = wasm_memory_new(store, memory_type); + wasmtime_memory_t memory; + error = wasmtime_memory_new(context, memory_type, &memory); + assert(!error); wasm_memorytype_delete(memory_type); - // Lexer functions + // Initialize lexer struct with function pointers in wasm memory. + uint8_t *memory_data = wasmtime_memory_data(context, &memory); + LexerInWasmMemory lexer = { + .lookahead = 0, + .result_symbol = 0, + .advance = 0, + .mark_end = 1, + .get_column = 2, + .is_at_included_range_start = 3, + .eof = 4, + }; + memcpy(&memory_data[LEXER_ADDRESS], &lexer, sizeof(lexer)); + + // Define lexer functions. FunctionDefinition definitions[] = { {advance_callback, wasm_functype_new_2_0(wasm_valtype_new_i32(), wasm_valtype_new_i32())}, {mark_end_callback, wasm_functype_new_1_0(wasm_valtype_new_i32())}, @@ -194,66 +285,59 @@ TSWasmStore *ts_wasm_store_new(TSWasmEngine *engine) { }; unsigned definitions_len = array_len(definitions); - // Function table + // Add lexer functions to the store's function table. + wasmtime_table_t function_table; wasm_limits_t table_limits = {.min = definitions_len, .max = wasm_limits_max_default}; wasm_tabletype_t *table_type = wasm_tabletype_new(wasm_valtype_new(WASM_FUNCREF), &table_limits); - wasm_table_t *function_table = wasm_table_new(store, table_type, NULL); + wasmtime_val_t initializer = {.kind = WASMTIME_FUNCREF}; + error = wasmtime_table_new(context, table_type, &initializer, &function_table); + assert(!error); wasm_tabletype_delete(table_type); - wasm_table_grow(function_table, definitions_len, NULL); + uint32_t prev_size; + error = wasmtime_table_grow(context, &function_table, definitions_len, &initializer, &prev_size); + assert(!error); for (unsigned i = 0; i < definitions_len; i++) { FunctionDefinition *definition = &definitions[i]; - wasm_func_t *func = wasm_func_new_with_env(store, definition->type, definition->callback, self, NULL); - wasm_table_set(function_table, i, (wasm_ref_t *)func); + wasmtime_func_t func; + wasmtime_func_new(context, definition->type, definition->callback, self, NULL, &func); + wasmtime_val_t func_val = {.kind = WASMTIME_FUNCREF, .of.funcref = func}; + error = wasmtime_table_set(context, &function_table, i, &func_val); + assert(!error); wasm_functype_delete(definition->type); } *self = (TSWasmStore) { .store = store, + .engine = engine, .memory = memory, .language_instances = array_new(), .function_table = function_table, + .current_memory_offset = LEXER_END_ADDRESS, + .current_function_table_offset = definitions_len, }; return self; } void ts_wasm_store_delete(TSWasmStore *self) { - wasm_memory_delete(self->memory); - wasm_table_delete(self->function_table); - wasm_store_delete(self->store); + wasmtime_store_delete(self->store); + wasm_engine_delete(self->engine); array_delete(&self->language_instances); ts_free(self); } -void ts_wasm_language_delete(const TSWasmLanguage *self) { - assert(self->ref_count > 0); - if (atomic_dec((volatile uint32_t *)&self->ref_count) == 0) { - wasm_shared_module_delete(self->module); - ts_free((void *)self->language->alias_map); - ts_free((void *)self->language->alias_sequences); - ts_free((void *)self->language->parse_table); - ts_free((void *)self->language->parse_actions); - ts_free((void *)self->language->primary_state_ids); - ts_free((void *)self->language->public_symbol_map); - ts_free(self->language); - ts_free(self->symbol_name_buffer); - ts_free(self->field_name_buffer); - ts_free((void *)self); - } -} - -void ts_wasm_language_retain(const TSWasmLanguage *self) { - atomic_inc((volatile uint32_t *)&self->ref_count); -} - static bool ts_wasm_store__instantiate( TSWasmStore *self, - wasm_module_t *module, + wasmtime_module_t *module, const char *language_name, - wasm_instance_t **result, + wasmtime_instance_t *result, int32_t *language_address ) { - // Build language function name string. + wasmtime_context_t *context = wasmtime_store_context(self->store); + wasmtime_error_t *error = NULL; + wasm_trap_t *trap = NULL; + + // Construct the language function name as string. unsigned prefix_len = strlen("tree_sitter_"); unsigned name_len = strlen(language_name); char language_function_name[prefix_len + name_len + 1]; @@ -261,32 +345,35 @@ static bool ts_wasm_store__instantiate( memcpy(&language_function_name[prefix_len], language_name, name_len); language_function_name[prefix_len + name_len] = '\0'; - wasm_importtype_vec_t import_types = WASM_EMPTY_VEC; - wasm_module_imports(module, &import_types); - + // Construct globals representing the offset in memory and in the function + // table where the module should be added. + wasmtime_val_t table_base_val = WASM_I32_VAL(self->current_function_table_offset); + wasmtime_val_t memory_base_val = WASM_I32_VAL(self->current_memory_offset); + wasmtime_global_t memory_base_global; + wasmtime_global_t table_base_global; wasm_globaltype_t *const_i32_type = wasm_globaltype_new(wasm_valtype_new_i32(), WASM_CONST); - wasm_val_t table_base_val = WASM_I32_VAL(5); - wasm_val_t memory_base_val = WASM_I32_VAL(1600); - wasm_global_t *memory_base_global = wasm_global_new(self->store, const_i32_type, &memory_base_val); - wasm_global_t *table_base_global = wasm_global_new(self->store, const_i32_type, &table_base_val); + error = wasmtime_global_new(context, const_i32_type, &memory_base_val, &memory_base_global); + assert(!error); + error = wasmtime_global_new(context, const_i32_type, &table_base_val, &table_base_global); + assert(!error); wasm_globaltype_delete(const_i32_type); - // Build imports list - printf("import count: %lu\n", import_types.size); - wasm_extern_t *imports_list[import_types.size]; - wasm_extern_vec_t imports = WASM_ARRAY_VEC(imports_list); + // Build the imports list for the module. + wasm_importtype_vec_t import_types = WASM_EMPTY_VEC; + wasmtime_module_imports(module, &import_types); + wasmtime_extern_t imports[import_types.size]; for (unsigned i = 0; i < import_types.size; i++) { const wasm_importtype_t *import_type = import_types.data[i]; const wasm_name_t *import_name = wasm_importtype_name(import_type); if (name_eq(import_name, "__memory_base")) { - imports.data[i] = wasm_global_as_extern(memory_base_global); + imports[i] = (wasmtime_extern_t) {.kind = WASMTIME_EXTERN_GLOBAL, .of.global = memory_base_global}; } else if (name_eq(import_name, "__table_base")) { - imports.data[i] = wasm_global_as_extern(table_base_global); + imports[i] = (wasmtime_extern_t) {.kind = WASMTIME_EXTERN_GLOBAL, .of.global = table_base_global}; } else if (name_eq(import_name, "memory")) { - imports.data[i] = wasm_memory_as_extern(self->memory); + imports[i] = (wasmtime_extern_t) {.kind = WASMTIME_EXTERN_MEMORY, .of.memory = self->memory}; } else if (name_eq(import_name, "__indirect_function_table")) { - imports.data[i] = wasm_table_as_extern(self->function_table); + imports[i] = (wasmtime_extern_t) {.kind = WASMTIME_EXTERN_TABLE, .of.table = self->function_table}; } else { printf("unexpected import '%.*s'\n", (int)import_name->size, import_name->data); return false; @@ -294,88 +381,109 @@ static bool ts_wasm_store__instantiate( } wasm_importtype_vec_delete(&import_types); - wasm_trap_t *trap = NULL; - wasm_instance_t *instance = wasm_instance_new(self->store, module, &imports, &trap); + wasmtime_instance_t instance; + error = wasmtime_instance_new(context, module, imports, 4, &instance, &trap); + assert(!error); if (trap) { wasm_message_t message; wasm_trap_message(trap, &message); - printf("error loading wasm: %s\n", message.data); - abort(); + printf("error instantiating wasm module: %s\n", message.data); + return false; } - wasm_extern_vec_t exports = WASM_EMPTY_VEC; + // Process the module's exports. + wasmtime_extern_t language_extern; wasm_exporttype_vec_t export_types = WASM_EMPTY_VEC; - wasm_module_exports(module, &export_types); - wasm_instance_exports(instance, &exports); - assert(export_types.size == exports.size); - printf("export count: %lu\n", export_types.size); - - wasm_extern_t *language_extern = NULL; - for (unsigned i = 0; i < exports.size; i++) { - wasm_extern_t *export = exports.data[i]; + wasmtime_module_exports(module, &export_types); + for (unsigned i = 0; i < export_types.size; i++) { wasm_exporttype_t *export_type = export_types.data[i]; const wasm_name_t *name = wasm_exporttype_name(export_type); - printf(" export name: %.*s\n", (int)name->size, name->data); + + char *export_name; + size_t name_len; + wasmtime_extern_t export = {.kind = WASM_EXTERN_GLOBAL}; + bool exists = wasmtime_instance_export_nth(context, &instance, i, &export_name, &name_len, &export); + assert(exists); // Update pointers to reflect memory and function table offsets. if (name_eq(name, "__wasm_apply_data_relocs")) { - wasm_func_t *apply_relocation_func = wasm_extern_as_func(export); - wasm_val_vec_t arguments = WASM_EMPTY_VEC; - wasm_val_vec_t results = WASM_EMPTY_VEC; - wasm_trap_t *trap = wasm_func_call(apply_relocation_func, &arguments, &results); + wasmtime_func_t apply_relocation_func = export.of.func; + error = wasmtime_func_call(context, &apply_relocation_func, NULL, 0, NULL, 0, &trap); + assert(!error); if (trap) { wasm_message_t message; wasm_trap_message(trap, &message); printf("error calling relocation function: %s\n", message.data); abort(); } - } else if (name_eq(name, language_function_name)) { + } + + // Find the main language function for the module. + else if (name_eq(name, language_function_name)) { language_extern = export; } } wasm_exporttype_vec_delete(&export_types); - if (!language_extern) { + if (language_extern.kind != WASMTIME_EXTERN_FUNC) { printf("failed to find function %s\n", language_function_name); - abort(); + return false; } - wasm_func_t *language_func = wasm_extern_as_func(language_extern); - wasm_val_vec_t arguments = WASM_EMPTY_VEC; - wasm_val_vec_t results = WASM_EMPTY_VEC; - wasm_val_vec_new_uninitialized(&results, 1); - trap = wasm_func_call(language_func, &arguments, &results); + // Invoke the language function to get the static address of the language object. + wasmtime_func_t language_func = language_extern.of.func; + wasmtime_val_t language_address_val; + error = wasmtime_func_call(context, &language_func, NULL, 0, &language_address_val, 1, &trap); + assert(!error); if (trap) { wasm_message_t message; wasm_trap_message(trap, &message); printf("error calling language function: %s\n", message.data); - abort(); + return false; } - wasm_val_t language_address_val = results.data[0]; - assert(language_address_val.kind == WASM_I32); - + assert(language_address_val.kind == WASMTIME_I32); *result = instance; *language_address = language_address_val.of.i32; return true; } -TSWasmLanguage *ts_wasm_store_load_language( +static bool ts_wasm_store__sentinel_lex_fn(TSLexer *_lexer, TSStateId state) { + return false; +} + +const TSLanguage *ts_wasm_store_load_language( TSWasmStore *self, const char *language_name, const char *wasm, uint32_t wasm_len ) { - // Instantiate module. - wasm_byte_vec_t file = {.size = wasm_len, .data = (char *)wasm}; - wasm_module_t *module = wasm_module_new(self->store, &file); + // Compile the wasm code. + wasmtime_module_t *module; + wasmtime_error_t *error = wasmtime_module_new(self->engine, (const uint8_t *)wasm, wasm_len, &module); + if (error) { + wasm_message_t message; + wasmtime_error_message(error, &message); + printf("failed to load wasm language: %s", message.data); + return NULL; + } - wasm_instance_t *instance; + // Instantiate the module in this store. + wasmtime_instance_t instance; int32_t language_address; - if (!ts_wasm_store__instantiate(self, module, language_name, &instance, &language_address)) return NULL; + if (!ts_wasm_store__instantiate( + self, + module, + language_name, + &instance, + &language_address + )) return NULL; + // Copy all of the static data out of the language object in wasm memory, + // constructing a native language object. LanguageInWasmMemory wasm_language; - const byte_t *memory = wasm_memory_data(self->memory); + wasmtime_context_t *context = wasmtime_store_context(self->store); + const uint8_t *memory = wasmtime_memory_data(context, &self->memory); memcpy(&wasm_language, &memory[language_address], sizeof(LanguageInWasmMemory)); TSLanguage *language = ts_malloc(sizeof(TSLanguage)); @@ -400,12 +508,12 @@ TSWasmLanguage *ts_wasm_store_load_language( ), .parse_actions = copy( &memory[wasm_language.parse_actions], - 3000 // TODO - determine number of parse actions + 2800 * sizeof(TSParseActionEntry) // TODO - determine number of parse actions ), .symbol_names = copy_strings( memory, wasm_language.symbol_names, - wasm_language.symbol_count, + wasm_language.symbol_count + wasm_language.alias_count, &symbol_name_buffer ), .symbol_metadata = copy( @@ -435,7 +543,7 @@ TSWasmLanguage *ts_wasm_store_load_language( language->field_names = copy_strings( memory, wasm_language.field_names, - wasm_language.field_count, + wasm_language.field_count + 1, &field_name_buffer ); } @@ -454,7 +562,7 @@ TSWasmLanguage *ts_wasm_store_load_language( } language->alias_map = copy( &memory[wasm_language.alias_map], - alias_map_size + alias_map_size * sizeof(TSSymbol) ); language->alias_sequences = copy( &memory[wasm_language.alias_sequences], @@ -475,42 +583,74 @@ TSWasmLanguage *ts_wasm_store_load_language( ); } - TSWasmLanguage *result = ts_malloc(sizeof(TSWasmLanguage)); - *result = (TSWasmLanguage) { - .id = atomic_inc(&NEXT_LANGUAGE_ID), - .ref_count = 1, - .module = wasm_module_share(module), - .language = language, + unsigned name_len = strlen(language_name); + char *name = ts_malloc(name_len + 1); + memcpy(name, language_name, name_len); + name[name_len] = '\0'; + + LanguageWasmModule *language_module = ts_malloc(sizeof(LanguageWasmModule)); + *language_module = (LanguageWasmModule) { + .language_id = atomic_inc(&NEXT_LANGUAGE_ID), + .module = module, + .name = name, .symbol_name_buffer = symbol_name_buffer.contents, .field_name_buffer = field_name_buffer.contents, }; - array_push(&self->language_instances, ((LanguageInstance) { + + // The lex functions are not used for wasm languages. Use those two fields + // to mark this language as WASM-based and to store the language's + // WASM-specific data. + language->lex_fn = ts_wasm_store__sentinel_lex_fn; + language->keyword_lex_fn = (void *)language_module; + + // Store some information about this store's specific instance of this + // language module, keyed by the language's id. + array_push(&self->language_instances, ((LanguageWasmInstance) { + .language_id = language_module->language_id, .instance = instance, - .external_scan_index = wasm_language.external_scanner.scan, - .keyword_lex_fn_index = wasm_language.keyword_lex_fn, .main_lex_fn_index = wasm_language.lex_fn, + .keyword_lex_fn_index = wasm_language.keyword_lex_fn, })); - return result; + + return language; } -bool ts_wasm_store_start(TSWasmStore *self, TSLexer *lexer, const TSWasmLanguage *language) { +bool ts_wasm_store_start(TSWasmStore *self, TSLexer *lexer, const TSLanguage *language) { + if (!ts_language_is_wasm(language)) return false; + wasmtime_context_t *context = wasmtime_store_context(self->store); + const LanguageWasmModule *language_module = (void *)language->keyword_lex_fn; + + // Search for the information about this store's instance of the language module. uint32_t instance_index = 0; bool exists = false; - array_search_sorted_by(&self->language_instances, .id, language->id, &instance_index, &exists); + array_search_sorted_by( + &self->language_instances, + .language_id, + language_module->language_id, + &instance_index, + &exists + ); + // If the language module has not been instantiated in this store, then add + // it to this store. if (!exists) { - wasm_module_t *module = wasm_module_obtain(self->store, language->module); - wasm_instance_t *instance; + wasmtime_instance_t instance; int32_t language_address; - if (!ts_wasm_store__instantiate(self, module, language->name, &instance, &language_address)) { + if (!ts_wasm_store__instantiate( + self, + language_module->module, + language_module->name, + &instance, + &language_address + )) { return false; } LanguageInWasmMemory wasm_language; - const byte_t *memory = wasm_memory_data(self->memory); + const uint8_t *memory = wasmtime_memory_data(context, &self->memory); memcpy(&wasm_language, &memory[language_address], sizeof(LanguageInWasmMemory)); - array_insert(&self->language_instances, instance_index, ((LanguageInstance) { - .id = language->id, + array_insert(&self->language_instances, instance_index, ((LanguageWasmInstance) { + .language_id = language_module->language_id, .instance = instance, .main_lex_fn_index = wasm_language.lex_fn, .keyword_lex_fn_index = wasm_language.keyword_lex_fn, @@ -525,4 +665,66 @@ bool ts_wasm_store_start(TSWasmStore *self, TSLexer *lexer, const TSWasmLanguage void ts_wasm_store_stop(TSWasmStore *self) { self->current_lexer = NULL; self->current_instance = NULL; +} + +bool ts_wasm_store_run_lex_function(TSWasmStore *self, TSStateId state, unsigned function_index) { + wasmtime_context_t *context = wasmtime_store_context(self->store); + + uint8_t *memory_data = wasmtime_memory_data(context, &self->memory); + memcpy( + &memory_data[LEXER_ADDRESS], + &self->current_lexer->lookahead, + sizeof(self->current_lexer->lookahead) + ); + + wasmtime_val_t lex_val; + bool succeeded = wasmtime_table_get(context, &self->function_table, function_index, &lex_val); + assert(succeeded); + assert(lex_val.kind == WASMTIME_FUNCREF); + wasmtime_func_t lex_func = lex_val.of.funcref; + + const wasmtime_val_t args[2] = { + {.kind = WASMTIME_I32, .of.i32 = LEXER_ADDRESS}, + {.kind = WASMTIME_I32, .of.i32 = state}, + }; + wasmtime_val_t results[1] = { + {.kind = WASMTIME_I32, .of.i32 = 0} + }; + + wasm_trap_t *trap = NULL; + wasmtime_func_call(context, &lex_func, args, 2, results, 1, &trap); + if (trap) { + wasm_message_t message; + wasm_trap_message(trap, &message); + printf("error calling lex function index %u: %s\n", function_index, message.data); + abort(); + } + assert(results[0].kind == WASM_I32); + + memcpy( + &self->current_lexer->lookahead, + &memory_data[LEXER_ADDRESS], + sizeof(self->current_lexer->lookahead) + sizeof(self->current_lexer->result_symbol) + ); + return results[0].of.i32; +} + +bool ts_wasm_store_run_main_lex_function(TSWasmStore *self, TSStateId state) { + return ts_wasm_store_run_lex_function( + self, + state, + self->current_instance->main_lex_fn_index + ); +} + +bool ts_wasm_store_run_keyword_lex_function(TSWasmStore *self, TSStateId state) { + return ts_wasm_store_run_lex_function( + self, + state, + self->current_instance->keyword_lex_fn_index + ); +} + +bool ts_language_is_wasm(const TSLanguage *self) { + return self->lex_fn == ts_wasm_store__sentinel_lex_fn; } \ No newline at end of file diff --git a/lib/src/wasm.h b/lib/src/wasm.h index 05d15d5f..47153c14 100644 --- a/lib/src/wasm.h +++ b/lib/src/wasm.h @@ -5,76 +5,13 @@ extern "C" { #endif -#include "./array.h" -#include "./wasm/wasm.h" -#include "./lexer.h" +#include "tree_sitter/api.h" +#include "tree_sitter/parser.h" -typedef struct { - uint32_t id; - wasm_instance_t *instance; - uint32_t main_lex_fn_index; - uint32_t keyword_lex_fn_index; - uint32_t external_scan_index; -} LanguageInstance; - -struct TSWasmStore { - wasm_store_t *store; - wasm_table_t *function_table; - wasm_memory_t *memory; - TSLexer *current_lexer; - LanguageInstance *current_instance; - Array(LanguageInstance) language_instances; -}; - -struct TSWasmLanguage { - uint32_t id; - volatile uint32_t ref_count; - wasm_shared_module_t *module; - TSLanguage *language; - const char *name; - char *symbol_name_buffer; - char *field_name_buffer; -}; - -void ts_wasm_language_retain(const TSWasmLanguage *); -bool ts_wasm_store_start(TSWasmStore *self, TSLexer *lexer, const TSWasmLanguage *language); +bool ts_wasm_store_start(TSWasmStore *self, TSLexer *lexer, const TSLanguage *language); void ts_wasm_store_stop(TSWasmStore *self); - -static inline bool ts_wasm_store_run_main_lex_function(TSWasmStore *self, TSStateId state) { - uint32_t function_index = self->current_instance->main_lex_fn_index; - wasm_ref_t *lex_ref = wasm_table_get(self->function_table, function_index); - wasm_func_t *lex_func = wasm_ref_as_func(lex_ref); - wasm_val_t args_list[2] = { - WASM_I32_VAL(state), - WASM_I32_VAL(state), - }; - wasm_val_t results_list[1] = { - WASM_I32_VAL(0), - }; - wasm_val_vec_t args = WASM_ARRAY_VEC(args_list); - wasm_val_vec_t results = WASM_ARRAY_VEC(results_list); - wasm_func_call(lex_func, &args, &results); - assert(results_list[0].kind == WASM_I32); - return results_list[0].of.i32; -} - -static inline bool ts_wasm_store_run_keyword_lex_function(TSWasmStore *self, TSStateId state) { - uint32_t function_index = self->current_instance->keyword_lex_fn_index; - wasm_ref_t *lex_ref = wasm_table_get(self->function_table, function_index); - wasm_func_t *lex_func = wasm_ref_as_func(lex_ref); - wasm_val_t args_list[2] = { - WASM_I32_VAL(state), - WASM_I32_VAL(state), - }; - wasm_val_t results_list[1] = { - WASM_I32_VAL(0), - }; - wasm_val_vec_t args = WASM_ARRAY_VEC(args_list); - wasm_val_vec_t results = WASM_ARRAY_VEC(results_list); - wasm_func_call(lex_func, &args, &results); - assert(results_list[0].kind == WASM_I32); - return results_list[0].of.i32; -} +bool ts_wasm_store_run_main_lex_function(TSWasmStore *self, TSStateId state); +bool ts_wasm_store_run_keyword_lex_function(TSWasmStore *self, TSStateId state); #ifdef __cplusplus } diff --git a/lib/src/wasm/README.md b/lib/src/wasm/README.md index b590da54..c67121af 100644 --- a/lib/src/wasm/README.md +++ b/lib/src/wasm/README.md @@ -1,6 +1,9 @@ ## WASM -The `wasm.h` header file contains a standard interface implemented by multiple WASM implementations. -It was taken from here: +The `wasm.h` header file contains a standard interface implemented by multiple WASM implementations. It was taken from here: -https://github.com/WebAssembly/wasm-c-api/blob/c9d31284651b975f05ac27cee0bab1377560b87e/include/wasm.h \ No newline at end of file +https://github.com/WebAssembly/wasm-c-api/blob/c9d31284651b975f05ac27cee0bab1377560b87e/include/wasm.h + +The `wasmtime` headers were taken from here: + +https://github.com/bytecodealliance/wasmtime/tree/main/crates/c-api/include/wasmtime \ No newline at end of file diff --git a/lib/src/wasm/wasi.h b/lib/src/wasm/wasi.h new file mode 100644 index 00000000..994c66b2 --- /dev/null +++ b/lib/src/wasm/wasi.h @@ -0,0 +1,155 @@ +/** + * \file wasi.h + * + * C API for WASI + */ + +#ifndef WASI_H +#define WASI_H + +#include "wasm.h" + +#ifndef WASI_API_EXTERN +#ifdef _WIN32 +#define WASI_API_EXTERN __declspec(dllimport) +#else +#define WASI_API_EXTERN +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define own + +#define WASI_DECLARE_OWN(name) \ + typedef struct wasi_##name##_t wasi_##name##_t; \ + WASI_API_EXTERN void wasi_##name##_delete(own wasi_##name##_t*); + +/** + * \typedef wasi_config_t + * \brief Convenience alias for #wasi_config_t + * + * \struct wasi_config_t + * \brief TODO + * + * \fn void wasi_config_delete(wasi_config_t *); + * \brief Deletes a configuration object. + */ +WASI_DECLARE_OWN(config) + +/** + * \brief Creates a new empty configuration object. + * + * The caller is expected to deallocate the returned configuration + */ +WASI_API_EXTERN own wasi_config_t* wasi_config_new(); + +/** + * \brief Sets the argv list for this configuration object. + * + * By default WASI programs have an empty argv list, but this can be used to + * explicitly specify what the argv list for the program is. + * + * The arguments are copied into the `config` object as part of this function + * call, so the `argv` pointer only needs to stay alive for this function call. + */ +WASI_API_EXTERN void wasi_config_set_argv(wasi_config_t* config, int argc, const char* argv[]); + +/** + * \brief Indicates that the argv list should be inherited from this process's + * argv list. + */ +WASI_API_EXTERN void wasi_config_inherit_argv(wasi_config_t* config); + +/** + * \brief Sets the list of environment variables available to the WASI instance. + * + * By default WASI programs have a blank environment, but this can be used to + * define some environment variables for them. + * + * It is required that the `names` and `values` lists both have `envc` entries. + * + * The env vars are copied into the `config` object as part of this function + * call, so the `names` and `values` pointers only need to stay alive for this + * function call. + */ +WASI_API_EXTERN void wasi_config_set_env(wasi_config_t* config, int envc, const char* names[], const char* values[]); + +/** + * \brief Indicates that the entire environment of the calling process should be + * inherited by this WASI configuration. + */ +WASI_API_EXTERN void wasi_config_inherit_env(wasi_config_t* config); + +/** + * \brief Configures standard input to be taken from the specified file. + * + * By default WASI programs have no stdin, but this configures the specified + * file to be used as stdin for this configuration. + * + * If the stdin location does not exist or it cannot be opened for reading then + * `false` is returned. Otherwise `true` is returned. + */ +WASI_API_EXTERN bool wasi_config_set_stdin_file(wasi_config_t* config, const char* path); + +/** + * \brief Configures this process's own stdin stream to be used as stdin for + * this WASI configuration. + */ +WASI_API_EXTERN void wasi_config_inherit_stdin(wasi_config_t* config); + +/** + * \brief Configures standard output to be written to the specified file. + * + * By default WASI programs have no stdout, but this configures the specified + * file to be used as stdout. + * + * If the stdout location could not be opened for writing then `false` is + * returned. Otherwise `true` is returned. + */ +WASI_API_EXTERN bool wasi_config_set_stdout_file(wasi_config_t* config, const char* path); + +/** + * \brief Configures this process's own stdout stream to be used as stdout for + * this WASI configuration. + */ +WASI_API_EXTERN void wasi_config_inherit_stdout(wasi_config_t* config); + +/** + * \brief Configures standard output to be written to the specified file. + * + * By default WASI programs have no stderr, but this configures the specified + * file to be used as stderr. + * + * If the stderr location could not be opened for writing then `false` is + * returned. Otherwise `true` is returned. + */ +WASI_API_EXTERN bool wasi_config_set_stderr_file(wasi_config_t* config, const char* path); + +/** + * \brief Configures this process's own stderr stream to be used as stderr for + * this WASI configuration. + */ +WASI_API_EXTERN void wasi_config_inherit_stderr(wasi_config_t* config); + +/** + * \brief Configures a "preopened directory" to be available to WASI APIs. + * + * By default WASI programs do not have access to anything on the filesystem. + * This API can be used to grant WASI programs access to a directory on the + * filesystem, but only that directory (its whole contents but nothing above it). + * + * The `path` argument here is a path name on the host filesystem, and + * `guest_path` is the name by which it will be known in wasm. + */ +WASI_API_EXTERN bool wasi_config_preopen_dir(wasi_config_t* config, const char* path, const char* guest_path); + +#undef own + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // #ifdef WASI_H diff --git a/lib/src/wasm/wasmtime.h b/lib/src/wasm/wasmtime.h new file mode 100644 index 00000000..c70fd8b7 --- /dev/null +++ b/lib/src/wasm/wasmtime.h @@ -0,0 +1,228 @@ +/** + * \mainpage Wasmtime C API + * + * This documentation is an overview and API reference for the C API of + * Wasmtime. The C API is spread between three different header files: + * + * * \ref wasmtime.h + * * \ref wasi.h + * * \ref wasm.h + * + * The \ref wasmtime.h header file includes all the other header files and is + * the main header file you'll likely be using. The \ref wasm.h header file + * comes directly from the + * [WebAssembly/wasm-c-api](https://github.com/WebAssembly/wasm-c-api) + * repository, and at this time the upstream header file does not have + * documentation so Wasmtime provides documentation here. It should be noted + * some semantics may be Wasmtime-specific and may not be portable to other + * engines. + * + * ## Installing the C API + * + * To install the C API from precompiled binaries you can download the + * appropriate binary from the [releases page of + * Wasmtime](https://github.com/bytecodealliance/wasmtime/releases). Artifacts + * for the C API all end in "-c-api" for the filename. + * + * Each archive contains an `include` directory with necessary headers, as well + * as a `lib` directory with both a static archive and a dynamic library of + * Wasmtime. You can link to either of them as you see fit. + * + * ## Installing the C API through CMake + * + * CMake can be used to make the process of linking and compiling easier. An + * example of this if you have wasmtime as a git submodule at + * `third_party/wasmtime`: + * ``` + * add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/third_party/wasmtime/crates/c-api + * ${CMAKE_CURRENT_BINARY_DIR}/wasmtime) + * ... + * target_include_directories(YourProject PUBLIC wasmtime) + * target_link_libraries(YourProject PUBLIC wasmtime) + * ``` + * `BUILD_SHARED_LIBS` is provided as a define if you would like to build a + * shared library instead. You must distribute the appropriate shared library + * for your platform if you do this. + * + * ## Linking against the C API + * + * You'll want to arrange the `include` directory of the C API to be in your + * compiler's header path (e.g. the `-I` flag). If you're compiling for Windows + * and you're using the static library then you'll also need to pass + * `-DWASM_API_EXTERN=` and `-DWASI_API_EXTERN=` to disable dllimport. + * + * Your final artifact can then be linked with `-lwasmtime`. If you're linking + * against the static library you may need to pass other system libraries + * depending on your platform: + * + * * Linux - `-lpthread -ldl -lm` + * * macOS - no extra flags needed + * * Windows - `ws2_32.lib advapi32.lib userenv.lib ntdll.lib shell32.lib ole32.lib bcrypt.lib` + * + * ## Building from Source + * + * The C API is located in the + * [`crates/c-api`](https://github.com/bytecodealliance/wasmtime/tree/main/crates/c-api) + * directory of the [Wasmtime + * repository](https://github.com/bytecodealliance/wasmtime). To build from + * source you'll need a Rust compiler and a checkout of the `wasmtime` project. + * Afterwards you can execute: + * + * ``` + * $ cargo build --release -p wasmtime-c-api + * ``` + * + * This will place the final artifacts in `target/release`, with names depending + * on what platform you're compiling for. + * + * ## Other resources + * + * Some other handy resources you might find useful when exploring the C API + * documentation are: + * + * * [Rust `wasmtime` crate + * documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/) - + * although this documentation is for Rust and not C, you'll find that many + * functions mirror one another and there may be extra documentation in Rust + * you find helpful. If you find yourself having to frequently do this, + * though, please feel free to [file an + * issue](https://github.com/bytecodealliance/wasmtime/issues/new). + * + * * [C embedding + * examples](https://bytecodealliance.github.io/wasmtime/examples-c-embed.html) + * are available online and are tested from the Wasmtime repository itself. + * + * * [Contribution documentation for + * Wasmtime](https://bytecodealliance.github.io/wasmtime/contributing.html) in + * case you're interested in helping out! + */ + +/** + * \file wasmtime.h + * + * \brief Wasmtime's C API + * + * This file is the central inclusion point for Wasmtime's C API. There are a + * number of sub-header files but this file includes them all. The C API is + * based on \ref wasm.h but there are many Wasmtime-specific APIs which are + * tailored to Wasmtime's implementation. + * + * The #wasm_config_t and #wasm_engine_t types are used from \ref wasm.h. + * Additionally all type-level information (like #wasm_functype_t) is also + * used from \ref wasm.h. Otherwise, though, all wasm objects (like + * #wasmtime_store_t or #wasmtime_func_t) are used from this header file. + * + * ### Thread Safety + * + * The multithreading story of the C API very closely follows the + * multithreading story of the Rust API for Wasmtime. All objects are safe to + * send to other threads so long as user-specific data is also safe to send to + * other threads. Functions are safe to call from any thread but some functions + * cannot be called concurrently. For example, functions which correspond to + * `&T` in Rust can be called concurrently with any other methods that take + * `&T`. Functions that take `&mut T` in Rust, however, cannot be called + * concurrently with any other function (but can still be invoked on any + * thread). + * + * This generally equates to mutation of internal state. Functions which don't + * mutate anything, such as learning type information through + * #wasmtime_func_type, can be called concurrently. Functions which do require + * mutation, for example #wasmtime_func_call, cannot be called concurrently. + * This is conveyed in the C API with either `const wasmtime_context_t*` + * (concurrency is ok as it's read-only) or `wasmtime_context_t*` (concurrency + * is not ok, mutation may happen). + * + * When in doubt assume that functions cannot be called concurrently with + * aliasing objects. + * + * ### Aliasing + * + * The C API for Wasmtime is intended to be a relatively thin layer over the + * Rust API for Wasmtime. Rust has much more strict rules about aliasing than C + * does, and the Rust API for Wasmtime is designed around these rules to be + * used safely. These same rules must be upheld when using the C API of + * Wasmtime. + * + * The main consequence of this is that the #wasmtime_context_t pointer into + * the #wasmtime_store_t must be carefully used. Since the context is an + * internal pointer into the store it must be used carefully to ensure you're + * not doing something that Rust would otherwise forbid at compile time. A + * #wasmtime_context_t can only be used when you would otherwise have been + * provided access to it. For example in a host function created with + * #wasmtime_func_new you can use #wasmtime_context_t in the host function + * callback. This is because an argument, a #wasmtime_caller_t, provides access + * to #wasmtime_context_t. On the other hand a destructor passed to + * #wasmtime_externref_new, however, cannot use a #wasmtime_context_t because + * it was not provided access to one. Doing so may lead to memory unsafety. + * + * ### Stores + * + * A foundational construct in this API is the #wasmtime_store_t. A store is a + * collection of host-provided objects and instantiated wasm modules. Stores are + * often treated as a "single unit" and items within a store are all allowed to + * reference one another. References across stores cannot currently be created. + * For example you cannot pass a function from one store into another store. + * + * A store is not intended to be a global long-lived object. Stores provide no + * means of internal garbage collections of wasm objects (such as instances), + * meaning that no memory from a store will be deallocated until you call + * #wasmtime_store_delete. If you're working with a web server, for example, + * then it's recommended to think of a store as a "one per request" sort of + * construct. Globally you'd have one #wasm_engine_t and a cache of + * #wasmtime_module_t instances compiled into that engine. Each request would + * create a new #wasmtime_store_t and then instantiate a #wasmtime_module_t + * into the store. This process of creating a store and instantiating a module + * is expected to be quite fast. When the request is finished you'd delete the + * #wasmtime_store_t keeping memory usage reasonable for the lifetime of the + * server. + */ + +#ifndef WASMTIME_API_H +#define WASMTIME_API_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Converts from the text format of WebAssembly to to the binary format. + * + * \param wat this it the input pointer with the WebAssembly Text Format inside of + * it. This will be parsed and converted to the binary format. + * \param wat_len this it the length of `wat`, in bytes. + * \param ret if the conversion is successful, this byte vector is filled in with + * the WebAssembly binary format. + * + * \return a non-null error if parsing fails, or returns `NULL`. If parsing + * fails then `ret` isn't touched. + * + * This function does not take ownership of `wat`, and the caller is expected to + * deallocate the returned #wasmtime_error_t and #wasm_byte_vec_t. + */ +WASM_API_EXTERN wasmtime_error_t* wasmtime_wat2wasm( + const char *wat, + size_t wat_len, + wasm_byte_vec_t *ret +); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_API_H diff --git a/lib/src/wasm/wasmtime/config.h b/lib/src/wasm/wasmtime/config.h new file mode 100644 index 00000000..77c11936 --- /dev/null +++ b/lib/src/wasm/wasmtime/config.h @@ -0,0 +1,283 @@ +/** + * \file wasmtime/config.h + * + * \brief Wasmtime-specific extensions to #wasm_config_t + */ + +#ifndef WASMTIME_CONFIG_H +#define WASMTIME_CONFIG_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Specifier for how Wasmtime will compile code, values are in + * #wasmtime_strategy_enum + */ +typedef uint8_t wasmtime_strategy_t; + +/** + * \brief Different ways that Wasmtime can compile WebAssembly + * + * The default value is #WASMTIME_STRATEGY_AUTO. + */ +enum wasmtime_strategy_enum { // Strategy + /// Automatically picks the compilation backend, currently always defaulting + /// to Cranelift. + WASMTIME_STRATEGY_AUTO, + + /// Indicates that Wasmtime will unconditionally use Cranelift to compile + /// WebAssembly code. + WASMTIME_STRATEGY_CRANELIFT, +}; + +/** + * \brief Specifier of what optimization level to use for generated JIT code. + * + * See #wasmtime_opt_level_enum for possible values. + */ +typedef uint8_t wasmtime_opt_level_t; + +/** + * \brief Different ways Wasmtime can optimize generated code. + * + * The default value is #WASMTIME_OPT_LEVEL_SPEED. + */ +enum wasmtime_opt_level_enum { // OptLevel + /// Generated code will not be optimized at all. + WASMTIME_OPT_LEVEL_NONE, + /// Generated code will be optimized purely for speed. + WASMTIME_OPT_LEVEL_SPEED, + /// Generated code will be optimized, but some speed optimizations are + /// disabled if they cause the generated code to be significantly larger. + WASMTIME_OPT_LEVEL_SPEED_AND_SIZE, +}; + +/** + * \brief Different ways wasmtime can enable profiling JIT code. + * + * See #wasmtime_profiling_strategy_enum for possible values. + */ +typedef uint8_t wasmtime_profiling_strategy_t; + +/** + * \brief Different ways to profile JIT code. + * + * The default is #WASMTIME_PROFILING_STRATEGY_NONE. + */ +enum wasmtime_profiling_strategy_enum { // ProfilingStrategy + /// No profiling is enabled at runtime. + WASMTIME_PROFILING_STRATEGY_NONE, + /// Linux's "jitdump" support in `perf` is enabled and when Wasmtime is run + /// under `perf` necessary calls will be made to profile generated JIT code. + WASMTIME_PROFILING_STRATEGY_JITDUMP, + /// Support for VTune will be enabled and the VTune runtime will be informed, + /// at runtime, about JIT code. + /// + /// Note that this isn't always enabled at build time. + WASMTIME_PROFILING_STRATEGY_VTUNE, +}; + +#define WASMTIME_CONFIG_PROP(ret, name, ty) \ + WASM_API_EXTERN ret wasmtime_config_##name##_set(wasm_config_t*, ty); + +/** + * \brief Configures whether DWARF debug information is constructed at runtime + * to describe JIT code. + * + * This setting is `false` by default. When enabled it will attempt to inform + * native debuggers about DWARF debugging information for JIT code to more + * easily debug compiled WebAssembly via native debuggers. This can also + * sometimes improve the quality of output when profiling is enabled. + */ +WASMTIME_CONFIG_PROP(void, debug_info, bool) + +/** + * \brief Whether or not fuel is enabled for generated code. + * + * This setting is `false` by default. When enabled it will enable fuel counting + * meaning that fuel will be consumed every time a wasm instruction is executed, + * and trap when reaching zero. + */ +WASMTIME_CONFIG_PROP(void, consume_fuel, bool) + +/** + * \brief Whether or not epoch-based interruption is enabled for generated code. + * + * This setting is `false` by default. When enabled wasm code will check the + * current epoch periodically and abort if the current epoch is beyond a + * store-configured limit. + * + * Note that when this setting is enabled all stores will immediately trap and + * need to have their epoch deadline otherwise configured with + * #wasmtime_context_set_epoch_deadline. + * + * Note that the current epoch is engine-local and can be incremented with + * #wasmtime_engine_increment_epoch. + */ +WASMTIME_CONFIG_PROP(void, epoch_interruption, bool) + +/** + * \brief Configures the maximum stack size, in bytes, that JIT code can use. + * + * This setting is 2MB by default. Configuring this setting will limit the + * amount of native stack space that JIT code can use while it is executing. If + * you're hitting stack overflow you can try making this setting larger, or if + * you'd like to limit wasm programs to less stack you can also configure this. + * + * Note that this setting is not interpreted with 100% precision. Additionally + * the amount of stack space that wasm takes is always relative to the first + * invocation of wasm on the stack, so recursive calls with host frames in the + * middle will all need to fit within this setting. + */ +WASMTIME_CONFIG_PROP(void, max_wasm_stack, size_t) + +/** + * \brief Configures whether the WebAssembly threading proposal is enabled. + * + * This setting is `false` by default. + * + * Note that threads are largely unimplemented in Wasmtime at this time. + */ +WASMTIME_CONFIG_PROP(void, wasm_threads, bool) + +/** + * \brief Configures whether the WebAssembly reference types proposal is + * enabled. + * + * This setting is `false` by default. + */ +WASMTIME_CONFIG_PROP(void, wasm_reference_types, bool) + +/** + * \brief Configures whether the WebAssembly SIMD proposal is + * enabled. + * + * This setting is `false` by default. + */ +WASMTIME_CONFIG_PROP(void, wasm_simd, bool) + +/** + * \brief Configures whether the WebAssembly bulk memory proposal is + * enabled. + * + * This setting is `false` by default. + */ +WASMTIME_CONFIG_PROP(void, wasm_bulk_memory, bool) + +/** + * \brief Configures whether the WebAssembly multi value proposal is + * enabled. + * + * This setting is `true` by default. + */ +WASMTIME_CONFIG_PROP(void, wasm_multi_value, bool) + +/** + * \brief Configures whether the WebAssembly multi-memory proposal is + * enabled. + * + * This setting is `false` by default. + */ +WASMTIME_CONFIG_PROP(void, wasm_multi_memory, bool) + +/** + * \brief Configures whether the WebAssembly memory64 proposal is + * enabled. + * + * This setting is `false` by default. + */ +WASMTIME_CONFIG_PROP(void, wasm_memory64, bool) + +/** + * \brief Configures how JIT code will be compiled. + * + * This setting is #WASMTIME_STRATEGY_AUTO by default. + */ +WASMTIME_CONFIG_PROP(void, strategy, wasmtime_strategy_t) + +/** + * \brief Configures whether Cranelift's debug verifier is enabled. + * + * This setting in `false` by default. + * + * When cranelift is used for compilation this enables expensive debug checks + * within Cranelift itself to verify it's correct. + */ +WASMTIME_CONFIG_PROP(void, cranelift_debug_verifier, bool) + +/** + * \brief Configures whether Cranelift should perform a NaN-canonicalization pass. + * + * When Cranelift is used as a code generation backend this will configure + * it to replace NaNs with a single canonical value. This is useful for users + * requiring entirely deterministic WebAssembly computation. + * + * This is not required by the WebAssembly spec, so it is not enabled by default. + * + * The default value for this is `false` + */ +WASMTIME_CONFIG_PROP(void, cranelift_nan_canonicalization, bool) + +/** + * \brief Configures Cranelift's optimization level for JIT code. + * + * This setting in #WASMTIME_OPT_LEVEL_SPEED by default. + */ +WASMTIME_CONFIG_PROP(void, cranelift_opt_level, wasmtime_opt_level_t) + +/** + * \brief Configures the profiling strategy used for JIT code. + * + * This setting in #WASMTIME_PROFILING_STRATEGY_NONE by default. + */ +WASMTIME_CONFIG_PROP(void, profiler, wasmtime_profiling_strategy_t) + +/** + * \brief Configures the maximum size for memory to be considered "static" + * + * For more information see the Rust documentation at + * https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Config.html#method.static_memory_maximum_size. + */ +WASMTIME_CONFIG_PROP(void, static_memory_maximum_size, uint64_t) + +/** + * \brief Configures the guard region size for "static" memory. + * + * For more information see the Rust documentation at + * https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Config.html#method.static_memory_guard_size. + */ +WASMTIME_CONFIG_PROP(void, static_memory_guard_size, uint64_t) + +/** + * \brief Configures the guard region size for "dynamic" memory. + * + * For more information see the Rust documentation at + * https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Config.html#method.dynamic_memory_guard_size. + */ +WASMTIME_CONFIG_PROP(void, dynamic_memory_guard_size, uint64_t) + +/** + * \brief Enables Wasmtime's cache and loads configuration from the specified + * path. + * + * By default the Wasmtime compilation cache is disabled. The configuration path + * here can be `NULL` to use the default settings, and otherwise the argument + * here must be a file on the filesystem with TOML configuration - + * https://bytecodealliance.github.io/wasmtime/cli-cache.html. + * + * An error is returned if the cache configuration could not be loaded or if the + * cache could not be enabled. + */ +WASM_API_EXTERN wasmtime_error_t* wasmtime_config_cache_config_load(wasm_config_t*, const char*); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_CONFIG_H + diff --git a/lib/src/wasm/wasmtime/engine.h b/lib/src/wasm/wasmtime/engine.h new file mode 100644 index 00000000..1b8336f4 --- /dev/null +++ b/lib/src/wasm/wasmtime/engine.h @@ -0,0 +1,36 @@ +/** + * \file wasmtime/engine.h + * + * Wasmtime-specific extensions to #wasm_engine_t. + */ + +#ifndef WASMTIME_ENGINE_H +#define WASMTIME_ENGINE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Increments the engine-local epoch variable. + * + * This function will increment the engine's current epoch which can be used to + * force WebAssembly code to trap if the current epoch goes beyond the + * #wasmtime_store_t configured epoch deadline. + * + * This function is safe to call from any thread, and it is also + * async-signal-safe. + * + * See also #wasmtime_config_epoch_interruption_set. + */ +WASM_API_EXTERN void wasmtime_engine_increment_epoch(wasm_engine_t *engine); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_ENGINE_H + + diff --git a/lib/src/wasm/wasmtime/error.h b/lib/src/wasm/wasmtime/error.h new file mode 100644 index 00000000..2ffee72b --- /dev/null +++ b/lib/src/wasm/wasmtime/error.h @@ -0,0 +1,55 @@ +/** + * \file wasmtime/error.h + * + * \brief Definition and accessors of #wasmtime_error_t + */ + +#ifndef WASMTIME_ERROR_H +#define WASMTIME_ERROR_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \typedef wasmtime_error_t + * \brief Convenience alias for #wasmtime_error + * + * \struct wasmtime_error + * \brief Errors generated by Wasmtime. + * \headerfile wasmtime/error.h + * + * This opaque type represents an error that happened as part of one of the + * functions below. Errors primarily have an error message associated with them + * at this time, which you can acquire by calling #wasmtime_error_message. + * + * Errors are safe to share across threads and must be deleted with + * #wasmtime_error_delete. + */ +typedef struct wasmtime_error wasmtime_error_t; + +/** + * \brief Deletes an error. + */ +WASM_API_EXTERN void wasmtime_error_delete(wasmtime_error_t *error); + +/** + * \brief Returns the string description of this error. + * + * This will "render" the error to a string and then return the string + * representation of the error to the caller. The `message` argument should be + * uninitialized before this function is called and the caller is responsible + * for deallocating it with #wasm_byte_vec_delete afterwards. + */ +WASM_API_EXTERN void wasmtime_error_message( + const wasmtime_error_t *error, + wasm_name_t *message +); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_ERROR_H diff --git a/lib/src/wasm/wasmtime/extern.h b/lib/src/wasm/wasmtime/extern.h new file mode 100644 index 00000000..29dcb217 --- /dev/null +++ b/lib/src/wasm/wasmtime/extern.h @@ -0,0 +1,145 @@ +/** + * \file wasmtime/extern.h + * + * \brief Definition of #wasmtime_extern_t and external items. + */ + +#ifndef WASMTIME_EXTERN_H +#define WASMTIME_EXTERN_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/// \brief Representation of a function in Wasmtime. +/// +/// Functions are represented with a 64-bit identifying integer in Wasmtime. +/// They do not have any destructor associated with them. Functions cannot +/// interoperate between #wasmtime_store_t instances and if the wrong function +/// is passed to the wrong store then it may trigger an assertion to abort the +/// process. +typedef struct wasmtime_func { + /// Internal identifier of what store this belongs to, never zero. + uint64_t store_id; + /// Internal index within the store. + size_t index; +} wasmtime_func_t; + +/// \brief Representation of a table in Wasmtime. +/// +/// Tables are represented with a 64-bit identifying integer in Wasmtime. +/// They do not have any destructor associated with them. Tables cannot +/// interoperate between #wasmtime_store_t instances and if the wrong table +/// is passed to the wrong store then it may trigger an assertion to abort the +/// process. +typedef struct wasmtime_table { + /// Internal identifier of what store this belongs to, never zero. + uint64_t store_id; + /// Internal index within the store. + size_t index; +} wasmtime_table_t; + +/// \brief Representation of a memory in Wasmtime. +/// +/// Memories are represented with a 64-bit identifying integer in Wasmtime. +/// They do not have any destructor associated with them. Memories cannot +/// interoperate between #wasmtime_store_t instances and if the wrong memory +/// is passed to the wrong store then it may trigger an assertion to abort the +/// process. +typedef struct wasmtime_memory { + /// Internal identifier of what store this belongs to, never zero. + uint64_t store_id; + /// Internal index within the store. + size_t index; +} wasmtime_memory_t; + +/// \brief Representation of a global in Wasmtime. +/// +/// Globals are represented with a 64-bit identifying integer in Wasmtime. +/// They do not have any destructor associated with them. Globals cannot +/// interoperate between #wasmtime_store_t instances and if the wrong global +/// is passed to the wrong store then it may trigger an assertion to abort the +/// process. +typedef struct wasmtime_global { + /// Internal identifier of what store this belongs to, never zero. + uint64_t store_id; + /// Internal index within the store. + size_t index; +} wasmtime_global_t; + +/// \brief Discriminant of #wasmtime_extern_t +typedef uint8_t wasmtime_extern_kind_t; + +/// \brief Value of #wasmtime_extern_kind_t meaning that #wasmtime_extern_t is a +/// function +#define WASMTIME_EXTERN_FUNC 0 +/// \brief Value of #wasmtime_extern_kind_t meaning that #wasmtime_extern_t is a +/// global +#define WASMTIME_EXTERN_GLOBAL 1 +/// \brief Value of #wasmtime_extern_kind_t meaning that #wasmtime_extern_t is a +/// table +#define WASMTIME_EXTERN_TABLE 2 +/// \brief Value of #wasmtime_extern_kind_t meaning that #wasmtime_extern_t is a +/// memory +#define WASMTIME_EXTERN_MEMORY 3 + +/** + * \typedef wasmtime_extern_union_t + * \brief Convenience alias for #wasmtime_extern_union + * + * \union wasmtime_extern_union + * \brief Container for different kinds of extern items. + * + * This type is contained in #wasmtime_extern_t and contains the payload for the + * various kinds of items an extern wasm item can be. + */ +typedef union wasmtime_extern_union { + /// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_FUNC + wasmtime_func_t func; + /// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_GLOBAL + wasmtime_global_t global; + /// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_TABLE + wasmtime_table_t table; + /// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_MEMORY + wasmtime_memory_t memory; +} wasmtime_extern_union_t; + +/** + * \typedef wasmtime_extern_t + * \brief Convenience alias for #wasmtime_extern_t + * + * \union wasmtime_extern + * \brief Container for different kinds of extern items. + * + * Note that this structure may contain an owned value, namely + * #wasmtime_module_t, depending on the context in which this is used. APIs + * which consume a #wasmtime_extern_t do not take ownership, but APIs that + * return #wasmtime_extern_t require that #wasmtime_extern_delete is called to + * deallocate the value. + */ +typedef struct wasmtime_extern { + /// Discriminant of which field of #of is valid. + wasmtime_extern_kind_t kind; + /// Container for the extern item's value. + wasmtime_extern_union_t of; +} wasmtime_extern_t; + +/// \brief Deletes a #wasmtime_extern_t. +void wasmtime_extern_delete(wasmtime_extern_t *val); + +/// \brief Returns the type of the #wasmtime_extern_t defined within the given +/// store. +/// +/// Does not take ownership of `context` or `val`, but the returned +/// #wasm_externtype_t is an owned value that needs to be deleted. +wasm_externtype_t *wasmtime_extern_type(wasmtime_context_t *context, wasmtime_extern_t *val); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_EXTERN_H + diff --git a/lib/src/wasm/wasmtime/func.h b/lib/src/wasm/wasmtime/func.h new file mode 100644 index 00000000..2683eaab --- /dev/null +++ b/lib/src/wasm/wasmtime/func.h @@ -0,0 +1,310 @@ +/** + * \file wasmtime/func.h + * + * Wasmtime definitions of how to interact with host and wasm functions. + */ + +#ifndef WASMTIME_FUNC_H +#define WASMTIME_FUNC_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \typedef wasmtime_caller_t + * \brief Alias to #wasmtime_caller + * + * \brief Structure used to learn about the caller of a host-defined function. + * \struct wasmtime_caller + * + * This structure is an argument to #wasmtime_func_callback_t. The purpose + * of this structure is acquire a #wasmtime_context_t pointer to interact with + * objects, but it can also be used for inspect the state of the caller (such as + * getting memories and functions) with #wasmtime_caller_export_get. + * + * This object is never owned and does not need to be deleted. + */ +typedef struct wasmtime_caller wasmtime_caller_t; + +/** + * \brief Callback signature for #wasmtime_func_new. + * + * This is the function signature for host functions that can be made accessible + * to WebAssembly. The arguments to this function are: + * + * \param env user-provided argument passed to #wasmtime_func_new + * \param caller a temporary object that can only be used during this function + * call. Used to acquire #wasmtime_context_t or caller's state + * \param args the arguments provided to this function invocation + * \param nargs how many arguments are provided + * \param results where to write the results of this function + * \param nresults how many results must be produced + * + * Callbacks are guaranteed to get called with the right types of arguments, but + * they must produce the correct number and types of results. Failure to do so + * will cause traps to get raised on the wasm side. + * + * This callback can optionally return a #wasm_trap_t indicating that a trap + * should be raised in WebAssembly. It's expected that in this case the caller + * relinquishes ownership of the trap and it is passed back to the engine. + */ +typedef wasm_trap_t* (*wasmtime_func_callback_t)( + void *env, + wasmtime_caller_t* caller, + const wasmtime_val_t *args, + size_t nargs, + wasmtime_val_t *results, + size_t nresults); + +/** + * \brief Creates a new host-defined function. + * + * Inserts a host-defined function into the `store` provided which can be used + * to then instantiate a module with or define within a #wasmtime_linker_t. + * + * \param store the store in which to create the function + * \param type the wasm type of the function that's being created + * \param callback the host-defined callback to invoke + * \param env host-specific data passed to the callback invocation, can be + * `NULL` + * \param finalizer optional finalizer for `env`, can be `NULL` + * \param ret the #wasmtime_func_t return value to be filled in. + * + * The returned function can only be used with the specified `store`. + */ +WASM_API_EXTERN void wasmtime_func_new( + wasmtime_context_t *store, + const wasm_functype_t* type, + wasmtime_func_callback_t callback, + void *env, + void (*finalizer)(void*), + wasmtime_func_t *ret +); + +/** + * \brief Callback signature for #wasmtime_func_new_unchecked. + * + * This is the function signature for host functions that can be made accessible + * to WebAssembly. The arguments to this function are: + * + * \param env user-provided argument passed to #wasmtime_func_new_unchecked + * \param caller a temporary object that can only be used during this function + * call. Used to acquire #wasmtime_context_t or caller's state + * \param args_and_results storage space for both the parameters to the + * function as well as the results of the function. The size of this + * array depends on the function type that the host function is created + * with, but it will be the maximum of the number of parameters and + * number of results. + * \param num_args_and_results the size of the `args_and_results` parameter in + * units of #wasmtime_val_raw_t. + * + * This callback can optionally return a #wasm_trap_t indicating that a trap + * should be raised in WebAssembly. It's expected that in this case the caller + * relinquishes ownership of the trap and it is passed back to the engine. + * + * This differs from #wasmtime_func_callback_t in that the payload of + * `args_and_results` does not have type information, nor does it have sizing + * information. This is especially unsafe because it's only valid within the + * particular #wasm_functype_t that the function was created with. The onus is + * on the embedder to ensure that `args_and_results` are all read correctly + * for parameters and all written for results within the execution of a + * function. + * + * Parameters will be listed starting at index 0 in the `args_and_results` + * array. Results are also written starting at index 0, which will overwrite + * the arguments. + */ +typedef wasm_trap_t* (*wasmtime_func_unchecked_callback_t)( + void *env, + wasmtime_caller_t* caller, + wasmtime_val_raw_t *args_and_results, + size_t num_args_and_results); + +/** + * \brief Creates a new host function in the same manner of #wasmtime_func_new, + * but the function-to-call has no type information available at runtime. + * + * This function is very similar to #wasmtime_func_new. The difference is that + * this version is "more unsafe" in that when the host callback is invoked there + * is no type information and no checks that the right types of values are + * produced. The onus is on the consumer of this API to ensure that all + * invariants are upheld such as: + * + * * The host callback reads parameters correctly and interprets their types + * correctly. + * * If a trap doesn't happen then all results are written to the results + * pointer. All results must have the correct type. + * * Types such as `funcref` cannot cross stores. + * * Types such as `externref` have valid reference counts. + * + * It's generally only recommended to use this if your application can wrap + * this in a safe embedding. This should not be frequently used due to the + * number of invariants that must be upheld on the wasm<->host boundary. On the + * upside, though, this flavor of host function will be faster to call than + * those created by #wasmtime_func_new (hence the reason for this function's + * existence). + */ +WASM_API_EXTERN void wasmtime_func_new_unchecked( + wasmtime_context_t *store, + const wasm_functype_t* type, + wasmtime_func_unchecked_callback_t callback, + void *env, + void (*finalizer)(void*), + wasmtime_func_t *ret +); + +/** + * \brief Returns the type of the function specified + * + * The returned #wasm_functype_t is owned by the caller. + */ +WASM_API_EXTERN wasm_functype_t* wasmtime_func_type( + const wasmtime_context_t *store, + const wasmtime_func_t *func +); + +/** + * \brief Call a WebAssembly function. + * + * This function is used to invoke a function defined within a store. For + * example this might be used after extracting a function from a + * #wasmtime_instance_t. + * + * \param store the store which owns `func` + * \param func the function to call + * \param args the arguments to the function call + * \param nargs the number of arguments provided + * \param results where to write the results of the function call + * \param nresults the number of results expected + * \param trap where to store a trap, if one happens. + * + * There are three possible return states from this function: + * + * 1. The returned error is non-null. This means `results` + * wasn't written to and `trap` will have `NULL` written to it. This state + * means that programmer error happened when calling the function, for + * example when the size of the arguments/results was wrong, the types of the + * arguments were wrong, or arguments may come from the wrong store. + * 2. The trap pointer is filled in. This means the returned error is `NULL` and + * `results` was not written to. This state means that the function was + * executing but hit a wasm trap while executing. + * 3. The error and trap returned are both `NULL` and `results` are written to. + * This means that the function call succeeded and the specified results were + * produced. + * + * The `trap` pointer cannot be `NULL`. The `args` and `results` pointers may be + * `NULL` if the corresponding length is zero. + * + * Does not take ownership of #wasmtime_val_t arguments. Gives ownership of + * #wasmtime_val_t results. + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_func_call( + wasmtime_context_t *store, + const wasmtime_func_t *func, + const wasmtime_val_t *args, + size_t nargs, + wasmtime_val_t *results, + size_t nresults, + wasm_trap_t **trap +); + +/** + * \brief Call a WebAssembly function in an "unchecked" fashion. + * + * This function is similar to #wasmtime_func_call except that there is no type + * information provided with the arguments (or sizing information). Consequently + * this is less safe to call since it's up to the caller to ensure that `args` + * has an appropriate size and all the parameters are configured with their + * appropriate values/types. Additionally all the results must be interpreted + * correctly if this function returns successfully. + * + * Parameters must be specified starting at index 0 in the `args_and_results` + * array. Results are written starting at index 0, which will overwrite + * the arguments. + * + * Callers must ensure that various correctness variants are upheld when this + * API is called such as: + * + * * The `args_and_results` pointer has enough space to hold all the parameters + * and all the results (but not at the same time). + * * Parameters must all be configured as if they were the correct type. + * * Values such as `externref` and `funcref` are valid within the store being + * called. + * + * When in doubt it's much safer to call #wasmtime_func_call. This function is + * faster than that function, but the tradeoff is that embeddings must uphold + * more invariants rather than relying on Wasmtime to check them for you. + */ +WASM_API_EXTERN wasm_trap_t *wasmtime_func_call_unchecked( + wasmtime_context_t *store, + const wasmtime_func_t *func, + wasmtime_val_raw_t *args_and_results +); + +/** + * \brief Loads a #wasmtime_extern_t from the caller's context + * + * This function will attempt to look up the export named `name` on the caller + * instance provided. If it is found then the #wasmtime_extern_t for that is + * returned, otherwise `NULL` is returned. + * + * Note that this only works for exported memories right now for WASI + * compatibility. + * + * \param caller the caller object to look up the export from + * \param name the name that's being looked up + * \param name_len the byte length of `name` + * \param item where to store the return value + * + * Returns a nonzero value if the export was found, or 0 if the export wasn't + * found. If the export wasn't found then `item` isn't written to. + */ +WASM_API_EXTERN bool wasmtime_caller_export_get( + wasmtime_caller_t *caller, + const char *name, + size_t name_len, + wasmtime_extern_t *item +); + +/** + * \brief Returns the store context of the caller object. + */ +WASM_API_EXTERN wasmtime_context_t* wasmtime_caller_context(wasmtime_caller_t* caller); + +/** + * \brief Converts a `raw` nonzero `funcref` value from #wasmtime_val_raw_t + * into a #wasmtime_func_t. + * + * This function can be used to interpret nonzero values of the `funcref` field + * of the #wasmtime_val_raw_t structure. It is assumed that `raw` does not have + * a value of 0, or otherwise the program will abort. + * + * Note that this function is unchecked and unsafe. It's only safe to pass + * values learned from #wasmtime_val_raw_t with the same corresponding + * #wasmtime_context_t that they were produced from. Providing arbitrary values + * to `raw` here or cross-context values with `context` is UB. + */ +WASM_API_EXTERN void wasmtime_func_from_raw( + wasmtime_context_t* context, + size_t raw, + wasmtime_func_t *ret); + +/** + * \brief Converts a `func` which belongs to `context` into a `usize` + * parameter that is suitable for insertion into a #wasmtime_val_raw_t. + */ +WASM_API_EXTERN size_t wasmtime_func_to_raw( + wasmtime_context_t* context, + const wasmtime_func_t *func); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_FUNC_H diff --git a/lib/src/wasm/wasmtime/global.h b/lib/src/wasm/wasmtime/global.h new file mode 100644 index 00000000..4e244285 --- /dev/null +++ b/lib/src/wasm/wasmtime/global.h @@ -0,0 +1,91 @@ +/** + * \file wasmtime/global.h + * + * Wasmtime APIs for interacting with WebAssembly globals. + */ + +#ifndef WASMTIME_GLOBAL_H +#define WASMTIME_GLOBAL_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Creates a new global value. + * + * Creates a new host-defined global value within the provided `store` + * + * \param store the store in which to create the global + * \param type the wasm type of the global being created + * \param val the initial value of the global + * \param ret a return pointer for the created global. + * + * This function may return an error if the `val` argument does not match the + * specified type of the global, or if `val` comes from a different store than + * the one provided. + * + * This function does not take ownership of any of its arguments but error is + * owned by the caller. + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_global_new( + wasmtime_context_t *store, + const wasm_globaltype_t *type, + const wasmtime_val_t *val, + wasmtime_global_t *ret +); + +/** + * \brief Returns the wasm type of the specified global. + * + * The returned #wasm_globaltype_t is owned by the caller. + */ +WASM_API_EXTERN wasm_globaltype_t* wasmtime_global_type( + const wasmtime_context_t *store, + const wasmtime_global_t *global +); + +/** + * \brief Get the value of the specified global. + * + * \param store the store that owns `global` + * \param global the global to get + * \param out where to store the value in this global. + * + * This function returns ownership of the contents of `out`, so + * #wasmtime_val_delete may need to be called on the value. + */ +WASM_API_EXTERN void wasmtime_global_get( + wasmtime_context_t *store, + const wasmtime_global_t *global, + wasmtime_val_t *out +); + +/** + * \brief Sets a global to a new value. + * + * \param store the store that owns `global` + * \param global the global to set + * \param val the value to store in the global + * + * This function may return an error if `global` is not mutable or if `val` has + * the wrong type for `global`. + * + * THis does not take ownership of any argument but returns ownership of the error. + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_global_set( + wasmtime_context_t *store, + const wasmtime_global_t *global, + const wasmtime_val_t *val +); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_GLOBAL_H diff --git a/lib/src/wasm/wasmtime/instance.h b/lib/src/wasm/wasmtime/instance.h new file mode 100644 index 00000000..544661f9 --- /dev/null +++ b/lib/src/wasm/wasmtime/instance.h @@ -0,0 +1,128 @@ +/** + * \file wasmtime/instance.h + * + * Wasmtime APIs for interacting with wasm instances. + */ + +#ifndef WASMTIME_INSTANCE_H +#define WASMTIME_INSTANCE_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/// \brief Representation of a instance in Wasmtime. +/// +/// Instances are represented with a 64-bit identifying integer in Wasmtime. +/// They do not have any destructor associated with them. Instances cannot +/// interoperate between #wasmtime_store_t instances and if the wrong instance +/// is passed to the wrong store then it may trigger an assertion to abort the +/// process. +typedef struct wasmtime_instance { + /// Internal identifier of what store this belongs to, never zero. + uint64_t store_id; + /// Internal index within the store. + size_t index; +} wasmtime_instance_t; + +/** + * \brief Instantiate a wasm module. + * + * This function will instantiate a WebAssembly module with the provided + * imports, creating a WebAssembly instance. The returned instance can then + * afterwards be inspected for exports. + * + * \param store the store in which to create the instance + * \param module the module that's being instantiated + * \param imports the imports provided to the module + * \param nimports the size of `imports` + * \param instance where to store the returned instance + * \param trap where to store the returned trap + * + * This function requires that `imports` is the same size as the imports that + * `module` has. Additionally the `imports` array must be 1:1 lined up with the + * imports of the `module` specified. This is intended to be relatively low + * level, and #wasmtime_linker_instantiate is provided for a more ergonomic + * name-based resolution API. + * + * The states of return values from this function are similar to + * #wasmtime_func_call where an error can be returned meaning something like a + * link error in this context. A trap can be returned (meaning no error or + * instance is returned), or an instance can be returned (meaning no error or + * trap is returned). + * + * Note that this function requires that all `imports` specified must be owned + * by the `store` provided as well. + * + * This function does not take ownership of any of its arguments, but all return + * values are owned by the caller. + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_instance_new( + wasmtime_context_t *store, + const wasmtime_module_t *module, + const wasmtime_extern_t* imports, + size_t nimports, + wasmtime_instance_t *instance, + wasm_trap_t **trap +); + +/** + * \brief Get an export by name from an instance. + * + * \param store the store that owns `instance` + * \param instance the instance to lookup within + * \param name the export name to lookup + * \param name_len the byte length of `name` + * \param item where to store the returned value + * + * Returns nonzero if the export was found, and `item` is filled in. Otherwise + * returns 0. + * + * Doesn't take ownership of any arguments but does return ownership of the + * #wasmtime_extern_t. + */ +WASM_API_EXTERN bool wasmtime_instance_export_get( + wasmtime_context_t *store, + const wasmtime_instance_t *instance, + const char *name, + size_t name_len, + wasmtime_extern_t *item +); + +/** + * \brief Get an export by index from an instance. + * + * \param store the store that owns `instance` + * \param instance the instance to lookup within + * \param index the index to lookup + * \param name where to store the name of the export + * \param name_len where to store the byte length of the name + * \param item where to store the export itself + * + * Returns nonzero if the export was found, and `name`, `name_len`, and `item` + * are filled in. Otherwise returns 0. + * + * Doesn't take ownership of any arguments but does return ownership of the + * #wasmtime_extern_t. The `name` pointer return value is owned by the `store` + * and must be immediately used before calling any other APIs on + * #wasmtime_context_t. + */ +WASM_API_EXTERN bool wasmtime_instance_export_nth( + wasmtime_context_t *store, + const wasmtime_instance_t *instance, + size_t index, + char **name, + size_t *name_len, + wasmtime_extern_t *item +); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_INSTANCE_H diff --git a/lib/src/wasm/wasmtime/linker.h b/lib/src/wasm/wasmtime/linker.h new file mode 100644 index 00000000..edd52442 --- /dev/null +++ b/lib/src/wasm/wasmtime/linker.h @@ -0,0 +1,297 @@ +/** + * \file wasmtime/linker.h + * + * Wasmtime API for a name-based linker used to instantiate modules. + */ + +#ifndef WASMTIME_LINKER_H +#define WASMTIME_LINKER_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \typedef wasmtime_linker_t + * \brief Alias to #wasmtime_linker + * + * \struct #wasmtime_linker + * \brief Object used to conveniently link together and instantiate wasm + * modules. + * + * This type corresponds to the `wasmtime::Linker` type in Rust. This + * type is intended to make it easier to manage a set of modules that link + * together, or to make it easier to link WebAssembly modules to WASI. + * + * A #wasmtime_linker_t is a higher level way to instantiate a module than + * #wasm_instance_new since it works at the "string" level of imports rather + * than requiring 1:1 mappings. + */ +typedef struct wasmtime_linker wasmtime_linker_t; + +/** + * \brief Creates a new linker for the specified engine. + * + * This function does not take ownership of the engine argument, and the caller + * is expected to delete the returned linker. + */ +WASM_API_EXTERN wasmtime_linker_t* wasmtime_linker_new(wasm_engine_t* engine); + +/** + * \brief Deletes a linker + */ +WASM_API_EXTERN void wasmtime_linker_delete(wasmtime_linker_t* linker); + +/** + * \brief Configures whether this linker allows later definitions to shadow + * previous definitions. + * + * By default this setting is `false`. + */ +WASM_API_EXTERN void wasmtime_linker_allow_shadowing(wasmtime_linker_t* linker, bool allow_shadowing); + +/** + * \brief Defines a new item in this linker. + * + * \param linker the linker the name is being defined in. + * \param module the module name the item is defined under. + * \param module_len the byte length of `module` + * \param name the field name the item is defined under + * \param name_len the byte length of `name` + * \param item the item that is being defined in this linker. + * + * \return On success `NULL` is returned, otherwise an error is returned which + * describes why the definition failed. + * + * For more information about name resolution consult the [Rust + * documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Linker.html#name-resolution). + */ +WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define( + wasmtime_linker_t *linker, + const char *module, + size_t module_len, + const char *name, + size_t name_len, + const wasmtime_extern_t *item +); + +/** + * \brief Defines a new function in this linker. + * + * \param linker the linker the name is being defined in. + * \param module the module name the item is defined under. + * \param module_len the byte length of `module` + * \param name the field name the item is defined under + * \param name_len the byte length of `name` + * \param ty the type of the function that's being defined + * \param cb the host callback to invoke when the function is called + * \param data the host-provided data to provide as the first argument to the callback + * \param finalizer an optional finalizer for the `data` argument. + * + * \return On success `NULL` is returned, otherwise an error is returned which + * describes why the definition failed. + * + * For more information about name resolution consult the [Rust + * documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Linker.html#name-resolution). + * + * Note that this function does not create a #wasmtime_func_t. This creates a + * store-independent function within the linker, allowing this function + * definition to be used with multiple stores. + * + * For more information about host callbacks see #wasmtime_func_new. + */ +WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define_func( + wasmtime_linker_t *linker, + const char *module, + size_t module_len, + const char *name, + size_t name_len, + const wasm_functype_t *ty, + wasmtime_func_callback_t cb, + void *data, + void (*finalizer)(void*) +); + +/** + * \brief Defines a new function in this linker. + * + * This is the same as #wasmtime_linker_define_func except that it's the analog + * of #wasmtime_func_new_unchecked instead of #wasmtime_func_new. Be sure to + * consult the documentation of #wasmtime_linker_define_func for argument + * information as well as #wasmtime_func_new_unchecked for why this is an + * unsafe API. + */ +WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define_func_unchecked( + wasmtime_linker_t *linker, + const char *module, + size_t module_len, + const char *name, + size_t name_len, + const wasm_functype_t *ty, + wasmtime_func_unchecked_callback_t cb, + void *data, + void (*finalizer)(void*) +); + +/** + * \brief Defines WASI functions in this linker. + * + * \param linker the linker the name is being defined in. + * + * \return On success `NULL` is returned, otherwise an error is returned which + * describes why the definition failed. + * + * This function will provide WASI function names in the specified linker. Note + * that when an instance is created within a store then the store also needs to + * have its WASI settings configured with #wasmtime_context_set_wasi for WASI + * functions to work, otherwise an assert will be tripped that will abort the + * process. + * + * For more information about name resolution consult the [Rust + * documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Linker.html#name-resolution). + */ +WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define_wasi( + wasmtime_linker_t *linker +); + +/** + * \brief Defines an instance under the specified name in this linker. + * + * \param linker the linker the name is being defined in. + * \param store the store that owns `instance` + * \param name the module name to define `instance` under. + * \param name_len the byte length of `name` + * \param instance a previously-created instance. + * + * \return On success `NULL` is returned, otherwise an error is returned which + * describes why the definition failed. + * + * This function will take all of the exports of the `instance` provided and + * defined them under a module called `name` with a field name as the export's + * own name. + * + * For more information about name resolution consult the [Rust + * documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Linker.html#name-resolution). + */ +WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define_instance( + wasmtime_linker_t *linker, + wasmtime_context_t *store, + const char *name, + size_t name_len, + const wasmtime_instance_t *instance +); + +/** + * \brief Instantiates a #wasm_module_t with the items defined in this linker. + * + * \param linker the linker used to instantiate the provided module. + * \param store the store that is used to instantiate within + * \param module the module that is being instantiated. + * \param instance the returned instance, if successful. + * \param trap a trap returned, if the start function traps. + * + * \return One of three things can happen as a result of this function. First + * the module could be successfully instantiated and returned through + * `instance`, meaning the return value and `trap` are both set to `NULL`. + * Second the start function may trap, meaning the return value and `instance` + * are set to `NULL` and `trap` describes the trap that happens. Finally + * instantiation may fail for another reason, in which case an error is returned + * and `trap` and `instance` are set to `NULL`. + * + * This function will attempt to satisfy all of the imports of the `module` + * provided with items previously defined in this linker. If any name isn't + * defined in the linker than an error is returned. (or if the previously + * defined item is of the wrong type). + */ +WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_instantiate( + const wasmtime_linker_t *linker, + wasmtime_context_t *store, + const wasmtime_module_t *module, + wasmtime_instance_t *instance, + wasm_trap_t **trap +); + +/** + * \brief Defines automatic instantiations of a #wasm_module_t in this linker. + * + * \param linker the linker the module is being added to + * \param store the store that is used to instantiate `module` + * \param name the name of the module within the linker + * \param name_len the byte length of `name` + * \param module the module that's being instantiated + * + * \return An error if the module could not be instantiated or added or `NULL` + * on success. + * + * This function automatically handles [Commands and + * Reactors](https://github.com/WebAssembly/WASI/blob/master/design/application-abi.md#current-unstable-abi) + * instantiation and initialization. + * + * For more information see the [Rust + * documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Linker.html#method.module). + */ +WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_module( + wasmtime_linker_t *linker, + wasmtime_context_t *store, + const char *name, + size_t name_len, + const wasmtime_module_t *module +); + +/** + * \brief Acquires the "default export" of the named module in this linker. + * + * \param linker the linker to load from + * \param store the store to load a function into + * \param name the name of the module to get the default export for + * \param name_len the byte length of `name` + * \param func where to store the extracted default function. + * + * \return An error is returned if the default export could not be found, or + * `NULL` is returned and `func` is filled in otherwise. + * + * For more information see the [Rust + * documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Linker.html#method.get_default). + */ +WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_get_default( + const wasmtime_linker_t *linker, + wasmtime_context_t *store, + const char *name, + size_t name_len, + wasmtime_func_t *func +); + +/** + * \brief Loads an item by name from this linker. + * + * \param linker the linker to load from + * \param store the store to load the item into + * \param module the name of the module to get + * \param module_len the byte length of `module` + * \param name the name of the field to get + * \param name_len the byte length of `name` + * \param item where to store the extracted item + * + * \return A nonzero value if the item is defined, in which case `item` is also + * filled in. Otherwise zero is returned. + */ +WASM_API_EXTERN bool wasmtime_linker_get( + const wasmtime_linker_t *linker, + wasmtime_context_t *store, + const char *module, + size_t module_len, + const char *name, + size_t name_len, + wasmtime_extern_t *item +); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_LINKER_H diff --git a/lib/src/wasm/wasmtime/memory.h b/lib/src/wasm/wasmtime/memory.h new file mode 100644 index 00000000..6aecbaff --- /dev/null +++ b/lib/src/wasm/wasmtime/memory.h @@ -0,0 +1,123 @@ +/** + * \file wasmtime/memory.h + * + * Wasmtime API for interacting with wasm memories. + */ + +#ifndef WASMTIME_MEMORY_H +#define WASMTIME_MEMORY_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Creates a new memory type from the specified parameters. + * + * Note that this function is preferred over #wasm_memorytype_new for + * compatibility with the memory64 proposal. + */ +WASM_API_EXTERN wasm_memorytype_t *wasmtime_memorytype_new(uint64_t min, bool max_present, uint64_t max, bool is_64); + +/** + * \brief Returns the minimum size, in pages, of the specified memory type. + * + * Note that this function is preferred over #wasm_memorytype_limits for + * compatibility with the memory64 proposal. + */ +WASM_API_EXTERN uint64_t wasmtime_memorytype_minimum(const wasm_memorytype_t *ty); + +/** + * \brief Returns the maximum size, in pages, of the specified memory type. + * + * If this memory type doesn't have a maximum size listed then `false` is + * returned. Otherwise `true` is returned and the `max` pointer is filled in + * with the specified maximum size, in pages. + * + * Note that this function is preferred over #wasm_memorytype_limits for + * compatibility with the memory64 proposal. + */ +WASM_API_EXTERN bool wasmtime_memorytype_maximum(const wasm_memorytype_t *ty, uint64_t *max); + +/** + * \brief Returns whether this type of memory represents a 64-bit memory. + */ +WASM_API_EXTERN bool wasmtime_memorytype_is64(const wasm_memorytype_t *ty); + +/** + * \brief Creates a new WebAssembly linear memory + * + * \param store the store to create the memory within + * \param ty the type of the memory to create + * \param ret where to store the returned memory + * + * If an error happens when creating the memory it's returned and owned by the + * caller. If an error happens then `ret` is not filled in. + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_memory_new( + wasmtime_context_t *store, + const wasm_memorytype_t* ty, + wasmtime_memory_t *ret +); + +/** + * \brief Returns the tyep of the memory specified + */ +WASM_API_EXTERN wasm_memorytype_t* wasmtime_memory_type( + const wasmtime_context_t *store, + const wasmtime_memory_t *memory +); + +/** + * \brief Returns the base pointer in memory where the linear memory starts. + */ +WASM_API_EXTERN uint8_t *wasmtime_memory_data( + const wasmtime_context_t *store, + const wasmtime_memory_t *memory +); + +/** + * \brief Returns the byte length of this linear memory. + */ +WASM_API_EXTERN size_t wasmtime_memory_data_size( + const wasmtime_context_t *store, + const wasmtime_memory_t *memory +); + +/** + * \brief Returns the length, in WebAssembly pages, of this linear memory + */ +WASM_API_EXTERN uint64_t wasmtime_memory_size( + const wasmtime_context_t *store, + const wasmtime_memory_t *memory +); + +/** + * \brief Attempts to grow the specified memory by `delta` pages. + * + * \param store the store that owns `memory` + * \param memory the memory to grow + * \param delta the number of pages to grow by + * \param prev_size where to store the previous size of memory + * + * If memory cannot be grown then `prev_size` is left unchanged and an error is + * returned. Otherwise `prev_size` is set to the previous size of the memory, in + * WebAssembly pages, and `NULL` is returned. + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_memory_grow( + wasmtime_context_t *store, + const wasmtime_memory_t *memory, + uint64_t delta, + uint64_t *prev_size +); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_MEMORY_H diff --git a/lib/src/wasm/wasmtime/module.h b/lib/src/wasm/wasmtime/module.h new file mode 100644 index 00000000..deb6bcec --- /dev/null +++ b/lib/src/wasm/wasmtime/module.h @@ -0,0 +1,155 @@ +/** + * \file wasmtime/module.h + * + * APIs for interacting with modules in Wasmtime + */ + +#ifndef WASMTIME_MODULE_H +#define WASMTIME_MODULE_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \typedef wasmtime_module_t + * \brief Convenience alias for #wasmtime_module + * + * \struct wasmtime_module + * \brief A compiled Wasmtime module. + * + * This type represents a compiled WebAssembly module. The compiled module is + * ready to be instantiated and can be inspected for imports/exports. It is safe + * to use a module across multiple threads simultaneously. + */ +typedef struct wasmtime_module wasmtime_module_t; + +/** + * \brief Compiles a WebAssembly binary into a #wasmtime_module_t + * + * This function will compile a WebAssembly binary into an owned #wasm_module_t. + * This performs the same as #wasm_module_new except that it returns a + * #wasmtime_error_t type to get richer error information. + * + * On success the returned #wasmtime_error_t is `NULL` and the `ret` pointer is + * filled in with a #wasm_module_t. On failure the #wasmtime_error_t is + * non-`NULL` and the `ret` pointer is unmodified. + * + * This function does not take ownership of any of its arguments, but the + * returned error and module are owned by the caller. + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_module_new( + wasm_engine_t *engine, + const uint8_t *wasm, + size_t wasm_len, + wasmtime_module_t **ret +); + +/** + * \brief Deletes a module. + */ +WASM_API_EXTERN void wasmtime_module_delete(wasmtime_module_t *m); + +/** + * \brief Creates a shallow clone of the specified module, increasing the + * internal reference count. + */ +WASM_API_EXTERN wasmtime_module_t *wasmtime_module_clone(wasmtime_module_t *m); + +/** + * \brief Same as #wasm_module_imports, but for #wasmtime_module_t. + */ +WASM_API_EXTERN void wasmtime_module_imports( + const wasmtime_module_t *module, + wasm_importtype_vec_t *out +); + +/** + * \brief Same as #wasm_module_exports, but for #wasmtime_module_t. + */ +WASM_API_EXTERN void wasmtime_module_exports( + const wasmtime_module_t *module, + wasm_exporttype_vec_t *out +); + +/** + * \brief Validate a WebAssembly binary. + * + * This function will validate the provided byte sequence to determine if it is + * a valid WebAssembly binary within the context of the engine provided. + * + * This function does not take ownership of its arguments but the caller is + * expected to deallocate the returned error if it is non-`NULL`. + * + * If the binary validates then `NULL` is returned, otherwise the error returned + * describes why the binary did not validate. + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_module_validate( + wasm_engine_t *engine, + const uint8_t *wasm, + size_t wasm_len +); + +/** + * \brief This function serializes compiled module artifacts as blob data. + * + * \param module the module + * \param ret if the conversion is successful, this byte vector is filled in with + * the serialized compiled module. + * + * \return a non-null error if parsing fails, or returns `NULL`. If parsing + * fails then `ret` isn't touched. + * + * This function does not take ownership of `module`, and the caller is + * expected to deallocate the returned #wasmtime_error_t and #wasm_byte_vec_t. + */ +WASM_API_EXTERN wasmtime_error_t* wasmtime_module_serialize( + wasmtime_module_t* module, + wasm_byte_vec_t *ret +); + +/** + * \brief Build a module from serialized data. + * + * This function does not take ownership of any of its arguments, but the + * returned error and module are owned by the caller. + * + * This function is not safe to receive arbitrary user input. See the Rust + * documentation for more information on what inputs are safe to pass in here + * (e.g. only that of #wasmtime_module_serialize) + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_module_deserialize( + wasm_engine_t *engine, + const uint8_t *bytes, + size_t bytes_len, + wasmtime_module_t **ret +); + +/** + * \brief Deserialize a module from an on-disk file. + * + * This function is the same as #wasmtime_module_deserialize except that it + * reads the data for the serialized module from the path on disk. This can be + * faster than the alternative which may require copying the data around. + * + * This function does not take ownership of any of its arguments, but the + * returned error and module are owned by the caller. + * + * This function is not safe to receive arbitrary user input. See the Rust + * documentation for more information on what inputs are safe to pass in here + * (e.g. only that of #wasmtime_module_serialize) + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_module_deserialize_file( + wasm_engine_t *engine, + const char *path, + wasmtime_module_t **ret +); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_MODULE_H diff --git a/lib/src/wasm/wasmtime/store.h b/lib/src/wasm/wasmtime/store.h new file mode 100644 index 00000000..55c9f680 --- /dev/null +++ b/lib/src/wasm/wasmtime/store.h @@ -0,0 +1,185 @@ +/** + * \file wasmtime/store.h + * + * Wasmtime definition of a "store". + */ + +#ifndef WASMTIME_STORE_H +#define WASMTIME_STORE_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \typedef wasmtime_store_t + * \brief Convenience alias for #wasmtime_store_t + * + * \struct wasmtime_store + * \brief Storage of WebAssembly objects + * + * A store is the unit of isolation between WebAssembly instances in an + * embedding of Wasmtime. Values in one #wasmtime_store_t cannot flow into + * another #wasmtime_store_t. Stores are cheap to create and cheap to dispose. + * It's expected that one-off stores are common in embeddings. + * + * Objects stored within a #wasmtime_store_t are referenced with integer handles + * rather than interior pointers. This means that most APIs require that the + * store be explicitly passed in, which is done via #wasmtime_context_t. It is + * safe to move a #wasmtime_store_t to any thread at any time. A store generally + * cannot be concurrently used, however. + */ +typedef struct wasmtime_store wasmtime_store_t; + +/** + * \typedef wasmtime_context_t + * \brief Convenience alias for #wasmtime_context + * + * \struct wasmtime_context + * \brief An interior pointer into a #wasmtime_store_t which is used as + * "context" for many functions. + * + * This context pointer is used pervasively throught Wasmtime's API. This can be + * acquired from #wasmtime_store_context or #wasmtime_caller_context. The + * context pointer for a store is the same for the entire lifetime of a store, + * so it can safely be stored adjacent to a #wasmtime_store_t itself. + * + * Usage of a #wasmtime_context_t must not outlive the original + * #wasmtime_store_t. Additionally #wasmtime_context_t can only be used in + * situations where it has explicitly been granted access to doing so. For + * example finalizers cannot use #wasmtime_context_t because they are not given + * access to it. + */ +typedef struct wasmtime_context wasmtime_context_t; + +/** + * \brief Creates a new store within the specified engine. + * + * \param engine the compilation environment with configuration this store is + * connected to + * \param data user-provided data to store, can later be acquired with + * #wasmtime_context_get_data. + * \param finalizer an optional finalizer for `data` + * + * This function creates a fresh store with the provided configuration settings. + * The returned store must be deleted with #wasmtime_store_delete. + */ +WASM_API_EXTERN wasmtime_store_t *wasmtime_store_new( + wasm_engine_t *engine, + void *data, + void (*finalizer)(void*) +); + +/** + * \brief Returns the interior #wasmtime_context_t pointer to this store + */ +WASM_API_EXTERN wasmtime_context_t *wasmtime_store_context(wasmtime_store_t *store); + +/** + * \brief Deletes a store. + */ +WASM_API_EXTERN void wasmtime_store_delete(wasmtime_store_t *store); + +/** + * \brief Returns the user-specified data associated with the specified store + */ +WASM_API_EXTERN void *wasmtime_context_get_data(const wasmtime_context_t* context); + +/** + * \brief Overwrites the user-specified data associated with this store. + * + * Note that this does not execute the original finalizer for the provided data, + * and the original finalizer will be executed for the provided data when the + * store is deleted. + */ +WASM_API_EXTERN void wasmtime_context_set_data(wasmtime_context_t* context, void *data); + +/** + * \brief Perform garbage collection within the given context. + * + * Garbage collects `externref`s that are used within this store. Any + * `externref`s that are discovered to be unreachable by other code or objects + * will have their finalizers run. + * + * The `context` argument must not be NULL. + */ +WASM_API_EXTERN void wasmtime_context_gc(wasmtime_context_t* context); + +/** + * \brief Adds fuel to this context's store for wasm to consume while executing. + * + * For this method to work fuel consumption must be enabled via + * #wasmtime_config_consume_fuel_set. By default a store starts with 0 fuel + * for wasm to execute with (meaning it will immediately trap). + * This function must be called for the store to have + * some fuel to allow WebAssembly to execute. + * + * Note that at this time when fuel is entirely consumed it will cause + * wasm to trap. More usages of fuel are planned for the future. + * + * If fuel is not enabled within this store then an error is returned. If fuel + * is successfully added then NULL is returned. + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_context_add_fuel(wasmtime_context_t *store, uint64_t fuel); + +/** + * \brief Returns the amount of fuel consumed by this context's store execution + * so far. + * + * If fuel consumption is not enabled via #wasmtime_config_consume_fuel_set + * then this function will return false. Otherwise true is returned and the + * fuel parameter is filled in with fuel consuemd so far. + * + * Also note that fuel, if enabled, must be originally configured via + * #wasmtime_context_add_fuel. + */ +WASM_API_EXTERN bool wasmtime_context_fuel_consumed(const wasmtime_context_t *context, uint64_t *fuel); + +/** + * \brief Attempt to manually consume fuel from the store. + * + * If fuel consumption is not enabled via #wasmtime_config_consume_fuel_set then + * this function will return an error. Otherwise this will attempt to consume + * the specified amount of `fuel` from the store. If successful the remaining + * amount of fuel is stored into `remaining`. If `fuel` couldn't be consumed + * then an error is returned. + * + * Also note that fuel, if enabled, must be originally configured via + * #wasmtime_context_add_fuel. + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_context_consume_fuel(wasmtime_context_t *context, uint64_t fuel, uint64_t *remaining); + +/** + * \brief Configures WASI state within the specified store. + * + * This function is required if #wasmtime_linker_define_wasi is called. This + * will configure the WASI state for instances defined within this store to the + * configuration specified. + * + * This function does not take ownership of `context` but it does take ownership + * of `wasi`. The caller should no longer use `wasi` after calling this function + * (even if an error is returned). + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_context_set_wasi(wasmtime_context_t *context, wasi_config_t *wasi); + +/** + * \brief Configures the relative deadline at which point WebAssembly code will + * trap. + * + * This function configures the store-local epoch deadline after which point + * WebAssembly code will trap. + * + * See also #wasmtime_config_epoch_interruption_set. + */ +WASM_API_EXTERN void wasmtime_context_set_epoch_deadline(wasmtime_context_t *context, uint64_t ticks_beyond_current); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_STORE_H + diff --git a/lib/src/wasm/wasmtime/table.h b/lib/src/wasm/wasmtime/table.h new file mode 100644 index 00000000..630cee9e --- /dev/null +++ b/lib/src/wasm/wasmtime/table.h @@ -0,0 +1,126 @@ +/** + * \file wasmtime/table.h + * + * Wasmtime APIs for interacting with WebAssembly tables. + */ + +#ifndef WASMTIME_TABLE_H +#define WASMTIME_TABLE_H + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Creates a new host-defined wasm table. + * + * \param store the store to create the table within + * \param ty the type of the table to create + * \param init the initial value for this table's elements + * \param table where to store the returned table + * + * This function does not take ownership of any of its parameters, but yields + * ownership of returned error. This function may return an error if the `init` + * value does not match `ty`, for example. + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_table_new( + wasmtime_context_t *store, + const wasm_tabletype_t *ty, + const wasmtime_val_t *init, + wasmtime_table_t *table +); + +/** + * \brief Returns the type of this table. + * + * The caller has ownership of the returned #wasm_tabletype_t + */ +WASM_API_EXTERN wasm_tabletype_t* wasmtime_table_type( + const wasmtime_context_t *store, + const wasmtime_table_t *table +); + +/** + * \brief Gets a value in a table. + * + * \param store the store that owns `table` + * \param table the table to access + * \param index the table index to access + * \param val where to store the table's value + * + * This function will attempt to access a table element. If a nonzero value is + * returned then `val` is filled in and is owned by the caller. Otherwise zero + * is returned because the `index` is out-of-bounds. + */ +WASM_API_EXTERN bool wasmtime_table_get( + wasmtime_context_t *store, + const wasmtime_table_t *table, + uint32_t index, + wasmtime_val_t *val +); + +/** + * \brief Sets a value in a table. + * + * \param store the store that owns `table` + * \param table the table to write to + * \param index the table index to write + * \param value the value to store. + * + * This function will store `value` into the specified index in the table. This + * does not take ownership of any argument but yields ownership of the error. + * This function can fail if `value` has the wrong type for the table, or if + * `index` is out of bounds. + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_table_set( + wasmtime_context_t *store, + const wasmtime_table_t *table, + uint32_t index, + const wasmtime_val_t *value +); + +/** + * \brief Returns the size, in elements, of the specified table + */ +WASM_API_EXTERN uint32_t wasmtime_table_size( + const wasmtime_context_t *store, + const wasmtime_table_t *table +); + +/** + * \brief Grows a table. + * + * \param store the store that owns `table` + * \param table the table to grow + * \param delta the number of elements to grow the table by + * \param init the initial value for new table element slots + * \param prev_size where to store the previous size of the table before growth + * + * This function will attempt to grow the table by `delta` table elements. This + * can fail if `delta` would exceed the maximum size of the table or if `init` + * is the wrong type for this table. If growth is successful then `NULL` is + * returned and `prev_size` is filled in with the previous size of the table, in + * elements, before the growth happened. + * + * This function does not take ownership of any of its arguments. + */ +WASM_API_EXTERN wasmtime_error_t *wasmtime_table_grow( + wasmtime_context_t *store, + const wasmtime_table_t *table, + uint32_t delta, + const wasmtime_val_t *init, + uint32_t *prev_size +); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_TABLE_H + diff --git a/lib/src/wasm/wasmtime/trap.h b/lib/src/wasm/wasmtime/trap.h new file mode 100644 index 00000000..909a4801 --- /dev/null +++ b/lib/src/wasm/wasmtime/trap.h @@ -0,0 +1,106 @@ +/** + * \file wasmtime/trap.h + * + * Wasmtime APIs for interacting with traps and extensions to #wasm_trap_t. + */ + +#ifndef WASMTIME_TRAP_H +#define WASMTIME_TRAP_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Code of an instruction trap. + * + * See #wasmtime_trap_code_enum for possible values. + */ +typedef uint8_t wasmtime_trap_code_t; + +/** + * \brief Trap codes for instruction traps. + */ +enum wasmtime_trap_code_enum { + /// The current stack space was exhausted. + WASMTIME_TRAP_CODE_STACK_OVERFLOW, + /// An out-of-bounds memory access. + WASMTIME_TRAP_CODE_MEMORY_OUT_OF_BOUNDS, + /// A wasm atomic operation was presented with a not-naturally-aligned linear-memory address. + WASMTIME_TRAP_CODE_HEAP_MISALIGNED, + /// An out-of-bounds access to a table. + WASMTIME_TRAP_CODE_TABLE_OUT_OF_BOUNDS, + /// Indirect call to a null table entry. + WASMTIME_TRAP_CODE_INDIRECT_CALL_TO_NULL, + /// Signature mismatch on indirect call. + WASMTIME_TRAP_CODE_BAD_SIGNATURE, + /// An integer arithmetic operation caused an overflow. + WASMTIME_TRAP_CODE_INTEGER_OVERFLOW, + /// An integer division by zero. + WASMTIME_TRAP_CODE_INTEGER_DIVISION_BY_ZERO, + /// Failed float-to-int conversion. + WASMTIME_TRAP_CODE_BAD_CONVERSION_TO_INTEGER, + /// Code that was supposed to have been unreachable was reached. + WASMTIME_TRAP_CODE_UNREACHABLE_CODE_REACHED, + /// Execution has potentially run too long and may be interrupted. + WASMTIME_TRAP_CODE_INTERRUPT, +}; + +/** + * \brief Creates a new trap. + * + * \param msg the message to associate with this trap + * \param msg_len the byte length of `msg` + * + * The #wasm_trap_t returned is owned by the caller. + */ +WASM_API_EXTERN wasm_trap_t *wasmtime_trap_new(const char *msg, size_t msg_len); + +/** + * \brief Attempts to extract the trap code from this trap. + * + * Returns `true` if the trap is an instruction trap triggered while + * executing Wasm. If `true` is returned then the trap code is returned + * through the `code` pointer. If `false` is returned then this is not + * an instruction trap -- traps can also be created using wasm_trap_new, + * or occur with WASI modules exiting with a certain exit code. + */ +WASM_API_EXTERN bool wasmtime_trap_code(const wasm_trap_t*, wasmtime_trap_code_t *code); + +/** + * \brief Attempts to extract a WASI-specific exit status from this trap. + * + * Returns `true` if the trap is a WASI "exit" trap and has a return status. If + * `true` is returned then the exit status is returned through the `status` + * pointer. If `false` is returned then this is not a wasi exit trap. + */ +WASM_API_EXTERN bool wasmtime_trap_exit_status(const wasm_trap_t*, int *status); + +/** + * \brief Returns a human-readable name for this frame's function. + * + * This function will attempt to load a human-readable name for function this + * frame points to. This function may return `NULL`. + * + * The lifetime of the returned name is the same as the #wasm_frame_t itself. + */ +WASM_API_EXTERN const wasm_name_t *wasmtime_frame_func_name(const wasm_frame_t*); + +/** + * \brief Returns a human-readable name for this frame's module. + * + * This function will attempt to load a human-readable name for module this + * frame points to. This function may return `NULL`. + * + * The lifetime of the returned name is the same as the #wasm_frame_t itself. + */ +WASM_API_EXTERN const wasm_name_t *wasmtime_frame_module_name(const wasm_frame_t*); + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_TRAP_H diff --git a/lib/src/wasm/wasmtime/val.h b/lib/src/wasm/wasmtime/val.h new file mode 100644 index 00000000..ae0a1961 --- /dev/null +++ b/lib/src/wasm/wasmtime/val.h @@ -0,0 +1,232 @@ +/** + * \file wasmtime/val.h + * + * APIs for interacting with WebAssembly values in Wasmtime. + */ + +#ifndef WASMTIME_VAL_H +#define WASMTIME_VAL_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \typedef wasmtime_externref_t + * \brief Convenience alias for #wasmtime_externref + * + * \struct wasmtime_externref + * \brief A host-defined un-forgeable reference to pass into WebAssembly. + * + * This structure represents an `externref` that can be passed to WebAssembly. + * It cannot be forged by WebAssembly itself and is guaranteed to have been + * created by the host. + */ +typedef struct wasmtime_externref wasmtime_externref_t; + +/** + * \brief Create a new `externref` value. + * + * Creates a new `externref` value wrapping the provided data, returning the + * pointer to the externref. + * + * \param data the host-specific data to wrap + * \param finalizer an optional finalizer for `data` + * + * When the reference is reclaimed, the wrapped data is cleaned up with the + * provided `finalizer`. + * + * The returned value must be deleted with #wasmtime_externref_delete + */ +WASM_API_EXTERN wasmtime_externref_t *wasmtime_externref_new(void *data, void (*finalizer)(void*)); + +/** + * \brief Get an `externref`'s wrapped data + * + * Returns the original `data` passed to #wasmtime_externref_new. It is required + * that `data` is not `NULL`. + */ +WASM_API_EXTERN void *wasmtime_externref_data(wasmtime_externref_t *data); + +/** + * \brief Creates a shallow copy of the `externref` argument, returning a + * separately owned pointer (increases the reference count). + */ +WASM_API_EXTERN wasmtime_externref_t *wasmtime_externref_clone(wasmtime_externref_t *ref); + +/** + * \brief Decrements the reference count of the `ref`, deleting it if it's the + * last reference. + */ +WASM_API_EXTERN void wasmtime_externref_delete(wasmtime_externref_t *ref); + +/** + * \brief Converts a raw `externref` value coming from #wasmtime_val_raw_t into + * a #wasmtime_externref_t. + * + * Note that the returned #wasmtime_externref_t is an owned value that must be + * deleted via #wasmtime_externref_delete by the caller if it is non-null. + */ +WASM_API_EXTERN wasmtime_externref_t *wasmtime_externref_from_raw(wasmtime_context_t *context, size_t raw); + +/** + * \brief Converts a #wasmtime_externref_t to a raw value suitable for storing + * into a #wasmtime_val_raw_t. + * + * Note that the returned underlying value is not tracked by Wasmtime's garbage + * collector until it enters WebAssembly. This means that a GC may release the + * context's reference to the raw value, making the raw value invalid within the + * context of the store. Do not perform a GC between calling this function and + * passing it to WebAssembly. + */ +WASM_API_EXTERN size_t wasmtime_externref_to_raw( + wasmtime_context_t *context, + const wasmtime_externref_t *ref); + +/// \brief Discriminant stored in #wasmtime_val::kind +typedef uint8_t wasmtime_valkind_t; +/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is an i32 +#define WASMTIME_I32 0 +/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is an i64 +#define WASMTIME_I64 1 +/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is a f32 +#define WASMTIME_F32 2 +/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is a f64 +#define WASMTIME_F64 3 +/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is a v128 +#define WASMTIME_V128 4 +/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is a funcref +#define WASMTIME_FUNCREF 5 +/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is an externref +#define WASMTIME_EXTERNREF 6 + +/// \brief A 128-bit value representing the WebAssembly `v128` type. Bytes are +/// stored in little-endian order. +typedef uint8_t wasmtime_v128[16]; + +/** + * \typedef wasmtime_valunion_t + * \brief Convenience alias for #wasmtime_valunion + * + * \union wasmtime_valunion + * \brief Container for different kinds of wasm values. + * + * This type is contained in #wasmtime_val_t and contains the payload for the + * various kinds of items a value can be. + */ +typedef union wasmtime_valunion { + /// Field used if #wasmtime_val_t::kind is #WASMTIME_I32 + /// + /// Note that this field is always stored in a little-endian format. + int32_t i32; + /// Field used if #wasmtime_val_t::kind is #WASMTIME_I64 + /// + /// Note that this field is always stored in a little-endian format. + int64_t i64; + /// Field used if #wasmtime_val_t::kind is #WASMTIME_F32 + /// + /// Note that this field is always stored in a little-endian format. + float32_t f32; + /// Field used if #wasmtime_val_t::kind is #WASMTIME_F64 + /// + /// Note that this field is always stored in a little-endian format. + float64_t f64; + /// Field used if #wasmtime_val_t::kind is #WASMTIME_FUNCREF + /// + /// If this value represents a `ref.null func` value then the `store_id` field + /// is set to zero. + /// + /// Note that this field is always stored in a little-endian format. + wasmtime_func_t funcref; + /// Field used if #wasmtime_val_t::kind is #WASMTIME_EXTERNREF + /// + /// If this value represents a `ref.null extern` value then this pointer will + /// be `NULL`. + /// + /// Note that this field is always stored in a little-endian format. + wasmtime_externref_t *externref; + /// Field used if #wasmtime_val_t::kind is #WASMTIME_V128 + /// + /// Note that this field is always stored in a little-endian format. + wasmtime_v128 v128; +} wasmtime_valunion_t; + +/** + * \typedef wasmtime_val_raw_t + * \brief Convenience alias for #wasmtime_val_raw + * + * \union wasmtime_val_raw + * \brief Container for possible wasm values. + * + * This type is used on conjunction with #wasmtime_func_new_unchecked as well + * as #wasmtime_func_call_unchecked. Instances of this type do not have type + * information associated with them, it's up to the embedder to figure out + * how to interpret the bits contained within, often using some other channel + * to determine the type. + */ +typedef union wasmtime_val_raw { + /// Field for when this val is a WebAssembly `i32` value. + int32_t i32; + /// Field for when this val is a WebAssembly `i64` value. + int64_t i64; + /// Field for when this val is a WebAssembly `f32` value. + float32_t f32; + /// Field for when this val is a WebAssembly `f64` value. + float64_t f64; + /// Field for when this val is a WebAssembly `v128` value. + wasmtime_v128 v128; + /// Field for when this val is a WebAssembly `funcref` value. + /// + /// If this is set to 0 then it's a null funcref, otherwise this must be + /// passed to `wasmtime_func_from_raw` to determine the `wasmtime_func_t`. + size_t funcref; + /// Field for when this val is a WebAssembly `externref` value. + /// + /// If this is set to 0 then it's a null externref, otherwise this must be + /// passed to `wasmtime_externref_from_raw` to determine the + /// `wasmtime_externref_t`. + size_t externref; +} wasmtime_val_raw_t; + +/** + * \typedef wasmtime_val_t + * \brief Convenience alias for #wasmtime_val_t + * + * \union wasmtime_val + * \brief Container for different kinds of wasm values. + * + * Note that this structure may contain an owned value, namely + * #wasmtime_externref_t, depending on the context in which this is used. APIs + * which consume a #wasmtime_val_t do not take ownership, but APIs that return + * #wasmtime_val_t require that #wasmtime_val_delete is called to deallocate + * the value. + */ +typedef struct wasmtime_val { + /// Discriminant of which field of #of is valid. + wasmtime_valkind_t kind; + /// Container for the extern item's value. + wasmtime_valunion_t of; +} wasmtime_val_t; + +/** + * \brief Delets an owned #wasmtime_val_t. + * + * Note that this only deletes the contents, not the memory that `val` points to + * itself (which is owned by the caller). + */ +WASM_API_EXTERN void wasmtime_val_delete(wasmtime_val_t *val); + +/** + * \brief Copies `src` into `dst`. + */ +WASM_API_EXTERN void wasmtime_val_copy(wasmtime_val_t *dst, const wasmtime_val_t *src); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_VAL_H +