diff --git a/CHANGELOG.md b/CHANGELOG.md index e093356fb2b..c25d00a6157 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ ### Changed +- [#1851](https://github.com/wasmerio/wasmer/pull/1851) Improve test suite and documentation of the Wasmer C API - [#1874](https://github.com/wasmerio/wasmer/pull/1874) Set `CompilerConfig` to be owned (following wasm-c-api) - [#1880](https://github.com/wasmerio/wasmer/pull/1880) Remove cmake dependency for tests diff --git a/lib/c-api/src/wasm_c_api/engine.rs b/lib/c-api/src/wasm_c_api/engine.rs index 24daf409c56..0174032ec56 100644 --- a/lib/c-api/src/wasm_c_api/engine.rs +++ b/lib/c-api/src/wasm_c_api/engine.rs @@ -79,7 +79,35 @@ pub struct wasm_config_t { compiler: wasmer_compiler_t, } -/// Create a new Wasmer configuration. +/// Create a new default Wasmer configuration. +/// +/// # Example +/// +/// ```rust +/// # use inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer_wasm.h" +/// # +/// int main() { +/// // Create the configuration. +/// wasm_config_t* config = wasm_config_new(); +/// +/// // Create the engine. +/// wasm_engine_t* engine = wasm_engine_new_with_config(config); +/// +/// // Check we have an engine! +/// assert(engine); +/// +/// // Free everything. +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` /// /// cbindgen:ignore #[no_mangle] @@ -87,7 +115,40 @@ pub extern "C" fn wasm_config_new() -> Box { Box::new(wasm_config_t::default()) } -/// Configure the compiler to use. +/// Updates the configuration to specify a particular compiler to use. +/// +/// This is a Wasmer-specific function. +/// +/// # Example +/// +/// ```rust,no_run +/// # use inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer_wasm.h" +/// # +/// int main() { +/// // Create the configuration. +/// wasm_config_t* config = wasm_config_new(); +/// +/// // Use the Cranelift compiler. +/// wasm_config_set_compiler(config, CRANELIFT); +/// +/// // Create the engine. +/// wasm_engine_t* engine = wasm_engine_new_with_config(config); +/// +/// // Check we have an engine! +/// assert(engine); +/// +/// // Free everything. +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` #[cfg(feature = "compiler")] #[no_mangle] pub extern "C" fn wasm_config_set_compiler( @@ -97,7 +158,40 @@ pub extern "C" fn wasm_config_set_compiler( config.compiler = compiler; } -/// Configure the engine to use. +/// Updates the configuration to specify a particular engine to use. +/// +/// This is a Wasmer-specific function. +/// +/// # Example +/// +/// ```rust,no_run +/// # use inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer_wasm.h" +/// # +/// int main() { +/// // Create the configuration. +/// wasm_config_t* config = wasm_config_new(); +/// +/// // Use the JIT engine. +/// wasm_config_set_engine(config, JIT); +/// +/// // Create the engine. +/// wasm_engine_t* engine = wasm_engine_new_with_config(config); +/// +/// // Check we have an engine! +/// assert(engine); +/// +/// // Free everything. +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` #[no_mangle] pub extern "C" fn wasm_config_set_engine(config: &mut wasm_config_t, engine: wasmer_engine_t) { config.engine = engine; @@ -132,6 +226,12 @@ fn get_default_compiler_config() -> Box { cfg_if! { if #[cfg(all(feature = "jit", feature = "compiler"))] { + /// Creates a new JIT engine with the default compiler. + /// + /// # Example + /// + /// See `wasm_engine_delete`. + /// /// cbindgen:ignore #[no_mangle] pub extern "C" fn wasm_engine_new() -> Box { @@ -139,17 +239,26 @@ cfg_if! { let engine: Arc = Arc::new(JIT::new(compiler_config).engine()); Box::new(wasm_engine_t { inner: engine }) } - } - else if #[cfg(feature = "jit")] { - // Headless JIT + } else if #[cfg(feature = "jit")] { + /// Creates a new headless JIT engine. + /// + /// # Example + /// + /// See `wasm_engine_delete`. + /// /// cbindgen:ignore #[no_mangle] pub extern "C" fn wasm_engine_new() -> Box { let engine: Arc = Arc::new(JIT::headless().engine()); Box::new(wasm_engine_t { inner: engine }) } - } - else if #[cfg(all(feature = "native", feature = "compiler"))] { + } else if #[cfg(all(feature = "native", feature = "compiler"))] { + /// Creates a new native engine with the default compiler. + /// + /// # Example + /// + /// See `wasm_engine_delete`. + /// /// cbindgen:ignore #[no_mangle] pub extern "C" fn wasm_engine_new() -> Box { @@ -157,8 +266,13 @@ cfg_if! { let engine: Arc = Arc::new(Native::new(compiler_config).engine()); Box::new(wasm_engine_t { inner: engine }) } - } - else if #[cfg(feature = "native")] { + } else if #[cfg(feature = "native")] { + /// Creates a new headless native engine. + /// + /// # Example + /// + /// See `wasm_engine_delete`. + /// /// cbindgen:ignore #[no_mangle] pub extern "C" fn wasm_engine_new() -> Box { @@ -169,26 +283,70 @@ cfg_if! { // There are currently no uses of the object-file engine + compiler from the C API. // So if we get here, we default to headless mode regardless of if `compiler` is enabled. else if #[cfg(feature = "object-file")] { + /// Creates a new headless object-file engine. + /// + /// # Example + /// + /// See `wasm_engine_delete`. + /// /// cbindgen:ignore #[no_mangle] pub extern "C" fn wasm_engine_new() -> Box { let engine: Arc = Arc::new(ObjectFile::headless().engine()); Box::new(wasm_engine_t { inner: engine }) } - } - else { + } else { + /// Creates a new unknown engine, i.e. it will panic with an error message. + /// + /// # Example + /// + /// See `wasm_engine_delete`. + /// /// cbindgen:ignore #[no_mangle] pub extern "C" fn wasm_engine_new() -> Box { - unimplemented!("The JITEngine is not attached; You might want to recompile `wasmer_c_api` with `--feature jit`"); + unimplemented!("No engine attached; You might want to recompile `wasmer_c_api` with for example `--feature jit`"); } } } +/// Deletes an engine. +/// +/// # Example +/// +/// ```rust +/// # use inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer_wasm.h" +/// # +/// int main() { +/// // Create a default engine. +/// wasm_engine_t* engine = wasm_engine_new(); +/// +/// // Check we have an engine! +/// assert(engine); +/// +/// // Free everything. +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +/// /// cbindgen:ignore #[no_mangle] pub unsafe extern "C" fn wasm_engine_delete(_engine: Option>) {} +/// Creates an engine with a particular configuration. +/// +/// # Example +/// +/// See `wasm_config_new`. +/// /// cbindgen:ignore #[no_mangle] pub extern "C" fn wasm_engine_new_with_config( @@ -305,3 +463,25 @@ pub extern "C" fn wasm_engine_new_with_config( } } } + +#[cfg(test)] +mod tests { + use inline_c::assert_c; + + #[test] + fn test_engine_new() { + (assert_c! { + #include "tests/wasmer_wasm.h" + + int main() { + wasm_engine_t* engine = wasm_engine_new(); + assert(engine); + + wasm_engine_delete(engine); + + return 0; + } + }) + .success(); + } +} diff --git a/lib/c-api/src/wasm_c_api/instance.rs b/lib/c-api/src/wasm_c_api/instance.rs index d15b658c735..893ba3fb192 100644 --- a/lib/c-api/src/wasm_c_api/instance.rs +++ b/lib/c-api/src/wasm_c_api/instance.rs @@ -7,11 +7,31 @@ use std::mem; use std::sync::Arc; use wasmer::{Extern, Instance, InstantiationError}; +/// Opaque type representing a WebAssembly instance. #[allow(non_camel_case_types)] pub struct wasm_instance_t { pub(crate) inner: Arc, } +/// Creates a new instance from a WebAssembly module and a +/// set of imports. +/// +/// ## Errors +/// +/// The function can fail in 2 ways: +/// +/// 1. Link errors that happen when plugging the imports into the +/// instance, +/// 2. Runtime errors that happen when running the module `start` +/// function. +/// +/// Failures are stored in the `traps` argument; the program doesn't +/// panic. +/// +/// # Notes +/// +/// The `store` argument is ignored. The store from the given module +/// will be used. #[no_mangle] pub unsafe extern "C" fn wasm_instance_new( _store: Option<&wasm_store_t>, @@ -60,9 +80,15 @@ pub unsafe extern "C" fn wasm_instance_new( Some(Box::new(wasm_instance_t { inner: instance })) } +/// Deletes an instance. +/// +/// # Example +/// +/// See `wasm_instance_new`. #[no_mangle] pub unsafe extern "C" fn wasm_instance_delete(_instance: Option>) {} +/// Gets the exports of the instance. #[no_mangle] pub unsafe extern "C" fn wasm_instance_exports( instance: &wasm_instance_t, @@ -93,3 +119,104 @@ pub unsafe extern "C" fn wasm_instance_exports( mem::forget(extern_vec); } + +#[cfg(test)] +mod tests { + use inline_c::assert_c; + + #[test] + fn test_instance_new() { + (assert_c! { + #include "tests/wasmer_wasm.h" + + // The `sum` host function implementation. + wasm_trap_t* sum_callback( + const wasm_val_vec_t* arguments, + wasm_val_vec_t* results + ) { + wasm_val_t sum = { + .kind = WASM_I32, + .of = { arguments->data[0].of.i32 + arguments->data[1].of.i32 }, + }; + results->data[0] = sum; + + return NULL; + } + + int main() { + // Create the engine and the store. + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + // Create a WebAssembly module from a WAT definition. + wasm_byte_vec_t wat; + wasmer_byte_vec_new_from_string( + &wat, + "(module\n" + " (import \"math\" \"sum\" (func $sum (param i32 i32) (result i32)))\n" + " (func (export \"add_one\") (param i32) (result i32)\n" + " local.get 0\n" + " i32.const 1\n" + " call $sum))" + ); + wasm_byte_vec_t wasm; + wat2wasm(&wat, &wasm); + + // Create the module. + wasm_module_t* module = wasm_module_new(store, &wasm); + + assert(module); + + // Prepare the imports. + wasm_functype_t* sum_type = wasm_functype_new_2_1( + wasm_valtype_new_i32(), + wasm_valtype_new_i32(), + wasm_valtype_new_i32() + ); + wasm_func_t* sum_function = wasm_func_new(store, sum_type, sum_callback); + wasm_extern_t* externs[] = { wasm_func_as_extern(sum_function) }; + wasm_extern_vec_t imports = WASM_ARRAY_VEC(externs); + + // Instantiate the module. + wasm_trap_t* traps = NULL; + wasm_instance_t* instance = wasm_instance_new(store, module, &imports, &traps); + + assert(instance); + + // Run the exported function. + wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + + assert(exports.size == 1); + + const wasm_func_t* run_function = wasm_extern_as_func(exports.data[0]); + + assert(run_function); + + wasm_val_t arguments[1] = { WASM_I32_VAL(1) }; + wasm_val_t results[1] = { WASM_INIT_VAL }; + + wasm_val_vec_t arguments_as_array = WASM_ARRAY_VEC(arguments); + wasm_val_vec_t results_as_array = WASM_ARRAY_VEC(results); + + wasm_trap_t* trap = wasm_func_call(run_function, &arguments_as_array, &results_as_array); + + assert(trap == NULL); + assert(results[0].of.i32 == 2); + + // Free everything. + wasm_instance_delete(instance); + wasm_func_delete(sum_function); + wasm_functype_delete(sum_type); + wasm_module_delete(module); + wasm_byte_vec_delete(&wasm); + wasm_byte_vec_delete(&wat); + wasm_store_delete(store); + wasm_engine_delete(engine); + + return 0; + } + }) + .success(); + } +} diff --git a/lib/c-api/src/wasm_c_api/macros.rs b/lib/c-api/src/wasm_c_api/macros.rs index c425d985da7..bb32783695f 100644 --- a/lib/c-api/src/wasm_c_api/macros.rs +++ b/lib/c-api/src/wasm_c_api/macros.rs @@ -25,14 +25,17 @@ macro_rules! wasm_declare_vec { } impl<'a> From]>> for [] { - fn from(other: Vec<[]>) -> Self { - let mut boxed_slice = other.into_boxed_slice(); - let size = boxed_slice.len(); - let data = boxed_slice.as_mut_ptr(); - ::std::mem::forget(boxed_slice); + fn from(mut vec: Vec<[]>) -> Self { + vec.shrink_to_fit(); + + let length = vec.len(); + let pointer = vec.as_mut_ptr(); + + ::std::mem::forget(vec); + Self { - size, - data, + size: length, + data: pointer, } } } @@ -100,7 +103,6 @@ macro_rules! wasm_declare_vec { ::std::mem::forget(bytes); } - #[no_mangle] pub unsafe extern "C" fn [](ptr: Option<&mut []>) { if let Some(vec) = ptr { diff --git a/lib/c-api/src/wasm_c_api/mod.rs b/lib/c-api/src/wasm_c_api/mod.rs index 58eefb2a0e3..5fa5cf29bea 100644 --- a/lib/c-api/src/wasm_c_api/mod.rs +++ b/lib/c-api/src/wasm_c_api/mod.rs @@ -3,14 +3,34 @@ #[macro_use] pub mod macros; +/// The engine drives the compilation and the runtime. +/// +/// Entry points: A default engine is created with `wasm_engine_new` +/// and freed with `wasm_engine_delete`. pub mod engine; /// cbindgen:ignore pub mod externals; +/// A WebAssembly instance is a stateful, executable instance of a +/// WebAssembly module. +/// +/// Instance objects contain all the exported WebAssembly functions, +/// memories, tables and globals that allow interacting with +/// WebAssembly. +/// +/// Entry points: A WebAssembly instance is created with +/// `wasm_instance_new` and freed with `wasm_instance_delete`. +/// /// cbindgen:ignore pub mod instance; +/// A WebAssembly module contains stateless WebAssembly code that has +/// already been compiled and can be instantiated multiple times. +/// +/// Entry points: A WebAssembly module is created with +/// `wasm_module_new` and freed with `wasm_module_delete`. +/// /// cbindgen:ignore pub mod module; diff --git a/lib/c-api/src/wasm_c_api/module.rs b/lib/c-api/src/wasm_c_api/module.rs index 92a71d17e59..feddc94f4a0 100644 --- a/lib/c-api/src/wasm_c_api/module.rs +++ b/lib/c-api/src/wasm_c_api/module.rs @@ -8,6 +8,7 @@ use std::ptr::NonNull; use std::sync::Arc; use wasmer::Module; +/// Opaque type representing a WebAssembly module. #[allow(non_camel_case_types)] pub struct wasm_module_t { pub(crate) inner: Arc, @@ -24,7 +25,7 @@ pub struct wasm_module_t { /// Before the code is compiled, it will be validated using the store /// features. /// -/// # Examples +/// # Example /// /// ```rust /// # use inline_c::assert_c; @@ -33,23 +34,29 @@ pub struct wasm_module_t { /// # #include "tests/wasmer_wasm.h" /// # /// int main() { +/// // Create the engine and the store. /// wasm_engine_t* engine = wasm_engine_new(); /// wasm_store_t* store = wasm_store_new(engine); -/// +/// +/// // Create a WebAssembly module from a WAT definition. /// wasm_byte_vec_t wat; /// wasmer_byte_vec_new_from_string(&wat, "(module)"); /// wasm_byte_vec_t wasm; /// wat2wasm(&wat, &wasm); /// +/// // Create the module. /// wasm_module_t* module = wasm_module_new(store, &wasm); + +/// // It works! /// assert(module); /// +/// // Free everything. /// wasm_byte_vec_delete(&wasm); /// wasm_byte_vec_delete(&wat); /// wasm_module_delete(module); /// wasm_store_delete(store); /// wasm_engine_delete(engine); -/// +/// /// return 0; /// } /// # }) @@ -72,9 +79,55 @@ pub unsafe extern "C" fn wasm_module_new( })) } +/// Destruct a WebAssembly module. +/// +/// # Example +/// +/// See `wasm_module_new`. #[no_mangle] pub unsafe extern "C" fn wasm_module_delete(_module: Option>) {} +/// Validates a new WebAssembly module given the configuration +/// in the `wasm_store_t`. +/// +/// This validation is normally pretty fast and checks the enabled +/// WebAssembly features in the store engine (`wasm_engine_t`) to +/// assure deterministic validation of the module. +/// +/// # Example +/// +/// ```rust +/// # use inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer_wasm.h" +/// # +/// int main() { +/// // Create the engine and the store. +/// wasm_engine_t* engine = wasm_engine_new(); +/// wasm_store_t* store = wasm_store_new(engine); +/// +/// // Create a WebAssembly module from a WAT definition. +/// wasm_byte_vec_t wat; +/// wasmer_byte_vec_new_from_string(&wat, "(module)"); +/// wasm_byte_vec_t wasm; +/// wat2wasm(&wat, &wasm); +/// +/// // Validate that the WebAssembly module is correct. +/// assert(wasm_module_validate(store, &wasm)); +/// +/// // Free everything. +/// wasm_byte_vec_delete(&wasm); +/// wasm_byte_vec_delete(&wat); +/// wasm_store_delete(store); +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` #[no_mangle] pub unsafe extern "C" fn wasm_module_validate( store: Option<&wasm_store_t>, @@ -103,6 +156,114 @@ pub unsafe extern "C" fn wasm_module_validate( } } +/// Returns an array of the exported types in the module. +/// +/// The order of the exports is guaranteed to be the same as in the +/// WebAssembly bytecode. +/// +/// # Example +/// +/// ```rust +/// # use inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer_wasm.h" +/// # +/// int main() { +/// // Create the engine and the store. +/// wasm_engine_t* engine = wasm_engine_new(); +/// wasm_store_t* store = wasm_store_new(engine); +/// +/// // Create a WebAssembly module from a WAT definition. +/// wasm_byte_vec_t wat; +/// wasmer_byte_vec_new_from_string( +/// &wat, +/// "(module\n" +/// " (func (export \"function\") (param i32 i64))\n" +/// " (global (export \"global\") i32 (i32.const 7))\n" +/// " (table (export \"table\") 0 funcref)\n" +/// " (memory (export \"memory\") 1))" +/// ); +/// wasm_byte_vec_t wasm; +/// wat2wasm(&wat, &wasm); +/// +/// // Create the module. +/// wasm_module_t* module = wasm_module_new(store, &wasm); +/// assert(module); +/// +/// // Extract the types exported by this module. +/// wasm_exporttype_vec_t export_types; +/// wasm_module_exports(module, &export_types); +/// +/// // We have 4 of them. +/// assert(export_types.size == 4); +/// +/// // The first one is a function. Use +/// // `wasm_externtype_as_functype_const` to continue to inspect the +/// // type. +/// { +/// wasm_exporttype_t* export_type = export_types.data[0]; +/// +/// const wasm_name_t* export_name = wasm_exporttype_name(export_type); +/// wasmer_assert_name(export_name, "function"); +/// +/// const wasm_externtype_t* extern_type = wasm_exporttype_type(export_type); +/// assert(wasm_externtype_kind(extern_type) == WASM_EXTERN_FUNC); +/// } +/// +/// // The second one is a global. Use +/// // `wasm_externtype_as_globaltype_const` to continue to inspect the +/// // type. +/// { +/// wasm_exporttype_t* export_type = export_types.data[1]; +/// +/// const wasm_name_t* export_name = wasm_exporttype_name(export_type); +/// wasmer_assert_name(export_name, "global"); +/// +/// const wasm_externtype_t* extern_type = wasm_exporttype_type(export_type); +/// assert(wasm_externtype_kind(extern_type) == WASM_EXTERN_GLOBAL); +/// } +/// +/// // The third one is a table. Use +/// // `wasm_externtype_as_tabletype_const` to continue to inspect the +/// // type. +/// { +/// wasm_exporttype_t* export_type = export_types.data[2]; +/// +/// const wasm_name_t* export_name = wasm_exporttype_name(export_type); +/// wasmer_assert_name(export_name, "table"); +/// +/// const wasm_externtype_t* extern_type = wasm_exporttype_type(export_type); +/// assert(wasm_externtype_kind(extern_type) == WASM_EXTERN_TABLE); +/// } +/// +/// // The fourth one is a memory. Use +/// // `wasm_externtype_as_memorytype_const` to continue to inspect the +/// // type. +/// { +/// wasm_exporttype_t* export_type = export_types.data[3]; +/// +/// const wasm_name_t* export_name = wasm_exporttype_name(export_type); +/// wasmer_assert_name(export_name, "memory"); +/// +/// const wasm_externtype_t* extern_type = wasm_exporttype_type(export_type); +/// assert(wasm_externtype_kind(extern_type) == WASM_EXTERN_MEMORY); +/// } +/// +/// // Free everything. +/// wasm_exporttype_vec_delete(&export_types); +/// wasm_byte_vec_delete(&wasm); +/// wasm_byte_vec_delete(&wat); +/// wasm_module_delete(module); +/// wasm_store_delete(store); +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` #[no_mangle] pub unsafe extern "C" fn wasm_module_exports( module: &wasm_module_t, @@ -119,6 +280,131 @@ pub unsafe extern "C" fn wasm_module_exports( *out = exports.into(); } +/// Returns an array of the imported types in the module. +/// +/// The order of the imports is guaranteed to be the same as in the +/// WebAssembly bytecode. +/// +/// # Example +/// +/// ```rust +/// # use inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer_wasm.h" +/// # +/// int main() { +/// // Create the engine and the store. +/// wasm_engine_t* engine = wasm_engine_new(); +/// wasm_store_t* store = wasm_store_new(engine); +/// +/// // Create a WebAssembly module from a WAT definition. +/// wasm_byte_vec_t wat; +/// wasmer_byte_vec_new_from_string( +/// &wat, +/// "(module\n" +/// " (import \"ns\" \"function\" (func))\n" +/// " (import \"ns\" \"global\" (global f32))\n" +/// " (import \"ns\" \"table\" (table 1 2 anyfunc))\n" +/// " (import \"ns\" \"memory\" (memory 3 4)))" +/// ); +/// wasm_byte_vec_t wasm; +/// wat2wasm(&wat, &wasm); +/// +/// // Create the module. +/// wasm_module_t* module = wasm_module_new(store, &wasm); +/// assert(module); +/// +/// // Extract the types imported by the module. +/// wasm_importtype_vec_t import_types; +/// wasm_module_imports(module, &import_types); +/// +/// // We have 4 of them. +/// assert(import_types.size == 4); +/// +/// // The first one is a function. Use +/// // `wasm_externtype_as_functype_const` to continue to inspect the +/// // type. +/// { +/// const wasm_importtype_t* import_type = import_types.data[0]; +/// +/// const wasm_name_t* import_module = wasm_importtype_module(import_type); +/// wasmer_assert_name(import_module, "ns"); +/// +/// const wasm_name_t* import_name = wasm_importtype_name(import_type); +/// wasmer_assert_name(import_name, "function"); +/// +/// const wasm_externtype_t* extern_type = wasm_importtype_type(import_type); +/// assert(wasm_externtype_kind(extern_type) == WASM_EXTERN_FUNC); +/// } +/// +/// // The second one is a global. Use +/// // `wasm_externtype_as_globaltype_const` to continue to inspect +/// // the type. +/// { +/// const wasm_importtype_t* import_type = import_types.data[1]; +/// +/// const wasm_name_t* import_module = wasm_importtype_module(import_type); +/// wasmer_assert_name(import_module, "ns"); +/// +/// const wasm_name_t* import_name = wasm_importtype_name(import_type); +/// wasmer_assert_name(import_name, "global"); +/// +/// const wasm_externtype_t* extern_type = wasm_importtype_type(import_type); +/// assert(wasm_externtype_kind(extern_type) == WASM_EXTERN_GLOBAL); +/// } +/// +/// // The third one is a table. Use +/// // `wasm_externtype_as_tabletype_const` to continue to inspect +/// // the type. +/// { +/// const wasm_importtype_t* import_type = import_types.data[2]; +/// +/// const wasm_name_t* import_module = wasm_importtype_module(import_type); +/// wasmer_assert_name(import_module, "ns"); +/// +/// const wasm_name_t* import_name = wasm_importtype_name(import_type); +/// wasmer_assert_name(import_name, "table"); +/// +/// const wasm_externtype_t* extern_type = wasm_importtype_type(import_type); +/// assert(wasm_externtype_kind(extern_type) == WASM_EXTERN_TABLE); +/// } +/// +/// // The fourth one is a memory. Use +/// // `wasm_externtype_as_memorytype_const` to continue to inspect +/// // the type. +/// { +/// const wasm_importtype_t* import_type = import_types.data[3]; +/// +/// const wasm_name_t* import_module = wasm_importtype_module(import_type); +/// wasmer_assert_name(import_module, "ns"); +/// +/// const wasm_name_t* import_name = wasm_importtype_name(import_type); +/// wasmer_assert_name(import_name, "memory"); +/// +/// const wasm_externtype_t* extern_type = wasm_importtype_type(import_type); +/// assert(wasm_externtype_kind(extern_type) == WASM_EXTERN_MEMORY); +/// +/// const wasm_memorytype_t* memory_type = wasm_externtype_as_memorytype_const(extern_type); +/// const wasm_limits_t* memory_limits = wasm_memorytype_limits(memory_type); +/// assert(memory_limits->min == 3); +/// assert(memory_limits->max == 4); +/// } +/// +/// // Free everything. +/// wasm_importtype_vec_delete(&import_types); +/// wasm_module_delete(module); +/// wasm_byte_vec_delete(&wasm); +/// wasm_byte_vec_delete(&wat); +/// wasm_store_delete(store); +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` #[no_mangle] pub unsafe extern "C" fn wasm_module_imports( module: &wasm_module_t, @@ -135,6 +421,89 @@ pub unsafe extern "C" fn wasm_module_imports( *out = imports.into(); } +/// Deserializes a serialized module binary into a `wasm_module_t`. +/// +/// Note: the module has to be serialized before with the +/// `wasm_module_serialize` function. +/// +/// # Safety +/// +/// This function is inherently **unsafe** as the provided bytes: +/// +/// 1. Are going to be deserialized directly into Rust and C objects, +/// 2. Contains the function assembly bodies and, if intercepted, +/// a malicious actor could inject code into executable +/// memory. +/// +/// And as such, the `wasm_module_deserialize` method is unsafe. +/// +/// # Example +/// +/// ```rust +/// # use inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer_wasm.h" +/// # +/// int main() { +/// // Create the engine and the store. +/// wasm_engine_t* engine = wasm_engine_new(); +/// wasm_store_t* store = wasm_store_new(engine); +/// +/// // Create a WebAssembly module from a WAT definition. +/// wasm_byte_vec_t wat; +/// wasmer_byte_vec_new_from_string( +/// &wat, +/// "(module\n" +/// " (func (export \"function\") (param i32 i64))\n" +/// " (global (export \"global\") i32 (i32.const 7))\n" +/// " (table (export \"table\") 0 funcref)\n" +/// " (memory (export \"memory\") 1))" +/// ); +/// wasm_byte_vec_t wasm; +/// wat2wasm(&wat, &wasm); +/// +/// // Create the module. +/// wasm_module_t* module = wasm_module_new(store, &wasm); +/// assert(module); +/// +/// // Serialize the module into bytes. +/// wasm_byte_vec_t serialized_module; +/// wasm_module_serialize(module, &serialized_module); +/// assert(serialized_module.size > 0); +/// +/// // Free the module. +/// wasm_module_delete(module); +/// +/// // Deserialize the serialized module. Note that the store must +/// // be the same as the one used to serialize. +/// wasm_module_t* deserialized_module = wasm_module_deserialize( +/// store, +/// &serialized_module +/// ); +/// wasm_byte_vec_delete(&serialized_module); +/// assert(deserialized_module); +/// +/// // Check we have our 4 export types. +/// wasm_exporttype_vec_t export_types; +/// wasm_module_exports(deserialized_module, &export_types); +/// +/// assert(export_types.size == 4); +/// +/// // Free everything. +/// wasm_exporttype_vec_delete(&export_types); +/// wasm_module_delete(deserialized_module); +/// wasm_byte_vec_delete(&wasm); +/// wasm_byte_vec_delete(&wat); +/// wasm_store_delete(store); +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` #[no_mangle] pub unsafe extern "C" fn wasm_module_deserialize( store: &wasm_store_t, @@ -161,6 +530,12 @@ pub unsafe extern "C" fn wasm_module_deserialize( )))) } +/// Serializes a module into a binary representation that the engine +/// (`wasm_engine_t`) can later process via `wasm_module_deserialize`. +/// +/// # Example +/// +/// See `wasm_module_deserialize`. #[no_mangle] pub unsafe extern "C" fn wasm_module_serialize( module: &wasm_module_t, @@ -337,6 +712,8 @@ mod tests { wasm_module_delete(module); wasm_store_delete(store); wasm_engine_delete(engine); + + return 0; } }) .success(); @@ -453,6 +830,8 @@ mod tests { wasm_byte_vec_delete(&wat); wasm_store_delete(store); wasm_engine_delete(engine); + + return 0; } }) .success(); @@ -485,6 +864,8 @@ mod tests { wasm_byte_vec_delete(&wat); wasm_store_delete(store); wasm_engine_delete(engine); + + return 0; } }) .success(); @@ -537,6 +918,8 @@ mod tests { wasm_byte_vec_delete(&wat); wasm_store_delete(store); wasm_engine_delete(engine); + + return 0; } }) .success(); diff --git a/lib/c-api/wasmer_wasm.h b/lib/c-api/wasmer_wasm.h index 40a8c375dcb..c04d85ea470 100644 --- a/lib/c-api/wasmer_wasm.h +++ b/lib/c-api/wasmer_wasm.h @@ -180,13 +180,79 @@ wasi_version_t wasi_get_wasi_version(const wasm_module_t *module); #if defined(WASMER_COMPILER_ENABLED) /** - * Configure the compiler to use. + * Updates the configuration to specify a particular compiler to use. + * + * This is a Wasmer-specific function. + * + * # Example + * + * ```rust,no_run + * # use inline_c::assert_c; + * # fn main() { + * # (assert_c! { + * # #include "tests/wasmer_wasm.h" + * # + * int main() { + * // Create the configuration. + * wasm_config_t* config = wasm_config_new(); + * + * // Use the Cranelift compiler. + * wasm_config_set_compiler(config, CRANELIFT); + * + * // Create the engine. + * wasm_engine_t* engine = wasm_engine_new_with_config(config); + * + * // Check we have an engine! + * assert(engine); + * + * // Free everything. + * wasm_engine_delete(engine); + * + * return 0; + * } + * # }) + * # .success(); + * # } + * ``` */ void wasm_config_set_compiler(wasm_config_t *config, wasmer_compiler_t compiler); #endif /** - * Configure the engine to use. + * Updates the configuration to specify a particular engine to use. + * + * This is a Wasmer-specific function. + * + * # Example + * + * ```rust,no_run + * # use inline_c::assert_c; + * # fn main() { + * # (assert_c! { + * # #include "tests/wasmer_wasm.h" + * # + * int main() { + * // Create the configuration. + * wasm_config_t* config = wasm_config_new(); + * + * // Use the JIT engine. + * wasm_config_set_engine(config, JIT); + * + * // Create the engine. + * wasm_engine_t* engine = wasm_engine_new_with_config(config); + * + * // Check we have an engine! + * assert(engine); + * + * // Free everything. + * wasm_engine_delete(engine); + * + * return 0; + * } + * # }) + * # .success(); + * # } + * ``` */ void wasm_config_set_engine(wasm_config_t *config, wasmer_engine_t engine);