Get 2 C API tests using ImportObject::extend working

This commit is contained in:
Mark McCaskey
2020-05-20 16:35:26 -07:00
parent 6b617895b0
commit a321f73988
10 changed files with 144 additions and 86 deletions

View File

@@ -8,7 +8,7 @@ use std::{
ffi::c_void, ffi::c_void,
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
use wasmer_engine::{ChainableResolver, NamedResolver}; use wasmer_engine::NamedResolver;
use wasmer_runtime::Export; use wasmer_runtime::Export;
/// The `LikeNamespace` trait represents objects that act as a namespace for imports. /// The `LikeNamespace` trait represents objects that act as a namespace for imports.
@@ -157,8 +157,6 @@ impl NamedResolver for ImportObject {
} }
} }
impl ChainableResolver for ImportObject {}
/// Iterator for an `ImportObject`'s exports. /// Iterator for an `ImportObject`'s exports.
pub struct ImportObjectIterator { pub struct ImportObjectIterator {
elements: VecDeque<((String, String), Export)>, elements: VecDeque<((String, String), Export)>,

View File

@@ -32,8 +32,8 @@ pub use wasm_common::{Bytes, Pages, ValueType, WasmExternType, WasmTypeList};
pub use wasmer_compiler::CompilerConfig; pub use wasmer_compiler::CompilerConfig;
pub use wasmer_compiler::{Features, Target}; pub use wasmer_compiler::{Features, Target};
pub use wasmer_engine::{ pub use wasmer_engine::{
ChainableResolver, DeserializeError, Engine, InstantiationError, LinkError, NamedResolver, ChainableNamedResolver, DeserializeError, Engine, InstantiationError, LinkError, NamedResolver,
Resolver, ResolverChain, RuntimeError, SerializeError, NamedResolverChain, Resolver, RuntimeError, SerializeError,
}; };
pub use wasmer_runtime::{raise_user_trap, MemoryError}; pub use wasmer_runtime::{raise_user_trap, MemoryError};

View File

@@ -8,7 +8,7 @@ use crate::{
}; };
use std::ptr; use std::ptr;
use wasmer::wasm::{Instance, Module}; use wasmer::{Instance, Module, NamedResolver};
use wasmer_emscripten::{EmscriptenData, EmscriptenGlobals}; use wasmer_emscripten::{EmscriptenData, EmscriptenGlobals};
/// Type used to construct an import_object_t with Emscripten imports. /// Type used to construct an import_object_t with Emscripten imports.
@@ -143,7 +143,13 @@ pub unsafe extern "C" fn wasmer_emscripten_generate_import_object(
} }
// TODO: figure out if we should be using UnsafeCell here or something // TODO: figure out if we should be using UnsafeCell here or something
let g = &mut *(globals as *mut EmscriptenGlobals); let g = &mut *(globals as *mut EmscriptenGlobals);
let import_object = Box::new(wasmer_emscripten::generate_emscripten_env(g)); let import_object_inner: Box<dyn NamedResolver> =
Box::new(wasmer_emscripten::generate_emscripten_env(g));
let import_object: Box<CAPIImportObject> = Box::new(CAPIImportObject {
import_object: import_object_inner,
imported_memories: vec![],
instance_pointers_to_update: vec![],
});
Box::into_raw(import_object) as *mut wasmer_import_object_t Box::into_raw(import_object) as *mut wasmer_import_object_t
} }

View File

