From 6b617895b03ad7a51a9ab85c0770d3862b7a0f5c Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Wed, 20 May 2020 14:49:30 -0700 Subject: [PATCH] Add resolver chaining and recursive resolving --- lib/api/src/import_object.rs | 38 ++++++++-------- lib/api/src/lib.rs | 4 +- lib/c-api/tests/CMakeLists.txt | 1 - lib/engine/src/lib.rs | 4 +- lib/engine/src/resolver.rs | 80 ++++++++++++++++++++++++++++++++-- 5 files changed, 99 insertions(+), 28 deletions(-) diff --git a/lib/api/src/import_object.rs b/lib/api/src/import_object.rs index 24d1b2014..1d2b19e3e 100644 --- a/lib/api/src/import_object.rs +++ b/lib/api/src/import_object.rs @@ -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>(&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 diff --git a/lib/api/src/lib.rs b/lib/api/src/lib.rs index d4e0f99e2..fb3cf3af3 100644 --- a/lib/api/src/lib.rs +++ b/lib/api/src/lib.rs @@ -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}; diff --git a/lib/c-api/tests/CMakeLists.txt b/lib/c-api/tests/CMakeLists.txt index 13ab46e1b..74eb05e81 100644 --- a/lib/c-api/tests/CMakeLists.txt +++ b/lib/c-api/tests/CMakeLists.txt @@ -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) diff --git a/lib/engine/src/lib.rs b/lib/engine/src/lib.rs index 3a8546e25..118e08a40 100644 --- a/lib/engine/src/lib.rs +++ b/lib/engine/src/lib.rs @@ -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; diff --git a/lib/engine/src/resolver.rs b/lib/engine/src/resolver.rs index 2a1664d8e..508de0918 100644 --- a/lib/engine/src/resolver.rs +++ b/lib/engine/src/resolver.rs @@ -63,8 +63,8 @@ impl Resolver for T { pub struct NullResolver {} impl Resolver for NullResolver { - fn resolve(&self, _idx: u32, _module: &str, _field: &str) -> Option { - None + fn resolve(&self, _idx: u32, _module: &str, _field: &str) -> Result { + 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: 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(self, other: U) -> ResolverChain + 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(self, other: U) -> ResolverChain + where + U: Resolver, + { + ResolverChain { a: self, b: other } + } +} + +impl ChainableResolver for ResolverChain +where + A: Resolver, + B: Resolver, +{ +} + +impl Resolver for ResolverChain +where + A: Resolver, + B: Resolver, +{ + fn resolve(&self, index: u32, module: &str, field: &str) -> Result { + 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 Clone for ResolverChain +where + A: Resolver + Clone, + B: Resolver + Clone, +{ + fn clone(&self) -> Self { + Self { + a: self.a.clone(), + b: self.b.clone(), + } + } +}