diff --git a/Makefile b/Makefile index 58b85dbb1..0f3560e67 100644 --- a/Makefile +++ b/Makefile @@ -105,6 +105,8 @@ capi: test-capi: capi cargo test -p wasmer-runtime-c-api --release +capi-test: test-capi + test-rest: cargo test --release --all --exclude wasmer-runtime-c-api --exclude wasmer-emscripten --exclude wasmer-spectests --exclude wasmer-wasi --exclude wasmer-middleware-common --exclude wasmer-middleware-common-tests --exclude wasmer-singlepass-backend --exclude wasmer-clif-backend --exclude wasmer-llvm-backend --exclude wasmer-wasi-tests --exclude wasmer-emscripten-tests diff --git a/lib/runtime-c-api/src/import/mod.rs b/lib/runtime-c-api/src/import/mod.rs index 3ef5a487c..ad0470af6 100644 --- a/lib/runtime-c-api/src/import/mod.rs +++ b/lib/runtime-c-api/src/import/mod.rs @@ -170,7 +170,35 @@ pub unsafe extern "C" fn wasmer_import_object_get_import( } #[no_mangle] -/// Call `wasmer_import_object_imports_destroy` to free the memory allocated by this function +/// Get the number of functions that an import object contains. +/// The result of this is useful as an argument to `wasmer_import_object_get_functions`. +/// This function returns -1 on error. +pub unsafe extern "C" fn wasmer_import_object_get_num_functions( + import_object: *const wasmer_import_object_t, +) -> i32 { + if import_object.is_null() { + update_last_error(CApiError { + msg: "import_object must not be null".to_owned(), + }); + return -1; + } + let import_object: &ImportObject = &*(import_object as *const ImportObject); + import_object + .clone_ref() + .into_iter() + .filter(|(_, _, e)| { + if let Export::Function { .. } = e { + true + } else { + false + } + }) + .count() as i32 +} + +#[no_mangle] +/// Call `wasmer_import_object_imports_destroy` to free the memory allocated by this function. +/// This function return -1 on error. pub unsafe extern "C" fn wasmer_import_object_get_functions( import_object: *const wasmer_import_object_t, imports: *mut wasmer_import_t, @@ -178,11 +206,11 @@ pub unsafe extern "C" fn wasmer_import_object_get_functions( ) -> i32 { if import_object.is_null() || imports.is_null() { update_last_error(CApiError { - msg: format!("import_object and imports must not be null"), + msg: "import_object and imports must not be null".to_owned(), }); return -1; } - let import_object: &mut ImportObject = &mut *(import_object as *mut ImportObject); + let import_object: &ImportObject = &*(import_object as *const ImportObject); let mut i = 0; for (namespace, name, export) in import_object.clone_ref().into_iter() { diff --git a/lib/runtime-c-api/tests/CMakeLists.txt b/lib/runtime-c-api/tests/CMakeLists.txt index 7db3cbcb0..d8f206c49 100644 --- a/lib/runtime-c-api/tests/CMakeLists.txt +++ b/lib/runtime-c-api/tests/CMakeLists.txt @@ -6,6 +6,8 @@ add_executable(test-exports test-exports.c) add_executable(test-globals test-globals.c) add_executable(test-import-function test-import-function.c) add_executable(test-imports test-imports.c) +add_executable(test-import-object test-import-object.c) +add_executable(test-wasi-import-object test-wasi-import-object.c) add_executable(test-instantiate test-instantiate.c) add_executable(test-memory test-memory.c) add_executable(test-module test-module.c) @@ -58,6 +60,14 @@ target_link_libraries(test-imports general ${WASMER_LIB}) target_compile_options(test-imports PRIVATE ${COMPILER_OPTIONS}) add_test(test-imports test-imports) +target_link_libraries(test-import-object general ${WASMER_LIB}) +target_compile_options(test-import-object PRIVATE ${COMPILER_OPTIONS}) +add_test(test-import-object test-import-object) + +target_link_libraries(test-wasi-import-object general ${WASMER_LIB}) +target_compile_options(test-wasi-import-object PRIVATE ${COMPILER_OPTIONS}) +add_test(test-wasi-import-object test-wasi-import-object) + target_link_libraries(test-instantiate general ${WASMER_LIB}) target_compile_options(test-instantiate PRIVATE ${COMPILER_OPTIONS}) add_test(test-instantiate test-instantiate) diff --git a/lib/runtime-c-api/tests/assets/README.md b/lib/runtime-c-api/tests/assets/README.md new file mode 100644 index 000000000..52e92c052 --- /dev/null +++ b/lib/runtime-c-api/tests/assets/README.md @@ -0,0 +1,3 @@ +These are used in tests in the parent directory. + +To keep the generated wasm small, use `wasm-opt` and `wasm-strip` from wabt-tools (can be installed via wapm). Addtionally, consider passing the `-C opt-level=z` flag to `rustc` to optimize for size. diff --git a/lib/runtime-c-api/tests/assets/extended_wasi.rs b/lib/runtime-c-api/tests/assets/extended_wasi.rs new file mode 100644 index 000000000..31c659f8e --- /dev/null +++ b/lib/runtime-c-api/tests/assets/extended_wasi.rs @@ -0,0 +1,31 @@ +extern "C" { + fn host_print(ptr: u32, len: u32); +} + +fn main() { + let args = std::env::args().collect::>(); + + println!("Found {} args on program {}", args.len(), args[0]); + + let env_vars = std::env::vars() + .map(|(arg, val)| format!("{}={}", arg, val)) + .collect::>(); + let env_var_list = env_vars.join(", "); + + println!("Found {} env vars: {}", env_vars.len(), env_var_list); + + let dirs_in_root = std::fs::read_dir("/") + .unwrap() + .map(|e| e.map(|inner| format!("{:?}", inner))) + .collect::, _>>() + .unwrap(); + + println!( + "Found {} pre opened dirs: {}", + dirs_in_root.len(), + dirs_in_root.join(", ") + ); + + const HOST_STR: &str = "This string came from a WASI module"; + unsafe { host_print(HOST_STR.as_ptr() as u32, HOST_STR.len() as u32) }; +} diff --git a/lib/runtime-c-api/tests/assets/extended_wasi.wasm b/lib/runtime-c-api/tests/assets/extended_wasi.wasm new file mode 100755 index 000000000..b8eedd722 Binary files /dev/null and b/lib/runtime-c-api/tests/assets/extended_wasi.wasm differ diff --git a/lib/runtime-c-api/tests/test-exports.c b/lib/runtime-c-api/tests/test-exports.c index 893174908..3af813d0e 100644 --- a/lib/runtime-c-api/tests/test-exports.c +++ b/lib/runtime-c-api/tests/test-exports.c @@ -242,7 +242,7 @@ int main() wasmer_result_t call_result = wasmer_export_func_call(exported_function, inputs, inputs_arity, outputs, outputs_arity); - printf("Result: %ld\n", outputs[0].value.I64); + printf("Result: %lld\n", outputs[0].value.I64); assert(outputs[0].value.I64 == 7); assert(call_result == WASMER_OK); diff --git a/lib/runtime-c-api/tests/test-import-object b/lib/runtime-c-api/tests/test-import-object new file mode 100755 index 000000000..42e1496de Binary files /dev/null and b/lib/runtime-c-api/tests/test-import-object differ diff --git a/lib/runtime-c-api/tests/test-import-object.c b/lib/runtime-c-api/tests/test-import-object.c new file mode 100644 index 000000000..640f16640 --- /dev/null +++ b/lib/runtime-c-api/tests/test-import-object.c @@ -0,0 +1,172 @@ +#include +#include "../wasmer.h" +#include +#include +#include + +bool static print_str_called = false; + +// Host function that will be imported into the Web Assembly Instance +void print_str(const wasmer_instance_context_t *ctx, int32_t ptr, int32_t len) +{ + print_str_called = true; + const wasmer_memory_t *memory = wasmer_instance_context_memory(ctx, 0); + uint32_t mem_len = wasmer_memory_length(memory); + uint8_t *mem_bytes = wasmer_memory_data(memory); + printf("%.*s", len, mem_bytes + ptr); +} + +// Use the last_error API to retrieve error messages +void print_wasmer_error() +{ + int error_len = wasmer_last_error_length(); + printf("Error len: `%d`\n", error_len); + char *error_str = malloc(error_len); + wasmer_last_error_message(error_str, error_len); + printf("Error str: `%s`\n", error_str); +} + +int main() +{ + // Create a new func to hold the parameter and signature + // of our `print_str` host function + wasmer_value_tag params_sig[] = {WASM_I32, WASM_I32}; + wasmer_value_tag returns_sig[] = {}; + wasmer_import_func_t *func = wasmer_import_func_new((void (*)(void *)) print_str, params_sig, 2, returns_sig, 0); + + // Create module name for our imports + // represented in bytes for UTF-8 compatability + const char *module_name = "env"; + wasmer_byte_array module_name_bytes; + module_name_bytes.bytes = (const uint8_t *) module_name; + module_name_bytes.bytes_len = strlen(module_name); + + // Define a function import + const char *import_name = "_print_str"; + wasmer_byte_array import_name_bytes; + import_name_bytes.bytes = (const uint8_t *) import_name; + import_name_bytes.bytes_len = strlen(import_name); + wasmer_import_t func_import; + func_import.module_name = module_name_bytes; + func_import.import_name = import_name_bytes; + func_import.tag = WASM_FUNCTION; + func_import.value.func = func; + + // Define a memory import + const char *import_memory_name = "memory"; + wasmer_byte_array import_memory_name_bytes; + import_memory_name_bytes.bytes = (const uint8_t *) import_memory_name; + import_memory_name_bytes.bytes_len = strlen(import_memory_name); + wasmer_import_t memory_import; + memory_import.module_name = module_name_bytes; + memory_import.import_name = import_memory_name_bytes; + memory_import.tag = WASM_MEMORY; + wasmer_memory_t *memory = NULL; + wasmer_limits_t descriptor; + descriptor.min = 256; + wasmer_limit_option_t max; + max.has_some = true; + max.some = 256; + descriptor.max = max; + wasmer_result_t memory_result = wasmer_memory_new(&memory, descriptor); + if (memory_result != WASMER_OK) + { + print_wasmer_error(); + } + memory_import.value.memory = memory; + + // Define a global import + const char *import_global_name = "__memory_base"; + wasmer_byte_array import_global_name_bytes; + import_global_name_bytes.bytes = (const uint8_t *) import_global_name; + import_global_name_bytes.bytes_len = strlen(import_global_name); + wasmer_import_t global_import; + global_import.module_name = module_name_bytes; + global_import.import_name = import_global_name_bytes; + global_import.tag = WASM_GLOBAL; + wasmer_value_t val; + val.tag = WASM_I32; + val.value.I32 = 1024; + wasmer_global_t *global = wasmer_global_new(val, false); + global_import.value.global = global; + + // Define a table import + const char *import_table_name = "table"; + wasmer_byte_array import_table_name_bytes; + import_table_name_bytes.bytes = (const uint8_t *) import_table_name; + import_table_name_bytes.bytes_len = strlen(import_table_name); + wasmer_import_t table_import; + table_import.module_name = module_name_bytes; + table_import.import_name = import_table_name_bytes; + table_import.tag = WASM_TABLE; + wasmer_table_t *table = NULL; + wasmer_limits_t table_descriptor; + table_descriptor.min = 256; + wasmer_limit_option_t table_max; + table_max.has_some = true; + table_max.some = 256; + table_descriptor.max = table_max; + wasmer_result_t table_result = wasmer_table_new(&table, table_descriptor); + if (table_result != WASMER_OK) + { + print_wasmer_error(); + } + table_import.value.table = table; + + // Define an empty import object + wasmer_import_object_t *import_object = wasmer_import_object_new(); + // Create our imports + wasmer_import_t imports[] = {func_import, global_import, memory_import, table_import}; + int imports_len = sizeof(imports) / sizeof(imports[0]); + // Add our imports to the import object + wasmer_import_object_extend(import_object, imports, imports_len); + + // Read the wasm file bytes + FILE *file = fopen("assets/hello_wasm.wasm", "r"); + fseek(file, 0, SEEK_END); + long len = ftell(file); + uint8_t *bytes = malloc(len); + fseek(file, 0, SEEK_SET); + fread(bytes, 1, len, file); + fclose(file); + + wasmer_module_t *module = NULL; + // Compile the WebAssembly module + wasmer_result_t compile_result = wasmer_compile(&module, bytes, len); + printf("Compile result: %d\n", compile_result); + if (compile_result != WASMER_OK) + { + print_wasmer_error(); + } + assert(compile_result == WASMER_OK); + + // Instantiatoe the module with our import_object + wasmer_instance_t *instance = NULL; + wasmer_result_t instantiate_result = wasmer_module_import_instantiate(&instance, module, import_object); + printf("Instantiate result: %d\n", instantiate_result); + if (instantiate_result != WASMER_OK) + { + print_wasmer_error(); + } + assert(instantiate_result == WASMER_OK); + + // Call the exported "hello_wasm" function of our instance + wasmer_value_t params[] = {}; + wasmer_value_t result_one; + wasmer_value_t results[] = {result_one}; + wasmer_result_t call_result = wasmer_instance_call(instance, "_hello_wasm", params, 0, results, 1); + printf("Call result: %d\n", call_result); + assert(call_result == WASMER_OK); + assert(print_str_called); + + // Use *_destroy methods to cleanup as specified in the header documentation + wasmer_import_func_destroy(func); + wasmer_global_destroy(global); + wasmer_memory_destroy(memory); + wasmer_table_destroy(table); + wasmer_instance_destroy(instance); + wasmer_import_object_destroy(import_object); + wasmer_module_destroy(module); + + return 0; +} diff --git a/lib/runtime-c-api/tests/test-wasi-import-object b/lib/runtime-c-api/tests/test-wasi-import-object new file mode 100755 index 000000000..70e24d17d Binary files /dev/null and b/lib/runtime-c-api/tests/test-wasi-import-object differ diff --git a/lib/runtime-c-api/tests/test-wasi-import-object.c b/lib/runtime-c-api/tests/test-wasi-import-object.c new file mode 100644 index 000000000..cf4a145d5 --- /dev/null +++ b/lib/runtime-c-api/tests/test-wasi-import-object.c @@ -0,0 +1,258 @@ +#include +#include "../wasmer.h" +#include +#include +#include + +bool static host_print_called = false; + +// Host function that will be imported into the Web Assembly Instance +void host_print(const wasmer_instance_context_t *ctx, int32_t ptr, int32_t len) +{ + host_print_called = true; + const wasmer_memory_t *memory = wasmer_instance_context_memory(ctx, 0); + uint32_t mem_len = wasmer_memory_length(memory); + uint8_t *mem_bytes = wasmer_memory_data(memory); + printf("%.*s", len, mem_bytes + ptr); +} + +// Use the last_error API to retrieve error messages +void print_wasmer_error() +{ + int error_len = wasmer_last_error_length(); + printf("Error len: `%d`\n", error_len); + char *error_str = malloc(error_len); + wasmer_last_error_message(error_str, error_len); + printf("Error str: `%s`\n", error_str); +} + +// helper function to print byte array to stdout +void print_byte_array(wasmer_byte_array *arr) { + for (int i = 0; i < arr->bytes_len; ++i) { + putchar(arr->bytes[i]); + } +} + +int main() +{ + // Create a new func to hold the parameter and signature + // of our `host_print` host function + wasmer_value_tag params_sig[] = {WASM_I32, WASM_I32}; + wasmer_value_tag returns_sig[] = {}; + wasmer_import_func_t *func = wasmer_import_func_new((void (*)(void *)) host_print, params_sig, 2, returns_sig, 0); + + // Create module name for our imports + // represented in bytes for UTF-8 compatability + const char *module_name = "env"; + wasmer_byte_array module_name_bytes; + module_name_bytes.bytes = (const uint8_t *) module_name; + module_name_bytes.bytes_len = strlen(module_name); + + // Define a function import + const char *import_name = "host_print"; + wasmer_byte_array import_name_bytes; + import_name_bytes.bytes = (const uint8_t *) import_name; + import_name_bytes.bytes_len = strlen(import_name); + wasmer_import_t func_import; + func_import.module_name = module_name_bytes; + func_import.import_name = import_name_bytes; + func_import.tag = WASM_FUNCTION; + func_import.value.func = func; + + // Define a memory import + const char *import_memory_name = "memory"; + wasmer_byte_array import_memory_name_bytes; + import_memory_name_bytes.bytes = (const uint8_t *) import_memory_name; + import_memory_name_bytes.bytes_len = strlen(import_memory_name); + wasmer_import_t memory_import; + memory_import.module_name = module_name_bytes; + memory_import.import_name = import_memory_name_bytes; + memory_import.tag = WASM_MEMORY; + wasmer_memory_t *memory = NULL; + wasmer_limits_t descriptor; + descriptor.min = 256; + wasmer_limit_option_t max; + max.has_some = true; + max.some = 256; + descriptor.max = max; + wasmer_result_t memory_result = wasmer_memory_new(&memory, descriptor); + if (memory_result != WASMER_OK) + { + print_wasmer_error(); + } + memory_import.value.memory = memory; + + // Define a global import + const char *import_global_name = "__memory_base"; + wasmer_byte_array import_global_name_bytes; + import_global_name_bytes.bytes = (const uint8_t *) import_global_name; + import_global_name_bytes.bytes_len = strlen(import_global_name); + wasmer_import_t global_import; + global_import.module_name = module_name_bytes; + global_import.import_name = import_global_name_bytes; + global_import.tag = WASM_GLOBAL; + wasmer_value_t val; + val.tag = WASM_I32; + val.value.I32 = 1024; + wasmer_global_t *global = wasmer_global_new(val, false); + global_import.value.global = global; + + // Define a table import + const char *import_table_name = "table"; + wasmer_byte_array import_table_name_bytes; + import_table_name_bytes.bytes = (const uint8_t *) import_table_name; + import_table_name_bytes.bytes_len = strlen(import_table_name); + wasmer_import_t table_import; + table_import.module_name = module_name_bytes; + table_import.import_name = import_table_name_bytes; + table_import.tag = WASM_TABLE; + wasmer_table_t *table = NULL; + wasmer_limits_t table_descriptor; + table_descriptor.min = 256; + wasmer_limit_option_t table_max; + table_max.has_some = true; + table_max.some = 256; + table_descriptor.max = table_max; + wasmer_result_t table_result = wasmer_table_new(&table, table_descriptor); + if (table_result != WASMER_OK) + { + print_wasmer_error(); + } + table_import.value.table = table; + + + // Create arbitrary arguments for our program + + // Set up data for our WASI import object + // + // Environment variables and program arguments are processed by the WASI + // program. They will not have any effects unless the program includes + // logic to process them. + const char *wasi_prog_name = "wasi_test_program"; + const char *wasi_first_arg = "--help"; + wasmer_byte_array args[] = { + { .bytes = (const uint8_t *) wasi_prog_name, + .bytes_len = strlen(wasi_prog_name) }, + { .bytes = (const uint8_t *) wasi_first_arg, + .bytes_len = strlen(wasi_first_arg) } + }; + int wasi_argc = sizeof(args) / sizeof(args[0]); + + // Create arbitrary environment variables for our program; + const char *wasi_color_env = "COLOR=TRUE"; + const char *wasi_app_should_log = "APP_SHOULD_LOG=FALSE"; + wasmer_byte_array envs[] = { + { .bytes = (const uint8_t *) wasi_color_env, + .bytes_len = strlen(wasi_color_env) }, + { .bytes = (const uint8_t *) wasi_app_should_log, + .bytes_len = strlen(wasi_app_should_log) } + }; + int wasi_env_len = sizeof(args) / sizeof(args[0]); + + // Open the host's current directory under a different name. + // WARNING: this gives the WASI module limited access to your host's file system, + // use caution when granting these permissions to untrusted Wasm modules. + const char *wasi_map_dir_alias = "the_host_current_dir"; + const char *wasi_map_dir_host_path = "."; + wasmer_wasi_map_dir_entry_t mapped_dirs[] = { + { .alias = + { .bytes = (const uint8_t *) wasi_map_dir_alias, + .bytes_len = strlen(wasi_map_dir_alias) }, + .host_file_path = + { .bytes = (const uint8_t *) wasi_map_dir_host_path, + .bytes_len = strlen(wasi_map_dir_host_path) } } + }; + int mapped_dir_len = sizeof(mapped_dirs) / sizeof(mapped_dirs[0]); + + // Create the WASI import object + wasmer_import_object_t *import_object = + wasmer_wasi_generate_import_object(args, wasi_argc, + envs, wasi_env_len, + NULL, 0, + mapped_dirs, mapped_dir_len); + + // Create our imports + wasmer_import_t imports[] = {func_import, global_import, memory_import, table_import}; + int imports_len = sizeof(imports) / sizeof(imports[0]); + // Add our imports to the import object + wasmer_import_object_extend(import_object, imports, imports_len); + + // Read the wasm file bytes + FILE *file = fopen("assets/extended_wasi.wasm", "r"); + assert(file); + fseek(file, 0, SEEK_END); + long len = ftell(file); + uint8_t *bytes = malloc(len); + fseek(file, 0, SEEK_SET); + fread(bytes, 1, len, file); + fclose(file); + + wasmer_module_t *module = NULL; + // Compile the WebAssembly module + wasmer_result_t compile_result = wasmer_compile(&module, bytes, len); + printf("Compile result: %d\n", compile_result); + if (compile_result != WASMER_OK) + { + print_wasmer_error(); + } + assert(compile_result == WASMER_OK); + + // Instantiatoe the module with our import_object + wasmer_instance_t *instance = NULL; + wasmer_result_t instantiate_result = wasmer_module_import_instantiate(&instance, module, import_object); + printf("Instantiate result: %d\n", instantiate_result); + if (instantiate_result != WASMER_OK) + { + print_wasmer_error(); + } + assert(instantiate_result == WASMER_OK); + + // Call the exported "hello_wasm" function of our instance + wasmer_value_t params[] = {}; + wasmer_value_t result_one; + wasmer_value_t results[] = {result_one}; + // _start runs before main for WASI programs + wasmer_result_t call_result = wasmer_instance_call(instance, "_start", params, 0, results, 1); + printf("Call result: %d\n", call_result); + assert(call_result == WASMER_OK); + assert(host_print_called); + + int32_t num_functions = wasmer_import_object_get_num_functions(import_object); + if (num_functions == -1) { + print_wasmer_error(); + return -1; + } + wasmer_import_t *func_array = malloc(sizeof(wasmer_import_t) * num_functions); + assert(func_array); + + int32_t num_returned = wasmer_import_object_get_functions(import_object, func_array, num_functions); + if (num_functions == -1) { + print_wasmer_error(); + return -1; + } + assert(num_functions == num_returned); + + printf("Found %d functions in import object:\n", num_returned); + for (int i = 0; i < num_returned; ++i) { + print_byte_array(&func_array[i].module_name); + putchar(' '); + print_byte_array(&func_array[i].import_name); + putchar('\n'); + assert(func_array[i].tag == WASM_FUNCTION); + assert(func_array[i].value.func); + } + + // Use *_destroy methods to cleanup as specified in the header documentation + wasmer_import_object_imports_destroy(func_array, num_returned); + free(func_array); + wasmer_import_func_destroy(func); + wasmer_global_destroy(global); + wasmer_memory_destroy(memory); + wasmer_table_destroy(table); + wasmer_instance_destroy(instance); + wasmer_import_object_destroy(import_object); + wasmer_module_destroy(module); + + return 0; +} + diff --git a/lib/runtime-c-api/wasmer.h b/lib/runtime-c-api/wasmer.h index 57338a5bc..8bb9fb254 100644 --- a/lib/runtime-c-api/wasmer.h +++ b/lib/runtime-c-api/wasmer.h @@ -470,7 +470,8 @@ wasmer_result_t wasmer_import_object_extend(wasmer_import_object_t *import_objec unsigned int imports_len); /** - * Call `wasmer_import_object_imports_destroy` to free the memory allocated by this function + * Call `wasmer_import_object_imports_destroy` to free the memory allocated by this function. + * This function return -1 on error. */ int32_t wasmer_import_object_get_functions(const wasmer_import_object_t *import_object, wasmer_import_t *imports, @@ -492,6 +493,13 @@ wasmer_result_t wasmer_import_object_get_import(const wasmer_import_object_t *im wasmer_import_export_value *import_export_value, uint32_t tag); +/** + * Get the number of functions that an import object contains. + * The result of this is useful as an argument to `wasmer_import_object_get_functions`. + * This function returns -1 on error. + */ +int32_t wasmer_import_object_get_num_functions(const wasmer_import_object_t *import_object); + /** * Frees the memory acquired in `wasmer_import_object_get_functions` * diff --git a/lib/runtime-c-api/wasmer.hh b/lib/runtime-c-api/wasmer.hh index 521c6fa6c..21426f278 100644 --- a/lib/runtime-c-api/wasmer.hh +++ b/lib/runtime-c-api/wasmer.hh @@ -371,7 +371,8 @@ wasmer_result_t wasmer_import_object_extend(wasmer_import_object_t *import_objec const wasmer_import_t *imports, unsigned int imports_len); -/// Call `wasmer_import_object_imports_destroy` to free the memory allocated by this function +/// Call `wasmer_import_object_imports_destroy` to free the memory allocated by this function. +/// This function return -1 on error. int32_t wasmer_import_object_get_functions(const wasmer_import_object_t *import_object, wasmer_import_t *imports, uint32_t imports_len); @@ -390,6 +391,11 @@ wasmer_result_t wasmer_import_object_get_import(const wasmer_import_object_t *im wasmer_import_export_value *import_export_value, uint32_t tag); +/// Get the number of functions that an import object contains. +/// The result of this is useful as an argument to `wasmer_import_object_get_functions`. +/// This function returns -1 on error. +int32_t wasmer_import_object_get_num_functions(const wasmer_import_object_t *import_object); + /// Frees the memory acquired in `wasmer_import_object_get_functions` /// /// This function does not free the memory in `wasmer_import_object_t`;