@@ -10,23 +10,18 @@ use crate::{
wasmer_byte_array, wasmer_result_t, wasmer_byte_array, wasmer_result_t,
}; };
use libc::c_uint; use libc::c_uint;
use std::ffi::{c_void, CStr};
use std::os::raw::c_char;
use std::ptr::{self, NonNull}; use std::ptr::{self, NonNull};
use std::{ use std::slice;
//convert::TryFrom, // use std::sync::Arc;
ffi::{c_void, CStr}, // use std::convert::TryFrom,
os::raw::c_char, use std::collections::HashMap;
slice,
//sync::Arc,
};
use wasmer::{ use wasmer::{
Function, FunctionType, Global, ImportObject, ImportObjectIterator, ImportType, Memory, Module, ChainableNamedResolver, Exports, Extern, Function, FunctionType, Global, ImportObject,
RuntimeError, Table, Val, ValType, ImportObjectIterator, ImportType, Memory, Module, NamedResolver, RuntimeError, Table, Val,
ValType,
}; };
//use wasmer::wasm::{Export, FuncSig, Global, Memory, Module, Table, Type};
/*use wasmer_runtime_core::{
export::{Context, FuncPointer},
module::ImportName,
};*/
#[repr(C)] #[repr(C)]
pub struct wasmer_import_t { pub struct wasmer_import_t {
@@ -36,9 +31,25 @@ pub struct wasmer_import_t {
pub value: wasmer_import_export_value, pub value: wasmer_import_export_value,
} }
// An opaque wrapper around `CAPIImportObject`
#[repr(C)] #[repr(C)]
pub struct wasmer_import_object_t; pub struct wasmer_import_object_t;
/// A wrapper around a Wasmer ImportObject with extra data to maintain the existing API
pub(crate) struct CAPIImportObject {
/// The real wasmer `ImportObject`-like thing.
pub(crate) import_object: Box<dyn NamedResolver>,
/// List of pointers of memories that are imported into this import object. This is
/// required because the old Wasmer API allowed accessing these from the Instance
/// but the new/current API does not. So we store them here to pass them to the Instance
/// to allow functions to access this data for backwards compatibilty.
pub(crate) imported_memories: Vec<*mut Memory>,
/// List of pointers to `LegacyEnv`s used to patch imported functions to be able to
/// pass a the "vmctx" as the first argument.
/// Needed here because of extending import objects.
pub(crate) instance_pointers_to_update: Vec<NonNull<LegacyEnv>>,
}
#[repr(C)] #[repr(C)]
#[derive(Clone)] #[derive(Clone)]
pub struct wasmer_import_func_t; pub struct wasmer_import_func_t;
@@ -60,7 +71,12 @@ pub struct wasmer_import_object_iter_t;
#[allow(clippy::cast_ptr_alignment)] #[allow(clippy::cast_ptr_alignment)]
#[no_mangle] #[no_mangle]
pub extern "C" fn wasmer_import_object_new() -> NonNull<wasmer_import_object_t> { pub extern "C" fn wasmer_import_object_new() -> NonNull<wasmer_import_object_t> {
let import_object = Box::new(ImportObject::new()); let import_object_inner: Box<dyn NamedResolver> = Box::new(ImportObject::new());
let import_object = Box::new(CAPIImportObject {
import_object: import_object_inner,
imported_memories: vec![],
instance_pointers_to_update: vec![],
});
// TODO: use `Box::into_raw_non_null` when it becomes stable // TODO: use `Box::into_raw_non_null` when it becomes stable
unsafe { NonNull::new_unchecked(Box::into_raw(import_object) as *mut wasmer_import_object_t) } unsafe { NonNull::new_unchecked(Box::into_raw(import_object) as *mut wasmer_import_object_t) }
@@ -379,6 +395,7 @@ pub unsafe extern "C" fn wasmer_import_object_imports_destroy(
); );
match import.tag { match import.tag {
wasmer_import_export_kind::WASM_FUNCTION => { wasmer_import_export_kind::WASM_FUNCTION => {
// TODO: Wrapped function
let _: Box<Function> = Box::from_raw(import.value.func as *mut _); let _: Box<Function> = Box::from_raw(import.value.func as *mut _);
} }
wasmer_import_export_kind::WASM_GLOBAL => { wasmer_import_export_kind::WASM_GLOBAL => {
@@ -402,12 +419,9 @@ pub unsafe extern "C" fn wasmer_import_object_extend(
imports: *const wasmer_import_t, imports: *const wasmer_import_t,
imports_len: c_uint, imports_len: c_uint,
) -> wasmer_result_t { ) -> wasmer_result_t {
todo!("Disabled until import object APIs change") let import_object: &mut CAPIImportObject = &mut *(import_object as *mut CAPIImportObject);
/*
let import_object: &mut ImportObject = &mut *(import_object as *mut ImportObject);
let mut extensions: Vec<((String, String), Export)> = Vec::new();
let mut import_data: HashMap<String, Exports> = HashMap::new();
let imports: &[wasmer_import_t] = slice::from_raw_parts(imports, imports_len as usize); let imports: &[wasmer_import_t] = slice::from_raw_parts(imports, imports_len as usize);
for import in imports { for import in imports {
let module_name = slice::from_raw_parts( let module_name = slice::from_raw_parts(
@@ -438,12 +452,16 @@ pub unsafe extern "C" fn wasmer_import_object_extend(
let export = match import.tag { let export = match import.tag {
wasmer_import_export_kind::WASM_MEMORY => { wasmer_import_export_kind::WASM_MEMORY => {
let mem = import.value.memory as *mut Memory; let mem = import.value.memory as *mut Memory;
import_object.imported_memories.push(mem);
Extern::Memory((&*mem).clone()) Extern::Memory((&*mem).clone())
} }
wasmer_import_export_kind::WASM_FUNCTION => { wasmer_import_export_kind::WASM_FUNCTION => {
// TODO: investigate consistent usage of `FunctionWrapper` in this context // TODO: investigate consistent usage of `FunctionWrapper` in this context
let func_wrapper = import.value.func as *mut FunctionWrapper; let func_wrapper = import.value.func as *mut FunctionWrapper;
let func_export = func_wrapper.func.as_ptr(); let func_export = (*func_wrapper).func.as_ptr();
import_object
.instance_pointers_to_update
.push((*func_wrapper).legacy_env);
Extern::Function((&*func_export).clone()) Extern::Function((&*func_export).clone())
} }
wasmer_import_export_kind::WASM_GLOBAL => { wasmer_import_export_kind::WASM_GLOBAL => {
@@ -456,14 +474,34 @@ pub unsafe extern "C" fn wasmer_import_object_extend(
} }
}; };
let extension = ((module_name.to_string(), import_name.to_string()), export); let export_entry = import_data
extensions.push(extension) .entry(module_name.to_string())
.or_insert_with(Exports::new);
export_entry.insert(import_name.to_string(), export);
} }
import_object.extend(extensions); let mut new_import_object = ImportObject::new();
for (k, v) in import_data {
new_import_object.register(k, v);
}
// We use `Option<Box<T>>` here to perform a swap (move value out and move a new value in)
// Thus this code will break if `size_of::<Option<Box<T>>>` != `size_of::<Box<T>>`.
debug_assert_eq!(
std::mem::size_of::<Option<Box<dyn NamedResolver>>>(),
std::mem::size_of::<Box<dyn NamedResolver>>()
);
let import_object_inner: &mut Option<Box<dyn NamedResolver>> =
&mut *(&mut import_object.import_object as *mut Box<dyn NamedResolver>
as *mut Option<Box<dyn NamedResolver>>);
let import_object_value = import_object_inner.take().unwrap();
let new_resolver: Box<dyn NamedResolver> =
Box::new(import_object_value.chain_front(new_import_object));
*import_object_inner = Some(new_resolver);
return wasmer_result_t::WASMER_OK; return wasmer_result_t::WASMER_OK;
*/
} }
/// Gets import descriptors for the given module /// Gets import descriptors for the given module
@@ -834,6 +872,7 @@ pub unsafe extern "C" fn wasmer_import_object_destroy(
import_object: Option<NonNull<wasmer_import_object_t>>, import_object: Option<NonNull<wasmer_import_object_t>>,
) { ) {
if let Some(import_object) = import_object { if let Some(import_object) = import_object {
Box::from_raw(import_object.cast::<ImportObject>().as_ptr()); let _resolver_box: Box<Box<dyn NamedResolver>> =
Box::from_raw(import_object.cast::<Box<dyn NamedResolver>>().as_ptr());
} }
} }

View File

@@ -2,7 +2,7 @@ use super::*;
use crate::get_slice_checked; use crate::get_slice_checked;
use libc::c_uchar; use libc::c_uchar;
use std::{path::PathBuf, ptr, str}; use std::{path::PathBuf, ptr, str};
use wasmer::{Memory, MemoryType}; use wasmer::{Memory, MemoryType, NamedResolver};
use wasmer_wasi as wasi; use wasmer_wasi as wasi;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
@@ -190,11 +190,11 @@ fn wasmer_wasi_generate_import_object_inner(
let memory = Memory::new(store, memory_type); let memory = Memory::new(store, memory_type);
wasi_env.set_memory(&memory); wasi_env.set_memory(&memory);
let import_object = Box::new(wasi::generate_import_object_from_env( let import_object: Box<Box<dyn NamedResolver>> = Box::new(Box::new(wasi::generate_import_object_from_env(
store, store,
&mut wasi_env, &mut wasi_env,
version, version,
)); )));
Ok(Box::into_raw(import_object) as *mut wasmer_import_object_t) Ok(Box::into_raw(import_object) as *mut wasmer_import_object_t)
*/ */
} }
@@ -216,11 +216,14 @@ pub unsafe extern "C" fn wasmer_wasi_generate_default_import_object() -> *mut wa
let memory = Memory::new(store, memory_type).expect("create memory"); let memory = Memory::new(store, memory_type).expect("create memory");
wasi_env.set_memory(&memory); wasi_env.set_memory(&memory);
// TODO(mark): review lifetime of `Memory` here // TODO(mark): review lifetime of `Memory` here
let import_object = Box::new(wasi::generate_import_object_from_env( let import_object_inner: Box<dyn NamedResolver> = Box::new(
store, wasi::generate_import_object_from_env(store, &mut wasi_env, wasi::WasiVersion::Latest),
&mut wasi_env, );
wasi::WasiVersion::Latest, let import_object: Box<CAPIImportObject> = Box::new(CAPIImportObject {
)); import_object: import_object_inner,
imported_memories: vec![],
instance_pointers_to_update: vec![],
});
Box::into_raw(import_object) as *mut wasmer_import_object_t Box::into_raw(import_object) as *mut wasmer_import_object_t
} }

View File

@@ -3,12 +3,13 @@
use crate::{ use crate::{
error::{update_last_error, CApiError}, error::{update_last_error, CApiError},
export::wasmer_import_export_kind, export::wasmer_import_export_kind,
import::{wasmer_import_object_t, wasmer_import_t}, import::{wasmer_import_object_t, wasmer_import_t, CAPIImportObject},
instance::{wasmer_instance_t, CAPIInstance}, instance::{wasmer_instance_t, CAPIInstance},
wasmer_byte_array, wasmer_result_t, wasmer_byte_array, wasmer_result_t,
}; };
use libc::c_int; use libc::c_int;
use std::collections::HashMap; use std::collections::HashMap;
use std::ptr::NonNull;
use std::slice; use std::slice;
use wasmer::{Exports, Extern, Function, Global, ImportObject, Instance, Memory, Module, Table}; use wasmer::{Exports, Extern, Function, Global, ImportObject, Instance, Memory, Module, Table};
@@ -172,23 +173,27 @@ pub unsafe extern "C" fn wasmer_module_import_instantiate(
module: *const wasmer_module_t, module: *const wasmer_module_t,
import_object: *const wasmer_import_object_t, import_object: *const wasmer_import_object_t,
) -> wasmer_result_t { ) -> wasmer_result_t {
let import_object: &ImportObject = &*(import_object as *const ImportObject); // mutable to mutate through `instance_pointers_to_update` to make host functions work
let import_object: &mut CAPIImportObject = &mut *(import_object as *mut CAPIImportObject);
let module: &Module = &*(module as *const Module); let module: &Module = &*(module as *const Module);
let new_instance: Instance = match Instance::new(module, import_object) { let new_instance: Instance = match Instance::new(module, &import_object.import_object) {
Ok(instance) => instance, Ok(instance) => instance,
Err(error) => { Err(error) => {
update_last_error(error); update_last_error(error);
return wasmer_result_t::WASMER_ERROR; return wasmer_result_t::WASMER_ERROR;
} }
}; };
let imported_memories = todo!("get imported memories");
let c_api_instance = CAPIInstance { let c_api_instance = CAPIInstance {
instance: new_instance, instance: new_instance,
imported_memories, imported_memories: import_object.imported_memories.clone(),
ctx_data: None, ctx_data: None,
}; };
*instance = Box::into_raw(Box::new(c_api_instance)) as *mut wasmer_instance_t; let c_api_instance_pointer = Box::into_raw(Box::new(c_api_instance));
for to_update in import_object.instance_pointers_to_update.iter_mut() {
to_update.as_mut().instance_ptr = Some(NonNull::new_unchecked(c_api_instance_pointer));
}
*instance = c_api_instance_pointer as *mut wasmer_instance_t;
return wasmer_result_t::WASMER_OK; return wasmer_result_t::WASMER_OK;
} }

