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,
sync::{Arc, Mutex},
};
use wasmer_engine::NamedResolver;
use wasmer_engine::{ChainableResolver, NamedResolver};
use wasmer_runtime::Export;
/// 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.
pub struct ImportObjectIterator {
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
/// Generate an [`ImportObject`] easily with the `imports!` macro.
@ -210,6 +206,7 @@ impl Extend<((String, String), Export)> for ImportObject {
/// }
/// ```
#[macro_export]
// TOOD: port of lost fixes of imports macro from wasmer master/imports macro tests
macro_rules! imports {
( $( $ns_name:expr => $ns:tt ),* $(,)? ) => {{
use $crate::ImportObject;
@ -251,17 +248,17 @@ macro_rules! import_namespace {
#[cfg(test)]
mod test {
use super::*;
use crate::{Global, Store, Val};
use wasm_common::Type;
use wasmer_runtime::Export;
#[test]
#[ignore]
fn extending_works() {
fn chaining_works() {
let store = Store::default();
let g = Global::new(&store, Val::I32(0));
let mut imports1 = imports! {
let imports1 = imports! {
"dog" => {
"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");
assert!(small_cat_export.is_some());
let small_cat_export = resolver.resolve(0, "cat", "small");
assert!(small_cat_export.is_ok());
let happy = imports1.get_export("dog", "happy");
let small = imports1.get_export("dog", "small");
assert!(happy.is_some());
assert!(small.is_some());
let happy = resolver.resolve(0, "dog", "happy");
let small = resolver.resolve(0, "dog", "small");
assert!(happy.is_ok());
assert!(small.is_ok());
}
#[test]
#[ignore]
fn extending_conflict_overwrites() {
let store = Store::default();
let g1 = Global::new(&store, Val::I32(0));
@ -306,8 +302,8 @@ mod test {
},
};
imports1.extend(imports2);
let happy_dog_entry = imports1.get_export("dog", "happy").unwrap();
let resolver = imports1.chain_front(imports2);
let happy_dog_entry = resolver.resolve(0, "dog", "happy").unwrap();
assert!(if let Export::Global(happy_dog_global) = happy_dog_entry {
happy_dog_global.global.ty == Type::I64
@ -327,7 +323,7 @@ mod test {
"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 {
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::{Features, Target};
pub use wasmer_engine::{
DeserializeError, Engine, InstantiationError, LinkError, NamedResolver, Resolver, RuntimeError,
SerializeError,
ChainableResolver, DeserializeError, Engine, InstantiationError, LinkError, NamedResolver,
Resolver, ResolverChain, RuntimeError, SerializeError,
};
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_compile_options(test-import-object PRIVATE ${COMPILER_OPTIONS})
# TODO: reenable this test
#add_test(test-import-object test-import-object)

View File

@ -34,7 +34,9 @@ pub use crate::engine::Engine;
pub use crate::error::{
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::trap::*;
pub use crate::tunables::Tunables;

View File

@ -63,8 +63,8 @@ impl<T: NamedResolver> Resolver for T {
pub struct NullResolver {}
impl Resolver for NullResolver {
fn resolve(&self, _idx: u32, _module: &str, _field: &str) -> Option<Export> {
None
fn resolve(&self, _idx: u32, _module: &str, _field: &str) -> Result<Export, u32> {
Err(0)
}
}
@ -136,7 +136,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 {
let resolved = match resolved.ok() {
None => {
return Err(LinkError::Import(
module_name.to_string(),
@ -229,3 +229,77 @@ pub fn resolve_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(),
}
}
}