Add resolver chaining and recursive resolving

This commit is contained in:
Mark McCaskey
2020-05-20 14:49:30 -07:00
parent 2c9ba69767
commit 6b617895b0
5 changed files with 99 additions and 28 deletions

View File

@ -8,7 +8,7 @@ use std::{
ffi::c_void, ffi::c_void,
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
use wasmer_engine::NamedResolver; use wasmer_engine::{ChainableResolver, 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,6 +157,8 @@ 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)>,
@ -180,12 +182,6 @@ impl IntoIterator for ImportObject {
} }
} }
impl Extend<((String, String), Export)> for ImportObject {
fn extend<T: IntoIterator<Item = ((String, String), Export)>>(&mut self, _iter: T) {
unimplemented!("Extend not yet implemented");
}
}
// The import! macro for ImportObject // The import! macro for ImportObject
/// Generate an [`ImportObject`] easily with the `imports!` macro. /// Generate an [`ImportObject`] easily with the `imports!` macro.
@ -210,6 +206,7 @@ impl Extend<((String, String), Export)> for ImportObject {
/// } /// }
/// ``` /// ```
#[macro_export] #[macro_export]
// TOOD: port of lost fixes of imports macro from wasmer master/imports macro tests
macro_rules! imports { macro_rules! imports {
( $( $ns_name:expr => $ns:tt ),* $(,)? ) => {{ ( $( $ns_name:expr => $ns:tt ),* $(,)? ) => {{
use $crate::ImportObject; use $crate::ImportObject;
@ -251,17 +248,17 @@ macro_rules! import_namespace {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*;
use crate::{Global, Store, Val}; use crate::{Global, Store, Val};
use wasm_common::Type; use wasm_common::Type;
use wasmer_runtime::Export; use wasmer_runtime::Export;
#[test] #[test]
#[ignore] fn chaining_works() {
fn extending_works() {
let store = Store::default(); let store = Store::default();
let g = Global::new(&store, Val::I32(0)); let g = Global::new(&store, Val::I32(0));
let mut imports1 = imports! { let imports1 = imports! {
"dog" => { "dog" => {
"happy" => g.clone() "happy" => g.clone()
} }
@ -276,19 +273,18 @@ mod test {
} }
}; };
imports1.extend(imports2); let resolver = imports1.chain_front(imports2);
let small_cat_export = imports1.get_export("cat", "small"); let small_cat_export = resolver.resolve(0, "cat", "small");
assert!(small_cat_export.is_some()); assert!(small_cat_export.is_ok());
let happy = imports1.get_export("dog", "happy"); let happy = resolver.resolve(0, "dog", "happy");
let small = imports1.get_export("dog", "small"); let small = resolver.resolve(0, "dog", "small");
assert!(happy.is_some()); assert!(happy.is_ok());
assert!(small.is_some()); assert!(small.is_ok());
} }
#[test] #[test]
#[ignore]
fn extending_conflict_overwrites() { fn extending_conflict_overwrites() {
let store = Store::default(); let store = Store::default();
let g1 = Global::new(&store, Val::I32(0)); let g1 = Global::new(&store, Val::I32(0));
@ -306,8 +302,8 @@ mod test {
}, },
}; };
imports1.extend(imports2); let resolver = imports1.chain_front(imports2);
let happy_dog_entry = imports1.get_export("dog", "happy").unwrap(); let happy_dog_entry = resolver.resolve(0, "dog", "happy").unwrap();
assert!(if let Export::Global(happy_dog_global) = happy_dog_entry { assert!(if let Export::Global(happy_dog_global) = happy_dog_entry {
happy_dog_global.global.ty == Type::I64 happy_dog_global.global.ty == Type::I64
@ -327,7 +323,7 @@ mod test {
"dog" => namespace "dog" => namespace
}; };
let happy_dog_entry = imports1.get_export("dog", "happy").unwrap(); let happy_dog_entry = imports1.resolve(0, "dog", "happy").unwrap();
assert!(if let Export::Global(happy_dog_global) = happy_dog_entry { assert!(if let Export::Global(happy_dog_global) = happy_dog_entry {
happy_dog_global.global.ty == Type::I32 happy_dog_global.global.ty == Type::I32

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::{
DeserializeError, Engine, InstantiationError, LinkError, NamedResolver, Resolver, RuntimeError, ChainableResolver, DeserializeError, Engine, InstantiationError, LinkError, NamedResolver,
SerializeError, Resolver, ResolverChain, RuntimeError, SerializeError,
}; };
pub use wasmer_runtime::{raise_user_trap, MemoryError}; pub use wasmer_runtime::{raise_user_trap, MemoryError};

View File

@ -86,7 +86,6 @@ 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})
# TODO: reenable this test
#add_test(test-import-object test-import-object) #add_test(test-import-object test-import-object)

View File

@ -34,7 +34,9 @@ pub use crate::engine::Engine;
pub use crate::error::{ pub use crate::error::{
DeserializeError, ImportError, InstantiationError, LinkError, SerializeError, DeserializeError, ImportError, InstantiationError, LinkError, SerializeError,
}; };
pub use crate::resolver::{resolve_imports, NamedResolver, NullResolver, Resolver}; pub use crate::resolver::{
resolve_imports, ChainableResolver, NamedResolver, NullResolver, Resolver, ResolverChain,
};
pub use crate::serialize::SerializableFunctionFrameInfo; pub use crate::serialize::SerializableFunctionFrameInfo;
pub use crate::trap::*; pub use crate::trap::*;
pub use crate::tunables::Tunables; pub use crate::tunables::Tunables;

View File

@ -63,8 +63,8 @@ impl<T: NamedResolver> Resolver for T {
pub struct NullResolver {} pub struct NullResolver {}
impl Resolver for NullResolver { impl Resolver for NullResolver {
fn resolve(&self, _idx: u32, _module: &str, _field: &str) -> Option<Export> { fn resolve(&self, _idx: u32, _module: &str, _field: &str) -> Result<Export, u32> {
None Err(0)
} }
} }
@ -136,7 +136,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 { let resolved = match resolved.ok() {
None => { None => {
return Err(LinkError::Import( return Err(LinkError::Import(
module_name.to_string(), module_name.to_string(),
@ -229,3 +229,77 @@ pub fn resolve_imports(
global_imports, global_imports,
)) ))
} }
/// A [`Resolver`] that links two resolvers together in a chain.
pub struct ResolverChain<A: Resolver, B: Resolver> {
a: A,
b: B,
}
/// A trait for chaining resolvers together.
///
/// TODO: add example
pub trait ChainableResolver: Resolver + 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>
where
U: Resolver,
{
ResolverChain { a: other, b: self }
}
/// Chain a resolver behind the current resolver.
///
/// This will cause the first resolver to override the second.
///
/// TODO: add example
fn chain_back<U>(self, other: U) -> ResolverChain<Self, U>
where
U: Resolver,
{
ResolverChain { a: self, b: other }
}
}
impl<A, B> ChainableResolver for ResolverChain<A, B>
where
A: Resolver,
B: Resolver,
{
}
impl<A, B> Resolver for ResolverChain<A, B>
where
A: Resolver,
B: Resolver,
{
fn resolve(&self, index: u32, module: &str, field: &str) -> Result<Export, u32> {
if index == 0 {
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),
}
}
}
}
impl<A, B> Clone for ResolverChain<A, B>
where
A: Resolver + Clone,
B: Resolver + Clone,
{
fn clone(&self) -> Self {
Self {
a: self.a.clone(),
b: self.b.clone(),
}
}
}