View File

@@ -86,7 +86,7 @@ add_test(test-imports test-imports)
target_link_libraries(test-import-object general ${WASMER_LIB}) target_link_libraries(test-import-object general ${WASMER_LIB})
target_compile_options(test-import-object PRIVATE ${COMPILER_OPTIONS}) target_compile_options(test-import-object PRIVATE ${COMPILER_OPTIONS})
#add_test(test-import-object test-import-object) add_test(test-import-object test-import-object)
if (DEFINED WASI_TESTS) if (DEFINED WASI_TESTS)
@@ -140,8 +140,7 @@ add_test(test-context test-context)
target_link_libraries(test-module-import-instantiate general ${WASMER_LIB}) target_link_libraries(test-module-import-instantiate general ${WASMER_LIB})
target_compile_options(test-module-import-instantiate PRIVATE ${COMPILER_OPTIONS}) target_compile_options(test-module-import-instantiate PRIVATE ${COMPILER_OPTIONS})
# TODO: reenable this test add_test(test-module-import-instantiate test-module-import-instantiate)
#add_test(test-module-import-instantiate test-module-import-instantiate)
# Commented out until we can find a solution to the exported function problem # Commented out until we can find a solution to the exported function problem
# #

View File

@@ -119,7 +119,13 @@ int main()
wasmer_import_t imports[] = {func_import, global_import, memory_import, table_import}; wasmer_import_t imports[] = {func_import, global_import, memory_import, table_import};
int imports_len = sizeof(imports) / sizeof(imports[0]); int imports_len = sizeof(imports) / sizeof(imports[0]);
// Add our imports to the import object // Add our imports to the import object
wasmer_import_object_extend(import_object, imports, imports_len); wasmer_result_t extend_result = wasmer_import_object_extend(import_object, imports, imports_len);
if (extend_result != WASMER_OK)
{
print_wasmer_error();
}
assert(extend_result == WASMER_OK);
printf("Set up import object\n");
// Read the wasm file bytes // Read the wasm file bytes
FILE *file = fopen("assets/hello_wasm.wasm", "r"); FILE *file = fopen("assets/hello_wasm.wasm", "r");

