616: runtime-c-api: create the import object separately from instance instantiation  r=bjfish a=YaronWittenstein

Creating an `import object` separately from the `wasmer` instance, let's us build the import object incrementally and support the use-case of having import functions (a.k.a `vmcalls`) implemented in 2 programming languages (Rust and Golang for example) and consumed by the same `wasmer` instance.

Co-authored-by: Yaron Wittenstein <yaron.wittenstein@gmail.com>
Co-authored-by: Brandon Fish <bjfish@users.noreply.github.com>
This commit is contained in:
bors[bot] 2019-08-02 20:03:56 +00:00
commit 9489af4207
11 changed files with 364 additions and 18 deletions

View File

@ -10,9 +10,10 @@ 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,
module::ImportName,
types::{FuncSig, Type},
};
@ -25,6 +26,9 @@ pub struct wasmer_import_t {
pub value: wasmer_import_export_value,
}
#[repr(C)]
pub struct wasmer_import_object_t;
#[repr(C)]
#[derive(Clone)]
pub struct wasmer_import_func_t;
@ -37,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.
@ -352,6 +433,14 @@ pub extern "C" fn wasmer_import_func_destroy(func: *mut wasmer_import_func_t) {
}
}
/// Frees memory of the given ImportObject
#[no_mangle]
pub extern "C" fn wasmer_import_object_destroy(import_object: *mut wasmer_import_object_t) {
if !import_object.is_null() {
unsafe { Box::from_raw(import_object as *mut ImportObject) };
}
}
struct NamedImportDescriptor {
module: String,
name: String,

View File

@ -3,15 +3,19 @@
use crate::{
error::{update_last_error, CApiError},
export::{wasmer_exports_t, wasmer_import_export_kind, NamedExport, NamedExports},
import::wasmer_import_t,
import::{wasmer_import_object_t, wasmer_import_t},
memory::wasmer_memory_t,
module::wasmer_module_t,
value::{wasmer_value, wasmer_value_t, wasmer_value_tag},
wasmer_result_t,
};
use libc::{c_char, c_int, c_void};
use std::{collections::HashMap, ffi::CStr, slice};
use wasmer_runtime::{Ctx, Global, ImportObject, Instance, Memory, Table, Value};
use wasmer_runtime_core::{export::Export, import::Namespace};
use wasmer_runtime::{Ctx, Global, Instance, Memory, Module, Table, Value};
use wasmer_runtime_core::{
export::Export,
import::{ImportObject, Namespace},
};
#[repr(C)]
pub struct wasmer_instance_t;
@ -108,6 +112,32 @@ pub unsafe extern "C" fn wasmer_instantiate(
wasmer_result_t::WASMER_OK
}
/// Given:
/// * A prepared `wasmer` import-object
/// * A compiled wasmer module
///
/// Instantiates a wasmer instance
#[no_mangle]
pub unsafe extern "C" fn wasmer_module_import_instantiate(
instance: *mut *mut wasmer_instance_t,
module: *const wasmer_module_t,
import_object: *const wasmer_import_object_t,
) -> wasmer_result_t {
let import_object: &ImportObject = &*(import_object as *const ImportObject);
let module: &Module = &*(module as *const Module);
let new_instance: Instance = match module.instantiate(import_object) {
Ok(instance) => instance,
Err(error) => {
update_last_error(error);
return wasmer_result_t::WASMER_ERROR;
}
};
*instance = Box::into_raw(Box::new(new_instance)) as *mut wasmer_instance_t;
return wasmer_result_t::WASMER_OK;
}
/// Extracts the instance's context and returns it.
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]

View File

@ -24,3 +24,4 @@ test-module-serialize
test-tables
test-validate
test-context
test-module-import-instantiate

View File

@ -15,6 +15,7 @@ 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-context test-context.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
@ -92,3 +93,7 @@ add_test(test-validate test-validate)
target_link_libraries(test-context general ${WASMER_LIB})
target_compile_options(test-context PRIVATE ${COMPILER_OPTIONS})
add_test(test-context test-context)
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)

View File

@ -1,7 +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))

View File

@ -34,6 +34,11 @@ void inc_counter(wasmer_instance_context_t *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;
@ -90,12 +95,17 @@ int main()
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);
wasmer_import_t imports[] = {inc_import, get_import};
wasmer_import_t imports[] = {inc_import, mul_import, get_import};
// Read the wasm file
wasm_file_t wasm_file = read_wasm_file("assets/inc.wasm");
@ -103,8 +113,9 @@ int main()
// Instantiate instance
printf("Instantiating\n");
wasmer_instance_t *instance = NULL;
wasmer_result_t compile_result = wasmer_instantiate(&instance, wasm_file.bytes, wasm_file.bytes_len, imports, 2);
printf("Compile result: %d\n", compile_result);
wasmer_result_t instantiate_res = wasmer_instantiate(&instance, wasm_file.bytes, wasm_file.bytes_len, imports, 3);
printf("Compile result: %d\n", instantiate_res);
assert(instantiate_res == WASMER_OK);
// Init counter
counter_data *counter = init_counter(2, 5);

View File

@ -0,0 +1,149 @@
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
#include <string.h>
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);
assert(instantiate_res == WASMER_OK);
// 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;
}

View File

@ -95,11 +95,7 @@ typedef struct {
typedef struct {
} wasmer_instance_t;
typedef struct {
} wasmer_instance_context_t;
} wasmer_import_object_t;
typedef struct {
@ -119,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;
@ -392,6 +396,24 @@ wasmer_result_t wasmer_import_func_returns(const wasmer_import_func_t *func,
wasmer_result_t wasmer_import_func_returns_arity(const wasmer_import_func_t *func,
uint32_t *result);
/**
* 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(void);
/**
* Calls an instances exported function by `name` with the provided parameters.
* Results are set using the provided `results` pointer.
@ -531,6 +553,16 @@ wasmer_result_t wasmer_module_deserialize(wasmer_module_t **module,
*/
void wasmer_module_destroy(wasmer_module_t *module);
/**
* Given:
* A prepared `wasmer svm` import-object
* A compiled wasmer module
* Instantiates a wasmer instance
*/
wasmer_result_t wasmer_module_import_instantiate(wasmer_instance_t **instance,
const wasmer_module_t *module,
const wasmer_import_object_t *import_object);
/**
* Creates a new Instance from the given module and imports.
* Returns `wasmer_result_t::WASMER_OK` upon success.

View File

@ -91,11 +91,7 @@ struct wasmer_import_func_t {
};
struct wasmer_instance_t {
};
struct wasmer_instance_context_t {
struct wasmer_import_object_t {
};
@ -117,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;
@ -318,6 +322,18 @@ wasmer_result_t wasmer_import_func_returns(const wasmer_import_func_t *func,
wasmer_result_t wasmer_import_func_returns_arity(const wasmer_import_func_t *func,
uint32_t *result);
/// 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.
@ -421,6 +437,14 @@ wasmer_result_t wasmer_module_deserialize(wasmer_module_t **module,
/// Frees memory for the given Module
void wasmer_module_destroy(wasmer_module_t *module);
/// Given:
/// A prepared `wasmer svm` import-object
/// A compiled wasmer module
/// Instantiates a wasmer instance
wasmer_result_t wasmer_module_import_instantiate(wasmer_instance_t **instance,
const wasmer_module_t *module,
const wasmer_import_object_t *import_object);
/// Creates a new Instance from the given module and imports.
/// Returns `wasmer_result_t::WASMER_OK` upon success.
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`