diff --git a/lib/runtime-c-api/src/import.rs b/lib/runtime-c-api/src/import.rs index 102a431ac..2d6f9e99d 100644 --- a/lib/runtime-c-api/src/import.rs +++ b/lib/runtime-c-api/src/import.rs @@ -10,7 +10,7 @@ use crate::{ }; use libc::c_uint; use std::{ffi::c_void, ptr, slice, sync::Arc}; -use wasmer_runtime::Module; +use wasmer_runtime::{Global, Memory, Module, Table}; use wasmer_runtime_core::{ export::{Context, Export, FuncPointer}, import::ImportObject, @@ -41,6 +41,83 @@ pub struct wasmer_import_descriptor_t; #[derive(Clone)] pub struct wasmer_import_descriptors_t; +/// Creates a new empty import object. +/// See also `wasmer_import_object_append` +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_import_object_new() -> *mut wasmer_import_object_t { + let import_object = Box::new(ImportObject::new()); + + Box::into_raw(import_object) as *mut wasmer_import_object_t +} + +/// Extends an existing import object with new imports +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_import_object_extend( + import_object: *mut wasmer_import_object_t, + imports: *mut wasmer_import_t, + imports_len: c_uint, +) -> wasmer_result_t { + let import_object: &mut ImportObject = &mut *(import_object as *mut ImportObject); + + let mut extensions: Vec<(String, String, Export)> = Vec::new(); + + let imports: &[wasmer_import_t] = slice::from_raw_parts(imports, imports_len as usize); + for import in imports { + let module_name = slice::from_raw_parts( + import.module_name.bytes, + import.module_name.bytes_len as usize, + ); + let module_name = if let Ok(s) = std::str::from_utf8(module_name) { + s + } else { + update_last_error(CApiError { + msg: "error converting module name to string".to_string(), + }); + return wasmer_result_t::WASMER_ERROR; + }; + let import_name = slice::from_raw_parts( + import.import_name.bytes, + import.import_name.bytes_len as usize, + ); + let import_name = if let Ok(s) = std::str::from_utf8(import_name) { + s + } else { + update_last_error(CApiError { + msg: "error converting import_name to string".to_string(), + }); + return wasmer_result_t::WASMER_ERROR; + }; + + let export = match import.tag { + wasmer_import_export_kind::WASM_MEMORY => { + let mem = import.value.memory as *mut Memory; + Export::Memory((&*mem).clone()) + } + wasmer_import_export_kind::WASM_FUNCTION => { + let func_export = import.value.func as *mut Export; + (&*func_export).clone() + } + wasmer_import_export_kind::WASM_GLOBAL => { + let global = import.value.global as *mut Global; + Export::Global((&*global).clone()) + } + wasmer_import_export_kind::WASM_TABLE => { + let table = import.value.table as *mut Table; + Export::Table((&*table).clone()) + } + }; + + let extension = (module_name.to_string(), import_name.to_string(), export); + extensions.push(extension) + } + + import_object.extend(extensions); + + return wasmer_result_t::WASMER_OK; +} + /// Gets import descriptors for the given module /// /// The caller owns the object and should call `wasmer_import_descriptors_destroy` to free it. diff --git a/lib/runtime-c-api/tests/.gitignore b/lib/runtime-c-api/tests/.gitignore index ed3b6ed6e..1b7383064 100644 --- a/lib/runtime-c-api/tests/.gitignore +++ b/lib/runtime-c-api/tests/.gitignore @@ -22,4 +22,5 @@ test-module-exports test-module-imports test-module-serialize test-tables -test-validate \ No newline at end of file +test-validate +test-module-import-instantiate diff --git a/lib/runtime-c-api/tests/CMakeLists.txt b/lib/runtime-c-api/tests/CMakeLists.txt index 6e636a6a0..3799485c3 100644 --- a/lib/runtime-c-api/tests/CMakeLists.txt +++ b/lib/runtime-c-api/tests/CMakeLists.txt @@ -14,6 +14,7 @@ add_executable(test-module-imports test-module-imports.c) add_executable(test-module-serialize test-module-serialize.c) add_executable(test-tables test-tables.c) add_executable(test-validate test-validate.c) +add_executable(test-module-import-instantiate test-module-import-instantiate.c) find_library( WASMER_LIB NAMES libwasmer_runtime_c_api.dylib libwasmer_runtime_c_api.so libwasmer_runtime_c_api.dll @@ -87,3 +88,8 @@ add_test(test-tables test-tables) target_link_libraries(test-validate general ${WASMER_LIB}) target_compile_options(test-validate PRIVATE ${COMPILER_OPTIONS}) add_test(test-validate test-validate) + +target_link_libraries(test-module-import-instantiate general ${WASMER_LIB}) +target_compile_options(test-module-import-instantiate PRIVATE ${COMPILER_OPTIONS}) +add_test(test-module-import-instantiate test-module-import-instantiate) + diff --git a/lib/runtime-c-api/tests/assets/inc.wasm b/lib/runtime-c-api/tests/assets/inc.wasm new file mode 100644 index 000000000..9ad23b5e7 Binary files /dev/null and b/lib/runtime-c-api/tests/assets/inc.wasm differ diff --git a/lib/runtime-c-api/tests/assets/inc.wast b/lib/runtime-c-api/tests/assets/inc.wast new file mode 100644 index 000000000..36a3eecb1 --- /dev/null +++ b/lib/runtime-c-api/tests/assets/inc.wast @@ -0,0 +1,12 @@ +(module + (func $inc (import "env" "inc")) + (func $mul (import "env" "mul")) + (func $get (import "env" "get") (result i32)) + + (func (export "inc_and_get") (result i32) + call $inc + call $get) + + (func (export "mul_and_get") (result i32) + call $mul + call $get)) diff --git a/lib/runtime-c-api/tests/test-module-import-instantiate.c b/lib/runtime-c-api/tests/test-module-import-instantiate.c index e69de29bb..6c4ef0912 100644 --- a/lib/runtime-c-api/tests/test-module-import-instantiate.c +++ b/lib/runtime-c-api/tests/test-module-import-instantiate.c @@ -0,0 +1,148 @@ +#include +#include "../wasmer.h" +#include +#include +#include + +typedef struct { + int32_t amount; + int32_t value; +} counter_data; + +typedef struct { + uint8_t* bytes; + long bytes_len; +} wasm_file_t; + +wasm_file_t read_wasm_file(const char* file_name) { + wasm_file_t wasm_file; + + FILE *file = fopen(file_name, "r"); + fseek(file, 0, SEEK_END); + wasm_file.bytes_len = ftell(file); + + wasm_file.bytes = malloc(wasm_file.bytes_len); + fseek(file, 0, SEEK_SET); + fread(wasm_file.bytes, 1, wasm_file.bytes_len, file); + fclose(file); + + return wasm_file; +} + +void inc_counter(wasmer_instance_context_t *ctx) { + counter_data* data = (counter_data*)wasmer_instance_context_data_get(ctx); + data->value = data->value + data->amount; +} + +void mul_counter(wasmer_instance_context_t *ctx) { + counter_data* data = (counter_data*)wasmer_instance_context_data_get(ctx); + data->value = data->value * data->amount; +} + +int32_t get_counter(wasmer_instance_context_t *ctx) { + counter_data* data = (counter_data*)wasmer_instance_context_data_get(ctx); + return data->value; +} + +counter_data *init_counter(int32_t value, int32_t amount) { + counter_data* counter = malloc(sizeof(counter_data)); + counter->value = value; + counter->amount = amount; + return counter; +} + +wasmer_import_t create_import(char* module_name, char* import_name, wasmer_import_func_t *func) { + wasmer_import_t import; + wasmer_byte_array module_name_bytes; + wasmer_byte_array import_name_bytes; + + module_name_bytes.bytes = (const uint8_t *) module_name; + module_name_bytes.bytes_len = strlen(module_name); + + import_name_bytes.bytes = (const uint8_t *) import_name; + import_name_bytes.bytes_len = strlen(import_name); + + import.module_name = module_name_bytes; + import.import_name = import_name_bytes; + + import.tag = WASM_FUNCTION; + import.value.func = func; + + return import; +} + +int main() +{ + // Prepare Imports + wasmer_value_tag inc_params_sig[] = {}; + wasmer_value_tag inc_returns_sig[] = {}; + wasmer_import_func_t *inc_func = wasmer_import_func_new((void (*)(void *)) inc_counter, inc_params_sig, 0, inc_returns_sig, 0); + wasmer_import_t inc_import = create_import("env", "inc", inc_func); + + wasmer_value_tag mul_params_sig[] = {}; + wasmer_value_tag mul_returns_sig[] = {}; + wasmer_import_func_t *mul_func = wasmer_import_func_new((void (*)(void *)) mul_counter, mul_params_sig, 0, mul_returns_sig, 0); + wasmer_import_t mul_import = create_import("env", "mul", mul_func); + + wasmer_value_tag get_params_sig[] = {}; + wasmer_value_tag get_returns_sig[] = {WASM_I32}; + wasmer_import_func_t *get_func = wasmer_import_func_new((void (*)(void *)) get_counter, get_params_sig, 0, get_returns_sig, 1); + wasmer_import_t get_import = create_import("env", "get", get_func); + + // Read the wasm file + wasm_file_t wasm_file = read_wasm_file("assets/inc.wasm"); + + // Compile module + wasmer_module_t *module = NULL; + wasmer_result_t compile_res = wasmer_compile(&module, wasm_file.bytes, wasm_file.bytes_len); + assert(compile_res == WASMER_OK); + + // Prepare Import Object + wasmer_import_object_t *import_object = wasmer_import_object_new(); + + // First, we import `inc_counter` and `mul_counter` + wasmer_import_t imports[] = {inc_import, mul_import}; + wasmer_result_t extend_res = wasmer_import_object_extend(import_object, imports, 2); + assert(extend_res == WASMER_OK); + + // Now, we'll import `inc_counter` and `mul_counter` + wasmer_import_t more_imports[] = {get_import}; + wasmer_result_t extend_res2 = wasmer_import_object_extend(import_object, more_imports, 1); + assert(extend_res2 == WASMER_OK); + + // Same `wasmer_import_object_extend` as the first, doesn't affect anything + wasmer_result_t extend_res3 = wasmer_import_object_extend(import_object, imports, 2); + assert(extend_res3 == WASMER_OK); + + // Instantiate instance + printf("Instantiating\n"); + wasmer_instance_t *instance = NULL; + wasmer_result_t instantiate_res = wasmer_module_import_instantiate(&instance, module, import_object); + printf("Compile result: %d\n", instantiate_res); + + // Init counter + counter_data *counter = init_counter(2, 5); + wasmer_instance_context_data_set(instance, counter); + + wasmer_value_t result_one; + wasmer_value_t params[] = {}; + wasmer_value_t results[] = {result_one}; + + wasmer_result_t call1_result = wasmer_instance_call(instance, "inc_and_get", params, 0, results, 1); + printf("Call result: %d\n", call1_result); + printf("Result: %d\n", results[0].value.I32); + + wasmer_result_t call2_result = wasmer_instance_call(instance, "mul_and_get", params, 0, results, 1); + printf("Call result: %d\n", call2_result); + printf("Result: %d\n", results[0].value.I32); + + // Clear resources + wasmer_import_func_destroy(inc_func); + wasmer_import_func_destroy(mul_func); + wasmer_import_func_destroy(get_func); + wasmer_instance_destroy(instance); + free(counter); + free(wasm_file.bytes); + + return 0; +} diff --git a/lib/runtime-c-api/wasmer.h b/lib/runtime-c-api/wasmer.h index 3b98620f8..086ae7680 100644 --- a/lib/runtime-c-api/wasmer.h +++ b/lib/runtime-c-api/wasmer.h @@ -99,14 +99,6 @@ typedef struct { typedef struct { -} wasmer_instance_t; - -typedef struct { - -} wasmer_instance_context_t; - -typedef struct { - } wasmer_table_t; typedef union { @@ -123,6 +115,14 @@ typedef struct { wasmer_import_export_value value; } wasmer_import_t; +typedef struct { + +} wasmer_instance_t; + +typedef struct { + +} wasmer_instance_context_t; + typedef struct { bool has_some; uint32_t some; @@ -401,6 +401,19 @@ wasmer_result_t wasmer_import_func_returns_arity(const wasmer_import_func_t *fun */ void wasmer_import_object_destroy(wasmer_import_object_t *import_object); +/** + * Extends an existing import object with new imports + */ +wasmer_result_t wasmer_import_object_extend(wasmer_import_object_t *import_object, + wasmer_import_t *imports, + unsigned int imports_len); + +/** + * Creates a new empty import object. + * See also `wasmer_import_object_append` + */ +wasmer_import_object_t *wasmer_import_object_new(void); + /** * Calls an instances exported function by `name` with the provided parameters. * Results are set using the provided `results` pointer. diff --git a/lib/runtime-c-api/wasmer.hh b/lib/runtime-c-api/wasmer.hh index ef8929492..931a6c9d7 100644 --- a/lib/runtime-c-api/wasmer.hh +++ b/lib/runtime-c-api/wasmer.hh @@ -95,14 +95,6 @@ struct wasmer_import_object_t { }; -struct wasmer_instance_t { - -}; - -struct wasmer_instance_context_t { - -}; - struct wasmer_table_t { }; @@ -121,6 +113,14 @@ struct wasmer_import_t { wasmer_import_export_value value; }; +struct wasmer_instance_t { + +}; + +struct wasmer_instance_context_t { + +}; + struct wasmer_limit_option_t { bool has_some; uint32_t some; @@ -325,6 +325,15 @@ wasmer_result_t wasmer_import_func_returns_arity(const wasmer_import_func_t *fun /// Frees memory of the given ImportObject void wasmer_import_object_destroy(wasmer_import_object_t *import_object); +/// Extends an existing import object with new imports +wasmer_result_t wasmer_import_object_extend(wasmer_import_object_t *import_object, + wasmer_import_t *imports, + unsigned int imports_len); + +/// Creates a new empty import object. +/// See also `wasmer_import_object_append` +wasmer_import_object_t *wasmer_import_object_new(); + /// Calls an instances exported function by `name` with the provided parameters. /// Results are set using the provided `results` pointer. /// Returns `wasmer_result_t::WASMER_OK` upon success. diff --git a/lib/runtime-core/src/import.rs b/lib/runtime-core/src/import.rs index 5fb5b4b28..9f1bf2f71 100644 --- a/lib/runtime-core/src/import.rs +++ b/lib/runtime-core/src/import.rs @@ -51,7 +51,7 @@ pub struct ImportObject { } impl ImportObject { - /// Create a new `ImportObject`. + /// Create a new `ImportObject`. pub fn new() -> Self { Self { map: Rc::new(RefCell::new(HashMap::new())),