View File

@@ -35,7 +35,8 @@ pub use crate::error::{
DeserializeError, ImportError, InstantiationError, LinkError, SerializeError, DeserializeError, ImportError, InstantiationError, LinkError, SerializeError,
}; };
pub use crate::resolver::{ pub use crate::resolver::{
resolve_imports, ChainableResolver, NamedResolver, NullResolver, Resolver, ResolverChain, resolve_imports, ChainableNamedResolver, NamedResolver, NamedResolverChain, NullResolver,
Resolver,
}; };
pub use crate::serialize::SerializableFunctionFrameInfo; pub use crate::serialize::SerializableFunctionFrameInfo;
pub use crate::trap::*; pub use crate::trap::*;

View File

@@ -59,12 +59,24 @@ impl<T: NamedResolver> Resolver for T {
} }
} }
impl<T: NamedResolver> NamedResolver for &T {
fn resolve_by_name(&self, module: &str, field: &str) -> Option<Export> {
(**self).resolve_by_name(module, field)
}
}
impl NamedResolver for Box<dyn NamedResolver> {
fn resolve_by_name(&self, module: &str, field: &str) -> Option<Export> {
(**self).resolve_by_name(module, field)
}
}
/// `Resolver` implementation that always resolves to `None`. /// `Resolver` implementation that always resolves to `None`.
pub struct NullResolver {} pub struct NullResolver {}
impl Resolver for NullResolver { impl Resolver for NullResolver {
fn resolve(&self, _idx: u32, _module: &str, _field: &str) -> Result<Export, u32> { fn resolve(&self, _idx: u32, _module: &str, _field: &str) -> Option<Export> {
Err(0) None
} }
} }
@@ -136,7 +148,7 @@ pub fn resolve_imports(
for ((module_name, field, import_idx), import_index) in module.imports.iter() { for ((module_name, field, import_idx), import_index) in module.imports.iter() {
let resolved = resolver.resolve(*import_idx, module_name, field); let resolved = resolver.resolve(*import_idx, module_name, field);
let import_extern = get_extern_from_import(module, import_index); let import_extern = get_extern_from_import(module, import_index);
let resolved = match resolved.ok() { let resolved = match resolved {
None => { None => {
return Err(LinkError::Import( return Err(LinkError::Import(
module_name.to_string(), module_name.to_string(),
@@ -231,7 +243,7 @@ pub fn resolve_imports(
} }
/// A [`Resolver`] that links two resolvers together in a chain. /// A [`Resolver`] that links two resolvers together in a chain.
pub struct ResolverChain<A: Resolver, B: Resolver> { pub struct NamedResolverChain<A: NamedResolver, B: NamedResolver> {
a: A, a: A,
b: B, b: B,
} }
@@ -239,17 +251,17 @@ pub struct ResolverChain<A: Resolver, B: Resolver> {
/// A trait for chaining resolvers together. /// A trait for chaining resolvers together.
/// ///
/// TODO: add example /// TODO: add example
pub trait ChainableResolver: Resolver + Sized { pub trait ChainableNamedResolver: NamedResolver + Sized {
/// Chain a resolver in front of the current resolver. /// Chain a resolver in front of the current resolver.
/// ///
/// This will cause the second resolver to override the first. /// This will cause the second resolver to override the first.
/// ///
/// TODO: add example /// TODO: add example
fn chain_front<U>(self, other: U) -> ResolverChain<U, Self> fn chain_front<U>(self, other: U) -> NamedResolverChain<U, Self>
where where
U: Resolver, U: NamedResolver,
{ {
ResolverChain { a: other, b: self } NamedResolverChain { a: other, b: self }
} }
/// Chain a resolver behind the current resolver. /// Chain a resolver behind the current resolver.
@@ -257,44 +269,33 @@ pub trait ChainableResolver: Resolver + Sized {
/// This will cause the first resolver to override the second. /// This will cause the first resolver to override the second.
/// ///
/// TODO: add example /// TODO: add example
fn chain_back<U>(self, other: U) -> ResolverChain<Self, U> fn chain_back<U>(self, other: U) -> NamedResolverChain<Self, U>
where where
U: Resolver, U: NamedResolver,
{ {
ResolverChain { a: self, b: other } NamedResolverChain { a: self, b: other }
} }
} }
impl<A, B> ChainableResolver for ResolverChain<A, B> // We give these chain methods to all types implementing NamedResolver
where impl<T: NamedResolver> ChainableNamedResolver for T {}
A: Resolver,
B: Resolver,
{
}
impl<A, B> Resolver for ResolverChain<A, B> impl<A, B> NamedResolver for NamedResolverChain<A, B>
where where
A: Resolver, A: NamedResolver,
B: Resolver, B: NamedResolver,
{ {
fn resolve(&self, index: u32, module: &str, field: &str) -> Result<Export, u32> { fn resolve_by_name(&self, module: &str, field: &str) -> Option<Export> {
if index == 0 { self.a
self.a .resolve_by_name(module, field)
.resolve(0, module, field) .or_else(|| self.b.resolve_by_name(module, field))
.or_else(|e1| self.b.resolve(0, module, field).map_err(|e2| e1 + e2))
} else {
match self.a.resolve(index, module, field) {
Ok(_) => self.b.resolve(index - 1, module, field).map_err(|e| e + 1),
Err(e1) => self.b.resolve(index, module, field).map_err(|e2| e1 + e2),
}
}
} }
} }
impl<A, B> Clone for ResolverChain<A, B> impl<A, B> Clone for NamedResolverChain<A, B>
where where
A: Resolver + Clone, A: NamedResolver + Clone,
B: Resolver + Clone, B: NamedResolver + Clone,
{ {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {