api/sys: Replace ImportObject with new type Imports

This commit is contained in:
Manos Pitsidianakis
2022-04-14 19:29:23 +03:00
parent 308d8a0a4b
commit 8aa225a192
26 changed files with 720 additions and 696 deletions

View File

@@ -1,6 +1,5 @@
use crate::js::export::Export;
use crate::js::externals::{Extern, Function, Global, Memory, Table};
use crate::js::import_object::LikeNamespace;
use crate::js::native::NativeFunc;
use crate::js::WasmTypeList;
use indexmap::IndexMap;
@@ -193,6 +192,32 @@ impl Exports {
iter: self.map.iter(),
}
}
/// safa
pub fn get_namespace_export(&self, name: &str) -> Option<Export> {
self.map.get(name).map(|is_export| is_export.to_export())
}
/// safa
pub fn get_namespace_externs(&self) -> Vec<(String, Extern)> {
self.map
.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect()
}
/// safa
pub fn get_namespace_exports(&self) -> Vec<(String, Export)> {
self.map
.iter()
.map(|(k, v)| (k.clone(), v.to_export()))
.collect()
}
/// safa
pub fn as_exports(&self) -> Option<Exports> {
Some(self.clone())
}
}
impl fmt::Debug for Exports {
@@ -274,16 +299,21 @@ impl FromIterator<(String, Extern)> for Exports {
}
}
impl LikeNamespace for Exports {
fn get_namespace_export(&self, name: &str) -> Option<Export> {
self.map.get(name).map(|is_export| is_export.to_export())
}
impl IntoIterator for Exports {
type IntoIter = std::vec::IntoIter<(String, Extern)>;
type Item = (String, Extern);
fn get_namespace_exports(&self) -> Vec<(String, Export)> {
self.map
.iter()
.map(|(k, v)| (k.clone(), v.to_export()))
.collect()
fn into_iter(self) -> Self::IntoIter {
self.get_namespace_externs().into_iter()
}
}
impl IntoIterator for &Exports {
type IntoIter = std::vec::IntoIter<(String, Extern)>;
type Item = (String, Extern);
fn into_iter(self) -> Self::IntoIter {
self.get_namespace_externs().into_iter()
}
}

View File

@@ -2,35 +2,25 @@
//! manipulate and access a wasm module's imports including memories, tables, globals, and
//! functions.
use crate::js::export::Export;
use crate::js::exports::Exportable;
use crate::js::resolver::NamedResolver;
use crate::Extern;
use std::borrow::{Borrow, BorrowMut};
use std::collections::VecDeque;
use std::collections::{hash_map::Entry, HashMap};
use std::fmt;
use std::sync::{Arc, Mutex};
/// The `LikeNamespace` trait represents objects that act as a namespace for imports.
/// For example, an `Instance` or `Namespace` could be
/// considered namespaces that could provide imports to an instance.
pub trait LikeNamespace {
/// Gets an export by name.
fn get_namespace_export(&self, name: &str) -> Option<Export>;
/// Gets all exports in the namespace.
fn get_namespace_exports(&self) -> Vec<(String, Export)>;
}
/// All of the import data used when instantiating.
///
/// It's suggested that you use the [`imports!`] macro
/// instead of creating an `ImportObject` by hand.
/// instead of creating an `Imports` by hand.
///
/// [`imports!`]: macro.imports.html
///
/// # Usage:
/// ```ignore
/// use wasmer::{Exports, ImportObject, Function};
/// use wasmer::{Exports, Imports, Function};
///
/// let mut import_object = ImportObject::new();
/// let mut import_object = Imports::new();
/// let mut env = Exports::new();
///
/// env.insert("foo", Function::new_native(foo));
@@ -41,134 +31,142 @@ pub trait LikeNamespace {
/// }
/// ```
#[derive(Clone, Default)]
pub struct ImportObject {
map: Arc<Mutex<HashMap<String, Box<dyn LikeNamespace + Send + Sync>>>>,
pub struct Imports {
map: HashMap<(String, String), Extern>,
}
impl ImportObject {
/// Create a new `ImportObject`.
impl Imports {
/// Create a new `Imports`.
pub fn new() -> Self {
Default::default()
}
/// Gets an export given a module and a name
/// Gets an export given a ns and a name
///
/// # Usage
/// ```ignore
/// # use wasmer::{ImportObject, Instance, Namespace};
/// let mut import_object = ImportObject::new();
/// import_object.get_export("module", "name");
/// # use wasmer::{Imports, Instance, Namespace};
/// let mut import_object = Imports::new();
/// import_object.get_export("ns", "name");
/// ```
pub fn get_export(&self, module: &str, name: &str) -> Option<Export> {
let guard = self.map.lock().unwrap();
let map_ref = guard.borrow();
if map_ref.contains_key(module) {
let namespace = map_ref[module].as_ref();
return namespace.get_namespace_export(name);
pub fn get_export(&self, ns: &str, name: &str) -> Option<Export> {
if self.map.contains_key(&(ns.to_string(), name.to_string())) {
let ext = &self.map[&(ns.to_string(), name.to_string())];
return Some(ext.to_export());
}
None
}
/// Returns true if the ImportObject contains namespace with the provided name.
/// Returns true if the Imports contains namespace with the provided name.
pub fn contains_namespace(&self, name: &str) -> bool {
self.map.lock().unwrap().borrow().contains_key(name)
self.map.keys().any(|(k, _)| (k == name))
}
/// Register anything that implements `LikeNamespace` as a namespace.
///
/// # Usage:
/// ```ignore
/// # use wasmer::{ImportObject, Instance, Namespace};
/// let mut import_object = ImportObject::new();
///
/// import_object.register("namespace0", instance);
/// import_object.register("namespace1", namespace);
/// // ...
/// ```
pub fn register<S, N>(&mut self, name: S, namespace: N) -> Option<Box<dyn LikeNamespace>>
where
S: Into<String>,
N: LikeNamespace + Send + Sync + 'static,
{
let mut guard = self.map.lock().unwrap();
let map = guard.borrow_mut();
match map.entry(name.into()) {
Entry::Vacant(empty) => {
empty.insert(Box::new(namespace));
None
}
Entry::Occupied(mut occupied) => Some(occupied.insert(Box::new(namespace))),
/// TODO: Add doc
pub fn register_namespace(
&mut self,
ns: &str,
contents: impl IntoIterator<Item = (String, Extern)>,
) {
for (name, extern_) in contents.into_iter() {
self.map.insert((ns.to_string(), name.clone()), extern_);
}
}
fn get_objects(&self) -> VecDeque<((String, String), Export)> {
let mut out = VecDeque::new();
let guard = self.map.lock().unwrap();
let map = guard.borrow();
for (name, ns) in map.iter() {
for (id, exp) in ns.get_namespace_exports() {
out.push_back(((name.clone(), id), exp));
}
}
out
/// TODO: Add doc
pub fn define(&mut self, ns: &str, name: &str, extern_: Extern) {
self.map.insert((ns.to_string(), name.to_string()), extern_);
}
/// Returns the `ImportObject` as a Javascript `Object`
// /// Register anything that implements `LikeNamespace` as a namespace.
// ///
// /// # Usage:
// /// ```ignore
// /// # use wasmer::{Imports, Instance, Namespace};
// /// let mut import_object = Imports::new();
// ///
// /// import_object.register("namespace0", instance);
// /// import_object.register("namespace1", namespace);
// /// // ...
// /// ```
// pub fn register<S, N>(&mut self, name: S, namespace: N) -> Option<Box<dyn LikeNamespace>>
// where
// S: Into<String>,
// N: LikeNamespace + Send + Sync + 'static,
// {
// let mut guard = self.map.lock().unwrap();
// let map = guard.borrow_mut();
// match map.entry(name.into()) {
// Entry::Vacant(empty) => {
// empty.insert(Box::new(namespace));
// None
// }
// Entry::Occupied(mut occupied) => Some(occupied.insert(Box::new(namespace))),
// }
// }
/// asdfsa
pub fn resolve_by_name(&self, ns: &str, name: &str) -> Option<Export> {
self.get_export(ns, name)
}
//fn iter(&self) -> impl Iterator<(&str, &str, Extern)> {
// todo!()
//}
/// Returns the `Imports` as a Javascript `Object`
pub fn as_jsobject(&self) -> js_sys::Object {
let guard = self.map.lock().expect("Can't get the map");
let map = guard.borrow();
let imports = js_sys::Object::new();
for (module, ns) in map.iter() {
let namespaces: HashMap<&str, Vec<(&str, &Extern)>> =
self.map
.iter()
.fold(HashMap::default(), |mut acc, ((ns, name), ext)| {
acc.entry(ns.as_str())
.or_default()
.push((name.as_str(), ext));
acc
});
for (ns, exports) in namespaces.into_iter() {
let import_namespace = js_sys::Object::new();
for (name, exp) in ns.get_namespace_exports() {
js_sys::Reflect::set(&import_namespace, &name.into(), exp.as_jsvalue())
.expect("Error while setting into the js namespace object");
for (name, ext) in exports {
js_sys::Reflect::set(
&import_namespace,
&name.into(),
ext.to_export().as_jsvalue(),
)
.expect("Error while setting into the js namespace object");
}
js_sys::Reflect::set(&imports, &module.into(), &import_namespace.into())
js_sys::Reflect::set(&imports, &ns.into(), &import_namespace.into())
.expect("Error while setting into the js imports object");
}
imports
}
}
impl Into<js_sys::Object> for ImportObject {
impl Into<js_sys::Object> for Imports {
fn into(self) -> js_sys::Object {
self.as_jsobject()
}
}
impl NamedResolver for ImportObject {
fn resolve_by_name(&self, module: &str, name: &str) -> Option<Export> {
self.get_export(module, name)
impl NamedResolver for Imports {
fn resolve_by_name(&self, ns: &str, name: &str) -> Option<Export> {
self.get_export(ns, name)
}
}
/// Iterator for an `ImportObject`'s exports.
pub struct ImportObjectIterator {
elements: VecDeque<((String, String), Export)>,
}
impl Iterator for ImportObjectIterator {
type Item = ((String, String), Export);
fn next(&mut self) -> Option<Self::Item> {
self.elements.pop_front()
}
}
impl IntoIterator for ImportObject {
type IntoIter = ImportObjectIterator;
type Item = ((String, String), Export);
impl IntoIterator for &Imports {
type IntoIter = std::collections::hash_map::IntoIter<(String, String), Extern>;
type Item = ((String, String), Extern);
fn into_iter(self) -> Self::IntoIter {
ImportObjectIterator {
elements: self.get_objects(),
}
self.map.clone().into_iter()
}
}
impl fmt::Debug for ImportObject {
impl fmt::Debug for Imports {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
enum SecretMap {
Empty,
@@ -194,20 +192,17 @@ impl fmt::Debug for ImportObject {
}
}
f.debug_struct("ImportObject")
.field(
"map",
&SecretMap::new(self.map.lock().unwrap().borrow().len()),
)
f.debug_struct("Imports")
.field("map", &SecretMap::new(self.map.len()))
.finish()
}
}
// The import! macro for ImportObject
// The import! macro for Imports
/// Generate an [`ImportObject`] easily with the `imports!` macro.
/// Generate an [`Imports`] easily with the `imports!` macro.
///
/// [`ImportObject`]: struct.ImportObject.html
/// [`Imports`]: struct.Imports.html
///
/// # Usage
///
@@ -230,12 +225,12 @@ impl fmt::Debug for ImportObject {
macro_rules! imports {
( $( $ns_name:expr => $ns:tt ),* $(,)? ) => {
{
let mut import_object = $crate::ImportObject::new();
let mut import_object = $crate::Imports::new();
$({
let namespace = $crate::import_namespace!($ns);
import_object.register($ns_name, namespace);
import_object.register_namespace($ns_name, namespace);
})*
import_object

View File

@@ -28,7 +28,7 @@ mod error;
mod export;
mod exports;
mod externals;
mod import_object;
mod imports;
mod instance;
mod js_import_object;
mod mem_access;
@@ -56,7 +56,7 @@ pub use crate::js::externals::{
Extern, FromToNativeWasmType, Function, Global, HostFunction, Memory, MemoryError, Table,
WasmTypeList,
};
pub use crate::js::import_object::{ImportObject, ImportObjectIterator, LikeNamespace};
pub use crate::js::imports::Imports;
pub use crate::js::instance::{Instance, InstantiationError};
pub use crate::js::js_import_object::JsImportObject;
pub use crate::js::mem_access::{MemoryAccessError, WasmRef, WasmSlice, WasmSliceIter};

View File

@@ -156,8 +156,8 @@
//! [`imports`] macro:
//!
//! ```
//! # use wasmer::{imports, Function, Memory, MemoryType, Store, ImportObject};
//! # fn imports_example(store: &Store) -> ImportObject {
//! # use wasmer::{imports, Function, Memory, MemoryType, Store, Imports};
//! # fn imports_example(store: &Store) -> Imports {
//! let memory = Memory::new(&store, MemoryType::new(1, None, false)).unwrap();
//! imports! {
//! "env" => {

View File

@@ -1,5 +1,4 @@
use crate::sys::externals::{Extern, Function, Global, Memory, Table};
use crate::sys::import_object::LikeNamespace;
use crate::sys::native::NativeFunc;
use crate::sys::WasmTypeList;
use indexmap::IndexMap;
@@ -276,20 +275,21 @@ impl FromIterator<(String, Extern)> for Exports {
}
}
impl LikeNamespace for Exports {
fn get_namespace_export(&self, name: &str) -> Option<Export> {
self.map.get(name).map(|is_export| is_export.to_export())
}
impl IntoIterator for Exports {
type IntoIter = indexmap::map::IntoIter<String, Extern>;
type Item = (String, Extern);
fn get_namespace_exports(&self) -> Vec<(String, Export)> {
self.map
.iter()
.map(|(k, v)| (k.clone(), v.to_export()))
.collect()
fn into_iter(self) -> Self::IntoIter {
self.map.clone().into_iter()
}
}
fn as_exports(&self) -> Option<Exports> {
Some(self.clone())
impl<'a> IntoIterator for &'a Exports {
type IntoIter = indexmap::map::Iter<'a, String, Extern>;
type Item = (&'a String, &'a Extern);
fn into_iter(self) -> Self::IntoIter {
self.map.iter()
}
}

View File

@@ -1,445 +0,0 @@
//! The import module contains the implementation data structures and helper functions used to
//! manipulate and access a wasm module's imports including memories, tables, globals, and
//! functions.
use crate::Exports;
use crate::Extern;
use std::borrow::{Borrow, BorrowMut};
use std::collections::VecDeque;
use std::collections::{hash_map::Entry, HashMap};
use std::fmt;
use std::sync::{Arc, Mutex};
use wasmer_engine::{Export, NamedResolver};
/// The `LikeNamespace` trait represents objects that act as a namespace for imports.
/// For example, an `Instance` or `Namespace` could be
/// considered namespaces that could provide imports to an instance.
pub trait LikeNamespace {
/// Gets an export by name.
fn get_namespace_export(&self, name: &str) -> Option<Export>;
/// Gets all exports in the namespace.
fn get_namespace_exports(&self) -> Vec<(String, Export)>;
/// Returns the contents of this namespace as an `Exports`.
///
/// This is used by `ImportObject::get_namespace_exports`.
fn as_exports(&self) -> Option<Exports> {
None
}
}
/// All of the import data used when instantiating.
///
/// It's suggested that you use the [`imports!`] macro
/// instead of creating an `ImportObject` by hand.
///
/// [`imports!`]: macro.imports.html
///
/// # Usage:
/// ```ignore
/// use wasmer::{Exports, ImportObject, Function};
///
/// let mut import_object = ImportObject::new();
/// let mut env = Exports::new();
///
/// env.insert("foo", Function::new_native(foo));
/// import_object.register("env", env);
///
/// fn foo(n: i32) -> i32 {
/// n
/// }
/// ```
#[derive(Clone, Default)]
pub struct ImportObject {
map: Arc<Mutex<HashMap<String, Box<dyn LikeNamespace + Send + Sync>>>>,
}
impl ImportObject {
/// Create a new `ImportObject`.
pub fn new() -> Self {
Default::default()
}
/// Gets an export given a module and a name
///
/// # Usage
/// ```ignore
/// # use wasmer_vm::{ImportObject, Instance, Namespace};
/// let mut import_object = ImportObject::new();
/// import_object.get_export("module", "name");
/// ```
pub fn get_export(&self, module: &str, name: &str) -> Option<Export> {
let guard = self.map.lock().unwrap();
let map_ref = guard.borrow();
if map_ref.contains_key(module) {
let namespace = map_ref[module].as_ref();
return namespace.get_namespace_export(name);
}
None
}
/// Returns true if the ImportObject contains namespace with the provided name.
pub fn contains_namespace(&self, name: &str) -> bool {
self.map.lock().unwrap().borrow().contains_key(name)
}
/// Register anything that implements `LikeNamespace` as a namespace.
///
/// # Usage:
/// ```ignore
/// # use wasmer_vm::{ImportObject, Instance, Namespace};
/// let mut import_object = ImportObject::new();
///
/// import_object.register("namespace0", instance);
/// import_object.register("namespace1", namespace);
/// // ...
/// ```
pub fn register<S, N>(&mut self, name: S, namespace: N) -> Option<Box<dyn LikeNamespace>>
where
S: Into<String>,
N: LikeNamespace + Send + Sync + 'static,
{
let mut guard = self.map.lock().unwrap();
let map = guard.borrow_mut();
match map.entry(name.into()) {
Entry::Vacant(empty) => {
empty.insert(Box::new(namespace));
None
}
Entry::Occupied(mut occupied) => Some(occupied.insert(Box::new(namespace))),
}
}
/// Returns the contents of a namespace as an `Exports`.
///
/// Returns `None` if the namespace doesn't exist or doesn't implement the
/// `as_exports` method.
pub fn get_namespace_exports(&self, name: &str) -> Option<Exports> {
let map = self.map.lock().unwrap();
map.get(name).and_then(|ns| ns.as_exports())
}
/// Returns a list of all externs defined in all namespaces.
pub fn externs_vec(&self) -> Vec<(String, String, Extern)> {
let mut out = Vec::new();
let guard = self.map.lock().unwrap();
let map = guard.borrow();
for (namespace, ns) in map.iter() {
match ns.as_exports() {
Some(exports) => {
for (name, extern_) in exports.iter() {
out.push((namespace.clone(), name.clone(), extern_.clone()));
}
}
None => {}
}
}
out
}
fn get_objects(&self) -> VecDeque<((String, String), Export)> {
let mut out = VecDeque::new();
let guard = self.map.lock().unwrap();
let map = guard.borrow();
for (name, ns) in map.iter() {
for (id, exp) in ns.get_namespace_exports() {
out.push_back(((name.clone(), id), exp));
}
}
out
}
}
impl NamedResolver for ImportObject {
fn resolve_by_name(&self, module: &str, name: &str) -> Option<Export> {
self.get_export(module, name)
}
}
/// Iterator for an `ImportObject`'s exports.
pub struct ImportObjectIterator {
elements: VecDeque<((String, String), Export)>,
}
impl Iterator for ImportObjectIterator {
type Item = ((String, String), Export);
fn next(&mut self) -> Option<Self::Item> {
self.elements.pop_front()
}
}
impl IntoIterator for ImportObject {
type IntoIter = ImportObjectIterator;
type Item = ((String, String), Export);
fn into_iter(self) -> Self::IntoIter {
ImportObjectIterator {
elements: self.get_objects(),
}
}
}
impl fmt::Debug for ImportObject {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
enum SecretMap {
Empty,
Some(usize),
}
impl SecretMap {
fn new(len: usize) -> Self {
if len == 0 {
Self::Empty
} else {
Self::Some(len)
}
}
}
impl fmt::Debug for SecretMap {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Empty => write!(f, "(empty)"),
Self::Some(len) => write!(f, "(... {} item(s) ...)", len),
}
}
}
f.debug_struct("ImportObject")
.field(
"map",
&SecretMap::new(self.map.lock().unwrap().borrow().len()),
)
.finish()
}
}
// The import! macro for ImportObject
/// Generate an [`ImportObject`] easily with the `imports!` macro.
///
/// [`ImportObject`]: struct.ImportObject.html
///
/// # Usage
///
/// ```
/// # use wasmer::{Function, Store};
/// # let store = Store::default();
/// use wasmer::imports;
///
/// let import_object = imports! {
/// "env" => {
/// "foo" => Function::new_native(&store, foo)
/// },
/// };
///
/// fn foo(n: i32) -> i32 {
/// n
/// }
/// ```
#[macro_export]
macro_rules! imports {
( $( $ns_name:expr => $ns:tt ),* $(,)? ) => {
{
let mut import_object = $crate::ImportObject::new();
$({
let namespace = $crate::import_namespace!($ns);
import_object.register($ns_name, namespace);
})*
import_object
}
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! namespace {
($( $import_name:expr => $import_item:expr ),* $(,)? ) => {
$crate::import_namespace!( { $( $import_name => $import_item, )* } )
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! import_namespace {
( { $( $import_name:expr => $import_item:expr ),* $(,)? } ) => {{
let mut namespace = $crate::Exports::new();
$(
namespace.insert($import_name, $import_item);
)*
namespace
}};
( $namespace:ident ) => {
$namespace
};
}
#[cfg(test)]
mod test {
use super::*;
use crate::sys::{Global, Store, Val};
use wasmer_engine::ChainableNamedResolver;
use wasmer_types::Type;
#[test]
fn chaining_works() {
let store = Store::default();
let g = Global::new(&store, Val::I32(0));
let imports1 = imports! {
"dog" => {
"happy" => g.clone()
}
};
let imports2 = imports! {
"dog" => {
"small" => g.clone()
},
"cat" => {
"small" => g.clone()
}
};
let resolver = imports1.chain_front(imports2);
let small_cat_export = resolver.resolve_by_name("cat", "small");
assert!(small_cat_export.is_some());
let happy = resolver.resolve_by_name("dog", "happy");
let small = resolver.resolve_by_name("dog", "small");
assert!(happy.is_some());
assert!(small.is_some());
}
#[test]
fn extending_conflict_overwrites() {
let store = Store::default();
let g1 = Global::new(&store, Val::I32(0));
let g2 = Global::new(&store, Val::I64(0));
let imports1 = imports! {
"dog" => {
"happy" => g1,
},
};
let imports2 = imports! {
"dog" => {
"happy" => g2,
},
};
let resolver = imports1.chain_front(imports2);
let happy_dog_entry = resolver.resolve_by_name("dog", "happy").unwrap();
assert!(if let Export::Global(happy_dog_global) = happy_dog_entry {
happy_dog_global.from.ty().ty == Type::I64
} else {
false
});
// now test it in reverse
let store = Store::default();
let g1 = Global::new(&store, Val::I32(0));
let g2 = Global::new(&store, Val::I64(0));
let imports1 = imports! {
"dog" => {
"happy" => g1,
},
};
let imports2 = imports! {
"dog" => {
"happy" => g2,
},
};
let resolver = imports1.chain_back(imports2);
let happy_dog_entry = resolver.resolve_by_name("dog", "happy").unwrap();
assert!(if let Export::Global(happy_dog_global) = happy_dog_entry {
happy_dog_global.from.ty().ty == Type::I32
} else {
false
});
}
#[test]
fn namespace() {
let store = Store::default();
let g1 = Global::new(&store, Val::I32(0));
let namespace = namespace! {
"happy" => g1
};
let imports1 = imports! {
"dog" => namespace
};
let happy_dog_entry = imports1.resolve_by_name("dog", "happy").unwrap();
assert!(if let Export::Global(happy_dog_global) = happy_dog_entry {
happy_dog_global.from.ty().ty == Type::I32
} else {
false
});
}
#[test]
fn imports_macro_allows_trailing_comma_and_none() {
use crate::sys::Function;
let store = Default::default();
fn func(arg: i32) -> i32 {
arg + 1
}
let _ = imports! {
"env" => {
"func" => Function::new_native(&store, func),
},
};
let _ = imports! {
"env" => {
"func" => Function::new_native(&store, func),
}
};
let _ = imports! {
"env" => {
"func" => Function::new_native(&store, func),
},
"abc" => {
"def" => Function::new_native(&store, func),
}
};
let _ = imports! {
"env" => {
"func" => Function::new_native(&store, func)
},
};
let _ = imports! {
"env" => {
"func" => Function::new_native(&store, func)
}
};
let _ = imports! {
"env" => {
"func1" => Function::new_native(&store, func),
"func2" => Function::new_native(&store, func)
}
};
let _ = imports! {
"env" => {
"func1" => Function::new_native(&store, func),
"func2" => Function::new_native(&store, func),
}
};
}
}

436
lib/api/src/sys/imports.rs Normal file
View File

@@ -0,0 +1,436 @@
//! The import module contains the implementation data structures and helper functions used to
//! manipulate and access a wasm module's imports including memories, tables, globals, and
//! functions.
use crate::{Exports, Extern, Module};
use std::collections::HashMap;
use std::fmt;
use wasmer_engine::{ImportError, LinkError};
/// All of the import data used when instantiating.
///
/// It's suggested that you use the [`imports!`] macro
/// instead of creating an `Imports` by hand.
///
/// [`imports!`]: macro.imports.html
///
/// # Usage:
/// ```no_run
/// use wasmer::{Exports, Module, Store, Instance, imports, Imports, Function};
/// # fn foo_test(module: Module, store: Store) {
///
/// let host_fn = Function::new_native(&store, foo);
/// let import_object: Imports = imports! {
/// "env" => {
/// "foo" => host_fn,
/// },
/// };
///
/// let instance = Instance::new(&module, &import_object).expect("Could not instantiate module.");
///
/// fn foo(n: i32) -> i32 {
/// n
/// }
///
/// # }
/// ```
#[derive(Clone, Default)]
pub struct Imports {
map: HashMap<(String, String), Extern>,
}
impl Imports {
/// Create a new `Imports`.
pub fn new() -> Self {
Default::default()
}
/// Gets an export given a module and a name
///
/// # Usage
/// ```no_run
/// # use wasmer::Imports;
/// let mut import_object = Imports::new();
/// import_object.get_export("module", "name");
/// ```
pub fn get_export(&self, module: &str, name: &str) -> Option<Extern> {
if self
.map
.contains_key(&(module.to_string(), name.to_string()))
{
let ext = &self.map[&(module.to_string(), name.to_string())];
return Some(ext.clone());
}
None
}
/// Returns true if the Imports contains namespace with the provided name.
pub fn contains_namespace(&self, name: &str) -> bool {
self.map.keys().any(|(k, _)| (k == name))
}
/// Register a list of externs into a namespace.
///
/// # Usage:
/// ```no_run
/// # use wasmer::{Imports, Exports, Memory};
/// # fn foo_test(memory: Memory) {
/// let mut exports = Exports::new();
/// exports.insert("memory", memory);
///
/// let mut import_object = Imports::new();
/// import_object.register_namespace("env", exports);
/// // ...
/// # }
/// ```
pub fn register_namespace(
&mut self,
ns: &str,
contents: impl IntoIterator<Item = (String, Extern)>,
) {
for (name, extern_) in contents.into_iter() {
self.map.insert((ns.to_string(), name.clone()), extern_);
}
}
/// Add a single import with a namespace `ns` and name `name`.
///
/// # Usage
/// ```no_run
/// # let store = Default::default();
/// use wasmer::{Imports, Function};
/// fn foo(n: i32) -> i32 {
/// n
/// }
/// let mut import_object = Imports::new();
/// import_object.define("env", "foo", Function::new_native(&store, foo));
/// ```
pub fn define(&mut self, ns: &str, name: &str, val: impl Into<Extern>) {
self.map
.insert((ns.to_string(), name.to_string()), val.into());
}
/// Returns the contents of a namespace as an `Exports`.
///
/// Returns `None` if the namespace doesn't exist.
pub fn get_namespace_exports(&self, name: &str) -> Option<Exports> {
let ret: Exports = self
.map
.iter()
.filter(|((ns, _), _)| ns == name)
.map(|((_, name), e)| (name.clone(), e.clone()))
.collect();
if ret.is_empty() {
None
} else {
Some(ret)
}
}
/// Resolve and return a vector of imports in the order they are defined in the `module`'s source code.
///
/// This means the returned `Vec<Extern>` might be a subset of the imports contained in `self`.
pub fn imports_for_module(&self, module: &Module) -> Result<Vec<Extern>, LinkError> {
let mut ret = vec![];
for import in module.imports() {
if let Some(imp) = self
.map
.get(&(import.module().to_string(), import.name().to_string()))
{
ret.push(imp.clone());
} else {
return Err(LinkError::Import(
import.module().to_string(),
import.name().to_string(),
ImportError::UnknownImport(import.ty().clone()),
));
}
}
Ok(ret)
}
}
impl IntoIterator for &Imports {
type IntoIter = std::collections::hash_map::IntoIter<(String, String), Extern>;
type Item = ((String, String), Extern);
fn into_iter(self) -> Self::IntoIter {
self.map.clone().into_iter()
}
}
impl Extend<((String, String), Extern)> for Imports {
fn extend<T: IntoIterator<Item = ((String, String), Extern)>>(&mut self, iter: T) {
for ((ns, name), ext) in iter.into_iter() {
self.define(&ns, &name, ext);
}
}
}
impl fmt::Debug for Imports {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
enum SecretMap {
Empty,
Some(usize),
}
impl SecretMap {
fn new(len: usize) -> Self {
if len == 0 {
Self::Empty
} else {
Self::Some(len)
}
}
}
impl fmt::Debug for SecretMap {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Empty => write!(f, "(empty)"),
Self::Some(len) => write!(f, "(... {} item(s) ...)", len),
}
}
}
f.debug_struct("Imports")
.field("map", &SecretMap::new(self.map.len()))
.finish()
}
}
// The import! macro for Imports
/// Generate an [`Imports`] easily with the `imports!` macro.
///
/// [`Imports`]: struct.Imports.html
///
/// # Usage
///
/// ```
/// # use wasmer::{Function, Store};
/// # let store = Store::default();
/// use wasmer::imports;
///
/// let import_object = imports! {
/// "env" => {
/// "foo" => Function::new_native(&store, foo)
/// },
/// };
///
/// fn foo(n: i32) -> i32 {
/// n
/// }
/// ```
#[macro_export]
macro_rules! imports {
( $( $ns_name:expr => $ns:tt ),* $(,)? ) => {
{
let mut import_object = $crate::Imports::new();
$({
let namespace = $crate::import_namespace!($ns);
import_object.register_namespace($ns_name, namespace);
})*
import_object
}
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! namespace {
($( $import_name:expr => $import_item:expr ),* $(,)? ) => {
$crate::import_namespace!( { $( $import_name => $import_item, )* } )
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! import_namespace {
( { $( $import_name:expr => $import_item:expr ),* $(,)? } ) => {{
let mut namespace = $crate::Exports::new();
$(
namespace.insert($import_name, $import_item);
)*
namespace
}};
( $namespace:ident ) => {
$namespace
};
}
#[cfg(test)]
mod test {
use crate::sys::exports::Exportable;
use crate::sys::Export;
use crate::sys::{Global, Store, Val};
use wasmer_types::Type;
#[test]
fn namespace() {
let store = Store::default();
let g1 = Global::new(&store, Val::I32(0));
let namespace = namespace! {
"happy" => g1
};
let imports1 = imports! {
"dog" => namespace
};
let happy_dog_entry = imports1.get_export("dog", "happy").unwrap();
assert!(
if let Export::Global(happy_dog_global) = happy_dog_entry.to_export() {
happy_dog_global.from.ty().ty == Type::I32
} else {
false
}
);
}
#[test]
fn imports_macro_allows_trailing_comma_and_none() {
use crate::sys::Function;
let store = Default::default();
fn func(arg: i32) -> i32 {
arg + 1
}
let _ = imports! {
"env" => {
"func" => Function::new_native(&store, func),
},
};
let _ = imports! {
"env" => {
"func" => Function::new_native(&store, func),
}
};
let _ = imports! {
"env" => {
"func" => Function::new_native(&store, func),
},
"abc" => {
"def" => Function::new_native(&store, func),
}
};
let _ = imports! {
"env" => {
"func" => Function::new_native(&store, func)
},
};
let _ = imports! {
"env" => {
"func" => Function::new_native(&store, func)
}
};
let _ = imports! {
"env" => {
"func1" => Function::new_native(&store, func),
"func2" => Function::new_native(&store, func)
}
};
let _ = imports! {
"env" => {
"func1" => Function::new_native(&store, func),
"func2" => Function::new_native(&store, func),
}
};
}
#[test]
fn chaining_works() {
let store = Store::default();
let g = Global::new(&store, Val::I32(0));
let mut imports1 = imports! {
"dog" => {
"happy" => g.clone()
}
};
let imports2 = imports! {
"dog" => {
"small" => g.clone()
},
"cat" => {
"small" => g.clone()
}
};
imports1.extend(&imports2);
let small_cat_export = imports1.get_export("cat", "small");
assert!(small_cat_export.is_some());
let happy = imports1.get_export("dog", "happy");
let small = imports1.get_export("dog", "small");
assert!(happy.is_some());
assert!(small.is_some());
}
#[test]
fn extending_conflict_overwrites() {
let store = Store::default();
let g1 = Global::new(&store, Val::I32(0));
let g2 = Global::new(&store, Val::I64(0));
let mut imports1 = imports! {
"dog" => {
"happy" => g1,
},
};
let imports2 = imports! {
"dog" => {
"happy" => g2,
},
};
imports1.extend(&imports2);
let happy_dog_entry = imports1.get_export("dog", "happy").unwrap();
assert!(
if let Export::Global(happy_dog_global) = happy_dog_entry.to_export() {
happy_dog_global.from.ty().ty == Type::I64
} else {
false
}
);
// now test it in reverse
let store = Store::default();
let g1 = Global::new(&store, Val::I32(0));
let g2 = Global::new(&store, Val::I64(0));
let imports1 = imports! {
"dog" => {
"happy" => g1,
},
};
let mut imports2 = imports! {
"dog" => {
"happy" => g2,
},
};
imports2.extend(&imports1);
let happy_dog_entry = imports2.get_export("dog", "happy").unwrap();
assert!(
if let Export::Global(happy_dog_global) = happy_dog_entry.to_export() {
happy_dog_global.from.ty().ty == Type::I32
} else {
false
}
);
}
}

View File

@@ -1,5 +1,6 @@
use crate::sys::exports::Exports;
use crate::sys::externals::Extern;
use crate::sys::imports::Imports;
use crate::sys::module::Module;
use crate::sys::store::Store;
use crate::sys::{HostEnvInitError, LinkError, RuntimeError};
@@ -7,7 +8,6 @@ use loupe::MemoryUsage;
use std::fmt;
use std::sync::{Arc, Mutex};
use thiserror::Error;
use wasmer_engine::Resolver;
use wasmer_vm::{InstanceHandle, VMContext};
/// A WebAssembly Instance is a stateful, executable
@@ -22,6 +22,8 @@ use wasmer_vm::{InstanceHandle, VMContext};
pub struct Instance {
handle: Arc<Mutex<InstanceHandle>>,
module: Module,
#[allow(dead_code)]
imports: Vec<Extern>,
/// The exports for an instance.
pub exports: Exports,
}
@@ -86,15 +88,10 @@ impl From<HostEnvInitError> for InstantiationError {
impl Instance {
/// Creates a new `Instance` from a WebAssembly [`Module`] and a
/// set of imports resolved by the [`Resolver`].
/// set of imports using [`Imports`] or the [`imports`] macro helper.
///
/// The resolver can be anything that implements the [`Resolver`] trait,
/// so you can plug custom resolution for the imports, if you wish not
/// to use [`ImportObject`].
///
/// The [`ImportObject`] is the easiest way to provide imports to the instance.
///
/// [`ImportObject`]: crate::ImportObject
/// [`imports`]: crate::imports
/// [`Imports`]: crate::Imports
///
/// ```
/// # use wasmer::{imports, Store, Module, Global, Value, Instance};
@@ -118,12 +115,12 @@ impl Instance {
/// Those are, as defined by the spec:
/// * Link errors that happen when plugging the imports into the instance
/// * Runtime errors that happen when running the module `start` function.
pub fn new(
module: &Module,
resolver: &(dyn Resolver + Send + Sync),
) -> Result<Self, InstantiationError> {
pub fn new(module: &Module, imports: &Imports) -> Result<Self, InstantiationError> {
let store = module.store();
let handle = module.instantiate(resolver)?;
let imports = imports
.imports_for_module(module)
.map_err(InstantiationError::Link)?;
let handle = module.instantiate(&imports)?;
let exports = module
.exports()
.map(|export| {
@@ -137,6 +134,57 @@ impl Instance {
let instance = Self {
handle: Arc::new(Mutex::new(handle)),
module: module.clone(),
imports,
exports,
};
// # Safety
// `initialize_host_envs` should be called after instantiation but before
// returning an `Instance` to the user. We set up the host environments
// via `WasmerEnv::init_with_instance`.
//
// This usage is correct because we pass a valid pointer to `instance` and the
// correct error type returned by `WasmerEnv::init_with_instance` as a generic
// parameter.
unsafe {
instance
.handle
.lock()
.unwrap()
.initialize_host_envs::<HostEnvInitError>(&instance as *const _ as *const _)?;
}
Ok(instance)
}
/// Creates a new `Instance` from a WebAssembly [`Module`] and a
/// vector of imports.
///
/// ## Errors
///
/// The function can return [`InstantiationError`]s.
///
/// Those are, as defined by the spec:
/// * Link errors that happen when plugging the imports into the instance
/// * Runtime errors that happen when running the module `start` function.
pub fn new_by_index(module: &Module, externs: &[Extern]) -> Result<Self, InstantiationError> {
let store = module.store();
let imports = externs.iter().cloned().collect::<Vec<_>>();
let handle = module.instantiate(&imports)?;
let exports = module
.exports()
.map(|export| {
let name = export.name().to_string();
let export = handle.lookup(&name).expect("export");
let extern_ = Extern::from_vm_export(store, export.into());
(name, extern_)
})
.collect::<Exports>();
let instance = Self {
handle: Arc::new(Mutex::new(handle)),
module: module.clone(),
imports,
exports,
};

View File

@@ -1,7 +1,7 @@
mod env;
mod exports;
mod externals;
mod import_object;
mod imports;
mod instance;
mod mem_access;
mod module;
@@ -31,7 +31,7 @@ pub use crate::sys::exports::{ExportError, Exportable, Exports, ExportsIterator}
pub use crate::sys::externals::{
Extern, FromToNativeWasmType, Function, Global, HostFunction, Memory, Table, WasmTypeList,
};
pub use crate::sys::import_object::{ImportObject, ImportObjectIterator, LikeNamespace};
pub use crate::sys::imports::Imports;
pub use crate::sys::instance::{Instance, InstantiationError};
pub use crate::sys::mem_access::{MemoryAccessError, WasmRef, WasmSlice, WasmSliceIter};
pub use crate::sys::module::Module;
@@ -55,8 +55,7 @@ pub use wasmer_compiler::{
};
pub use wasmer_derive::ValueType;
pub use wasmer_engine::{
ChainableNamedResolver, DeserializeError, Engine, Export, FrameInfo, LinkError, NamedResolver,
NamedResolverChain, Resolver, RuntimeError, SerializeError, Tunables,
DeserializeError, Engine, Export, FrameInfo, LinkError, RuntimeError, SerializeError, Tunables,
};
pub use wasmer_types::is_wasm;
#[cfg(feature = "experimental-reference-types-extern-ref")]

View File

@@ -1,3 +1,4 @@
use crate::sys::exports::Exportable;
use crate::sys::store::Store;
use crate::sys::types::{ExportType, ImportType};
use crate::sys::InstantiationError;
@@ -10,7 +11,7 @@ use thiserror::Error;
use wasmer_compiler::CompileError;
#[cfg(feature = "wat")]
use wasmer_compiler::WasmError;
use wasmer_engine::{Artifact, DeserializeError, Resolver, SerializeError};
use wasmer_engine::{Artifact, DeserializeError, SerializeError};
use wasmer_types::{ExportsIterator, ImportsIterator, ModuleInfo};
use wasmer_vm::InstanceHandle;
@@ -276,12 +277,15 @@ impl Module {
pub(crate) fn instantiate(
&self,
resolver: &dyn Resolver,
imports: &[crate::Extern],
) -> Result<InstanceHandle, InstantiationError> {
unsafe {
let instance_handle = self.artifact.instantiate(
self.store.tunables(),
resolver,
&imports
.iter()
.map(crate::Extern::to_export)
.collect::<Vec<_>>(),
Box::new(self.clone()),
)?;

View File

@@ -19,8 +19,8 @@ mod sys {
",
)?;
let import_object = ImportObject::new();
let instance = Instance::new(&module, &import_object)?;
let imports = Imports::new();
let instance = Instance::new(&module, &imports)?;
let instance2 = instance.clone();
let instance3 = instance.clone();