mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-12 05:18:43 +00:00
Get 2 C API tests using ImportObject::extend working
This commit is contained in:
@@ -8,7 +8,7 @@ use std::{
|
||||
ffi::c_void,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use wasmer_engine::{ChainableResolver, NamedResolver};
|
||||
use wasmer_engine::NamedResolver;
|
||||
use wasmer_runtime::Export;
|
||||
|
||||
/// 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.
|
||||
pub struct ImportObjectIterator {
|
||||
elements: VecDeque<((String, String), Export)>,
|
||||
|
||||
@@ -32,8 +32,8 @@ pub use wasm_common::{Bytes, Pages, ValueType, WasmExternType, WasmTypeList};
|
||||
pub use wasmer_compiler::CompilerConfig;
|
||||
pub use wasmer_compiler::{Features, Target};
|
||||
pub use wasmer_engine::{
|
||||
ChainableResolver, DeserializeError, Engine, InstantiationError, LinkError, NamedResolver,
|
||||
Resolver, ResolverChain, RuntimeError, SerializeError,
|
||||
ChainableNamedResolver, DeserializeError, Engine, InstantiationError, LinkError, NamedResolver,
|
||||
NamedResolverChain, Resolver, RuntimeError, SerializeError,
|
||||
};
|
||||
pub use wasmer_runtime::{raise_user_trap, MemoryError};
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::{
|
||||
};
|
||||
|
||||
use std::ptr;
|
||||
use wasmer::wasm::{Instance, Module};
|
||||
use wasmer::{Instance, Module, NamedResolver};
|
||||
use wasmer_emscripten::{EmscriptenData, EmscriptenGlobals};
|
||||
|
||||
/// 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
|
||||
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
|
||||
}
|
||||
|
||||
@@ -10,23 +10,18 @@ use crate::{
|
||||
wasmer_byte_array, wasmer_result_t,
|
||||
};
|
||||
use libc::c_uint;
|
||||
use std::ffi::{c_void, CStr};
|
||||
use std::os::raw::c_char;
|
||||
use std::ptr::{self, NonNull};
|
||||
use std::{
|
||||
//convert::TryFrom,
|
||||
ffi::{c_void, CStr},
|
||||
os::raw::c_char,
|
||||
slice,
|
||||
//sync::Arc,
|
||||
};
|
||||
use std::slice;
|
||||
// use std::sync::Arc;
|
||||
// use std::convert::TryFrom,
|
||||
use std::collections::HashMap;
|
||||
use wasmer::{
|
||||
Function, FunctionType, Global, ImportObject, ImportObjectIterator, ImportType, Memory, Module,
|
||||
RuntimeError, Table, Val, ValType,
|
||||
ChainableNamedResolver, Exports, Extern, Function, FunctionType, Global, ImportObject,
|
||||
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)]
|
||||
pub struct wasmer_import_t {
|
||||
@@ -36,9 +31,25 @@ pub struct wasmer_import_t {
|
||||
pub value: wasmer_import_export_value,
|
||||
}
|
||||
|
||||
// An opaque wrapper around `CAPIImportObject`
|
||||
#[repr(C)]
|
||||
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)]
|
||||
#[derive(Clone)]
|
||||
pub struct wasmer_import_func_t;
|
||||
@@ -60,7 +71,12 @@ pub struct wasmer_import_object_iter_t;
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
#[no_mangle]
|
||||
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
|
||||
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 {
|
||||
wasmer_import_export_kind::WASM_FUNCTION => {
|
||||
// TODO: Wrapped function
|
||||
let _: Box<Function> = Box::from_raw(import.value.func as *mut _);
|
||||
}
|
||||
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_len: c_uint,
|
||||
) -> wasmer_result_t {
|
||||
todo!("Disabled until import object APIs change")
|
||||
/*
|
||||
let import_object: &mut ImportObject = &mut *(import_object as *mut ImportObject);
|
||||
|
||||
let mut extensions: Vec<((String, String), Export)> = Vec::new();
|
||||
let import_object: &mut CAPIImportObject = &mut *(import_object as *mut CAPIImportObject);
|
||||
|
||||
let mut import_data: HashMap<String, Exports> = HashMap::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(
|
||||
@@ -438,12 +452,16 @@ pub unsafe extern "C" fn wasmer_import_object_extend(
|
||||
let export = match import.tag {
|
||||
wasmer_import_export_kind::WASM_MEMORY => {
|
||||
let mem = import.value.memory as *mut Memory;
|
||||
import_object.imported_memories.push(mem);
|
||||
Extern::Memory((&*mem).clone())
|
||||
}
|
||||
wasmer_import_export_kind::WASM_FUNCTION => {
|
||||
// TODO: investigate consistent usage of `FunctionWrapper` in this context
|
||||
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())
|
||||
}
|
||||
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);
|
||||
extensions.push(extension)
|
||||
let export_entry = import_data
|
||||
.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;
|
||||
*/
|
||||
}
|
||||
|
||||
/// 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>>,
|
||||
) {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use super::*;
|
||||
use crate::get_slice_checked;
|
||||
use libc::c_uchar;
|
||||
use std::{path::PathBuf, ptr, str};
|
||||
use wasmer::{Memory, MemoryType};
|
||||
use wasmer::{Memory, MemoryType, NamedResolver};
|
||||
use wasmer_wasi as wasi;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
@@ -190,11 +190,11 @@ fn wasmer_wasi_generate_import_object_inner(
|
||||
let memory = Memory::new(store, memory_type);
|
||||
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,
|
||||
&mut wasi_env,
|
||||
version,
|
||||
));
|
||||
)));
|
||||
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");
|
||||
wasi_env.set_memory(&memory);
|
||||
// TODO(mark): review lifetime of `Memory` here
|
||||
let import_object = Box::new(wasi::generate_import_object_from_env(
|
||||
store,
|
||||
&mut wasi_env,
|
||||
wasi::WasiVersion::Latest,
|
||||
));
|
||||
let import_object_inner: Box<dyn NamedResolver> = Box::new(
|
||||
wasi::generate_import_object_from_env(store, &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
|
||||
}
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
use crate::{
|
||||
error::{update_last_error, CApiError},
|
||||
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},
|
||||
wasmer_byte_array, wasmer_result_t,
|
||||
};
|
||||
use libc::c_int;
|
||||
use std::collections::HashMap;
|
||||
use std::ptr::NonNull;
|
||||
use std::slice;
|
||||
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,
|
||||
import_object: *const wasmer_import_object_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 new_instance: Instance = match Instance::new(module, import_object) {
|
||||
let new_instance: Instance = match Instance::new(module, &import_object.import_object) {
|
||||
Ok(instance) => instance,
|
||||
Err(error) => {
|
||||
update_last_error(error);
|
||||
return wasmer_result_t::WASMER_ERROR;
|
||||
}
|
||||
};
|
||||
let imported_memories = todo!("get imported memories");
|
||||
let c_api_instance = CAPIInstance {
|
||||
instance: new_instance,
|
||||
imported_memories,
|
||||
imported_memories: import_object.imported_memories.clone(),
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ 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)
|
||||
add_test(test-import-object test-import-object)
|
||||
|
||||
|
||||
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_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
|
||||
#
|
||||
|
||||
@@ -119,7 +119,13 @@ int main()
|
||||
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);
|
||||
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
|
||||
FILE *file = fopen("assets/hello_wasm.wasm", "r");
|
||||
|
||||
@@ -35,7 +35,8 @@ pub use crate::error::{
|
||||
DeserializeError, ImportError, InstantiationError, LinkError, SerializeError,
|
||||
};
|
||||
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::trap::*;
|
||||
|
||||
@@ -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`.
|
||||
pub struct NullResolver {}
|
||||
|
||||
impl Resolver for NullResolver {
|
||||
fn resolve(&self, _idx: u32, _module: &str, _field: &str) -> Result<Export, u32> {
|
||||
Err(0)
|
||||
fn resolve(&self, _idx: u32, _module: &str, _field: &str) -> Option<Export> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +148,7 @@ pub fn resolve_imports(
|
||||
for ((module_name, field, import_idx), import_index) in module.imports.iter() {
|
||||
let resolved = resolver.resolve(*import_idx, module_name, field);
|
||||
let import_extern = get_extern_from_import(module, import_index);
|
||||
let resolved = match resolved.ok() {
|
||||
let resolved = match resolved {
|
||||
None => {
|
||||
return Err(LinkError::Import(
|
||||
module_name.to_string(),
|
||||
@@ -231,7 +243,7 @@ pub fn resolve_imports(
|
||||
}
|
||||
|
||||
/// 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,
|
||||
b: B,
|
||||
}
|
||||
@@ -239,17 +251,17 @@ pub struct ResolverChain<A: Resolver, B: Resolver> {
|
||||
/// A trait for chaining resolvers together.
|
||||
///
|
||||
/// TODO: add example
|
||||
pub trait ChainableResolver: Resolver + Sized {
|
||||
pub trait ChainableNamedResolver: NamedResolver + Sized {
|
||||
/// Chain a resolver in front of the current resolver.
|
||||
///
|
||||
/// This will cause the second resolver to override the first.
|
||||
///
|
||||
/// TODO: add example
|
||||
fn chain_front<U>(self, other: U) -> ResolverChain<U, Self>
|
||||
fn chain_front<U>(self, other: U) -> NamedResolverChain<U, Self>
|
||||
where
|
||||
U: Resolver,
|
||||
U: NamedResolver,
|
||||
{
|
||||
ResolverChain { a: other, b: self }
|
||||
NamedResolverChain { a: other, b: self }
|
||||
}
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// TODO: add example
|
||||
fn chain_back<U>(self, other: U) -> ResolverChain<Self, U>
|
||||
fn chain_back<U>(self, other: U) -> NamedResolverChain<Self, U>
|
||||
where
|
||||
U: Resolver,
|
||||
U: NamedResolver,
|
||||
{
|
||||
ResolverChain { a: self, b: other }
|
||||
NamedResolverChain { a: self, b: other }
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> ChainableResolver for ResolverChain<A, B>
|
||||
where
|
||||
A: Resolver,
|
||||
B: Resolver,
|
||||
{
|
||||
}
|
||||
// We give these chain methods to all types implementing NamedResolver
|
||||
impl<T: NamedResolver> ChainableNamedResolver for T {}
|
||||
|
||||
impl<A, B> Resolver for ResolverChain<A, B>
|
||||
impl<A, B> NamedResolver for NamedResolverChain<A, B>
|
||||
where
|
||||
A: Resolver,
|
||||
B: Resolver,
|
||||
A: NamedResolver,
|
||||
B: NamedResolver,
|
||||
{
|
||||
fn resolve(&self, index: u32, module: &str, field: &str) -> Result<Export, u32> {
|
||||
if index == 0 {
|
||||
fn resolve_by_name(&self, module: &str, field: &str) -> Option<Export> {
|
||||
self.a
|
||||
.resolve(0, 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),
|
||||
}
|
||||
}
|
||||
.resolve_by_name(module, field)
|
||||
.or_else(|| self.b.resolve_by_name(module, field))
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> Clone for ResolverChain<A, B>
|
||||
impl<A, B> Clone for NamedResolverChain<A, B>
|
||||
where
|
||||
A: Resolver + Clone,
|
||||
B: Resolver + Clone,
|
||||
A: NamedResolver + Clone,
|
||||
B: NamedResolver + Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
|
||||
Reference in New Issue
Block a user