Merge branch 'master' into fix-sync-js-sys-api

This commit is contained in:
Felix Schütt
2022-08-31 12:57:14 +02:00
committed by GitHub
13 changed files with 314 additions and 286 deletions

View File

@@ -1,11 +1,13 @@
//! 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::js::error::LinkError;
use crate::js::error::{InstantiationError, LinkError, WasmError};
use crate::js::export::Export;
use crate::js::exports::Exports;
use crate::js::module::Module;
use crate::js::store::AsStoreRef;
use crate::js::store::{AsStoreMut, AsStoreRef};
use crate::js::types::AsJs;
use crate::js::ExternType;
use crate::Extern;
use std::collections::HashMap;
use std::fmt;
@@ -179,6 +181,80 @@ impl Imports {
pub fn iter<'a>(&'a self) -> ImportsIterator<'a> {
ImportsIterator::new(self)
}
/// Create a new `Imports` from a JS Object, it receives a reference to a `Module` to
/// map and assign the types of each import and the JS Object
/// that contains the values of imports.
///
/// # Usage
/// ```ignore
/// let import_object = Imports::new_from_js_object(&mut store, &module, js_object);
/// ```
pub fn new_from_js_object(
store: &mut impl AsStoreMut,
module: &Module,
object: js_sys::Object,
) -> Result<Self, WasmError> {
let module_imports: HashMap<(String, String), ExternType> = module
.imports()
.map(|import| {
(
(import.module().to_string(), import.name().to_string()),
import.ty().clone(),
)
})
.collect::<HashMap<(String, String), ExternType>>();
let mut map: HashMap<(String, String), Extern> = HashMap::new();
for module_entry in js_sys::Object::entries(&object).iter() {
let module_entry: js_sys::Array = module_entry.into();
let module_name = module_entry.get(0).as_string().unwrap().to_string();
let module_import_object: js_sys::Object = module_entry.get(1).into();
for import_entry in js_sys::Object::entries(&module_import_object).iter() {
let import_entry: js_sys::Array = import_entry.into();
let import_name = import_entry.get(0).as_string().unwrap().to_string();
let import_js: wasm_bindgen::JsValue = import_entry.get(1);
let key = (module_name.clone(), import_name);
let extern_type = module_imports.get(&key).unwrap();
let export = Export::from_js_value(import_js, store, extern_type.clone())?;
let extern_ = Extern::from_vm_extern(store, export);
map.insert(key, extern_);
}
}
Ok(Self { map })
}
}
impl AsJs for Imports {
fn as_jsvalue(&self, store: &impl AsStoreRef) -> wasm_bindgen::JsValue {
let imports_object = js_sys::Object::new();
for (namespace, name, extern_) in self.iter() {
let val = js_sys::Reflect::get(&imports_object, &namespace.into()).unwrap();
if !val.is_undefined() {
// If the namespace is already set
js_sys::Reflect::set(
&val,
&name.into(),
&extern_.as_jsvalue(&store.as_store_ref()),
)
.unwrap();
} else {
// If the namespace doesn't exist
let import_namespace = js_sys::Object::new();
js_sys::Reflect::set(
&import_namespace,
&name.into(),
&extern_.as_jsvalue(&store.as_store_ref()),
)
.unwrap();
js_sys::Reflect::set(&imports_object, &namespace.into(), &import_namespace.into())
.unwrap();
}
}
imports_object.into()
}
}
pub struct ImportsIterator<'a> {
@@ -317,89 +393,18 @@ macro_rules! import_namespace {
};
}
/*
#[cfg(test)]
mod test {
use crate::js::exports::Exportable;
use crate::js::Type;
use crate::js::{Global, Store, Val};
use crate::js::{Global, Store, Value};
use crate::js::export::Export;
// use wasm_bindgen::*;
use wasm_bindgen_test::*;
fn namespace() {
let mut 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.ty.ty == Type::I32
} else {
false
}
);
}
fn imports_macro_allows_trailing_comma_and_none() {
use crate::js::Function;
let mut store = Default::default();
fn func(arg: i32) -> i32 {
arg + 1
}
let _ = imports! {
"env" => {
"func" => Function::new_typed(&store, func),
},
};
let _ = imports! {
"env" => {
"func" => Function::new_typed(&store, func),
}
};
let _ = imports! {
"env" => {
"func" => Function::new_typed(&store, func),
},
"abc" => {
"def" => Function::new_typed(&store, func),
}
};
let _ = imports! {
"env" => {
"func" => Function::new_typed(&store, func)
},
};
let _ = imports! {
"env" => {
"func" => Function::new_typed(&store, func)
}
};
let _ = imports! {
"env" => {
"func1" => Function::new_typed(&store, func),
"func2" => Function::new_typed(&store, func)
}
};
let _ = imports! {
"env" => {
"func1" => Function::new_typed(&store, func),
"func2" => Function::new_typed(&store, func),
}
};
}
#[wasm_bindgen_test]
fn chaining_works() {
let mut store = Store::default();
let g = Global::new(&store, Val::I32(0));
let g = Global::new(&mut store, Value::I32(0));
let mut imports1 = imports! {
"dog" => {
@@ -412,7 +417,7 @@ mod test {
"small" => g.clone()
},
"cat" => {
"small" => g.clone()
"small" => g
}
};
@@ -426,62 +431,162 @@ mod test {
assert!(happy.is_some());
assert!(small.is_some());
}
// fn namespace() {
// let mut store = Store::default();
// let g1 = Global::new(&store, Val::I32(0));
// let namespace = namespace! {
// "happy" => g1
// };
// let imports1 = imports! {
// "dog" => namespace
// };
fn extending_conflict_overwrites() {
let mut store = Store::default();
let g1 = Global::new(&store, Val::I32(0));
let g2 = Global::new(&store, Val::F32(0.));
// let happy_dog_entry = imports1.get_export("dog", "happy").unwrap();
let mut imports1 = imports! {
"dog" => {
"happy" => g1,
},
};
// assert!(
// if let Export::Global(happy_dog_global) = happy_dog_entry.to_export() {
// happy_dog_global.ty.ty == Type::I32
// } else {
// false
// }
// );
// }
let imports2 = imports! {
"dog" => {
"happy" => g2,
},
};
// fn imports_macro_allows_trailing_comma_and_none() {
// use crate::js::Function;
imports1.extend(&imports2);
let happy_dog_entry = imports1.get_export("dog", "happy").unwrap();
// let mut store = Default::default();
assert!(
if let Export::Global(happy_dog_global) = happy_dog_entry.to_export() {
happy_dog_global.ty.ty == Type::F32
} else {
false
}
);
// fn func(arg: i32) -> i32 {
// arg + 1
// }
// now test it in reverse
let mut store = Store::default();
let g1 = Global::new(&store, Val::I32(0));
let g2 = Global::new(&store, Val::F32(0.));
// let _ = imports! {
// "env" => {
// "func" => Function::new_typed(&store, func),
// },
// };
// let _ = imports! {
// "env" => {
// "func" => Function::new_typed(&store, func),
// }
// };
// let _ = imports! {
// "env" => {
// "func" => Function::new_typed(&store, func),
// },
// "abc" => {
// "def" => Function::new_typed(&store, func),
// }
// };
// let _ = imports! {
// "env" => {
// "func" => Function::new_typed(&store, func)
// },
// };
// let _ = imports! {
// "env" => {
// "func" => Function::new_typed(&store, func)
// }
// };
// let _ = imports! {
// "env" => {
// "func1" => Function::new_typed(&store, func),
// "func2" => Function::new_typed(&store, func)
// }
// };
// let _ = imports! {
// "env" => {
// "func1" => Function::new_typed(&store, func),
// "func2" => Function::new_typed(&store, func),
// }
// };
// }
let imports1 = imports! {
"dog" => {
"happy" => g1,
},
};
// fn chaining_works() {
// let mut store = Store::default();
// let g = Global::new(&store, Val::I32(0));
let mut imports2 = imports! {
"dog" => {
"happy" => g2,
},
};
// let mut imports1 = imports! {
// "dog" => {
// "happy" => g.clone()
// }
// };
imports2.extend(&imports1);
let happy_dog_entry = imports2.get_export("dog", "happy").unwrap();
// let imports2 = imports! {
// "dog" => {
// "small" => g.clone()
// },
// "cat" => {
// "small" => g.clone()
// }
// };
assert!(
if let Export::Global(happy_dog_global) = happy_dog_entry.to_export() {
happy_dog_global.ty.ty == Type::I32
} else {
false
}
);
}
// 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());
// }
// fn extending_conflict_overwrites() {
// let mut store = Store::default();
// let g1 = Global::new(&store, Val::I32(0));
// let g2 = Global::new(&store, Val::F32(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.ty.ty == Type::F32
// } else {
// false
// }
// );
// // now test it in reverse
// let mut store = Store::default();
// let g1 = Global::new(&store, Val::I32(0));
// let g2 = Global::new(&store, Val::F32(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.ty.ty == Type::I32
// } else {
// false
// }
// );
// }
}
*/

View File

@@ -20,8 +20,6 @@ use std::fmt;
pub struct Instance {
_handle: StoreHandle<WebAssembly::Instance>,
module: Module,
#[allow(dead_code)]
imports: Imports,
/// The exports for an instance.
pub exports: Exports,
}
@@ -65,12 +63,11 @@ impl Instance {
module: &Module,
imports: &Imports,
) -> Result<Self, InstantiationError> {
let import_copy = imports.clone();
let (instance, _imports): (StoreHandle<WebAssembly::Instance>, Vec<Extern>) = module
let instance: WebAssembly::Instance = module
.instantiate(&mut store, imports)
.map_err(|e| InstantiationError::Start(e))?;
let self_instance = Self::from_module_and_instance(store, module, instance, import_copy)?;
let self_instance = Self::from_module_and_instance(store, module, instance)?;
//self_instance.init_envs(&imports.iter().map(Extern::to_export).collect::<Vec<_>>())?;
Ok(self_instance)
}
@@ -109,8 +106,7 @@ impl Instance {
pub fn from_module_and_instance(
mut store: &mut impl AsStoreMut,
module: &Module,
instance: StoreHandle<WebAssembly::Instance>,
imports: Imports,
instance: WebAssembly::Instance,
) -> Result<Self, InstantiationError> {
use crate::js::externals::VMExtern;
let instance_exports = instance.get(store.as_store_ref().objects()).exports();
@@ -127,11 +123,10 @@ impl Instance {
Ok((name.to_string(), extern_))
})
.collect::<Result<Exports, InstantiationError>>()?;
let handle = StoreHandle::new(store.as_store_mut().objects_mut(), instance);
Ok(Self {
_handle: instance,
_handle: handle,
module: module.clone(),
imports,
exports,
})
}

View File

@@ -1,83 +0,0 @@
use crate::js::error::WasmError;
use crate::js::store::AsStoreMut;
use crate::js::{Export, ExternType, Module};
use std::collections::HashMap;
/// This struct is used in case you want to create an `Instance`
/// of a `Module` with imports that are provided directly from
/// Javascript with a JS Object.
#[derive(Clone, Default)]
pub struct JsImportObject {
module_imports: HashMap<(String, String), ExternType>,
object: js_sys::Object,
}
/// JS Objects with wasm-bindgen are not currently Send/Sync (although they
/// are in Javascript, since we can use them safely between webworkers).
unsafe impl Send for JsImportObject {}
unsafe impl Sync for JsImportObject {}
impl JsImportObject {
/// Create a new `JsImportObject`, it receives a reference to a `Module` to
/// map and assign the types of each import and the JS Object
/// that contains the values of imports.
///
/// # Usage
/// ```ignore
/// # use wasmer::JsImportObject;
/// let import_object = JsImportObject::new(&module, js_object);
/// ```
pub fn new(module: &Module, object: js_sys::Object) -> Self {
let module_imports = module
.imports()
.map(|import| {
(
(import.module().to_string(), import.name().to_string()),
import.ty().clone(),
)
})
.collect::<HashMap<(String, String), ExternType>>();
Self {
module_imports,
object,
}
}
/// Gets an export given a module and a name
///
/// # Usage
/// ```ignore
/// # use wasmer::JsImportObject;
/// let import_object = JsImportObject::new(&module, js_object);
/// import_object.get_export("module", "name");
/// ```
pub fn get_export(
&self,
store: &mut impl AsStoreMut,
module: &str,
name: &str,
) -> Result<Export, WasmError> {
let namespace = js_sys::Reflect::get(&self.object, &module.into())?;
let js_export = js_sys::Reflect::get(&namespace, &name.into())?;
match self
.module_imports
.get(&(module.to_string(), name.to_string()))
{
Some(extern_type) => Ok(Export::from_js_value(
js_export,
store,
extern_type.clone(),
)?),
None => Err(WasmError::Generic(format!(
"Name {} not found in module {}",
name, module
))),
}
}
}
impl Into<js_sys::Object> for JsImportObject {
fn into(self) -> js_sys::Object {
self.object
}
}

View File

@@ -30,7 +30,6 @@ mod externals;
mod function_env;
mod imports;
mod instance;
mod js_import_object;
mod mem_access;
mod module;
#[cfg(feature = "wasm-types-polyfill")]
@@ -54,7 +53,6 @@ pub use crate::js::externals::{
pub use crate::js::function_env::{FunctionEnv, FunctionEnvMut};
pub use crate::js::imports::Imports;
pub use crate::js::instance::Instance;
pub use crate::js::js_import_object::JsImportObject;
pub use crate::js::mem_access::{MemoryAccessError, WasmRef, WasmSlice, WasmSliceIter};
pub use crate::js::module::{Module, ModuleTypeHints};
pub use crate::js::native::TypedFunction;

View File

@@ -3,9 +3,8 @@ use crate::js::error::WasmError;
use crate::js::error::{CompileError, InstantiationError};
#[cfg(feature = "js-serializable-module")]
use crate::js::error::{DeserializeError, SerializeError};
use crate::js::externals::Extern;
use crate::js::imports::Imports;
use crate::js::store::{AsStoreMut, StoreHandle};
use crate::js::store::AsStoreMut;
use crate::js::types::{AsJs, ExportType, ImportType};
use crate::js::RuntimeError;
use crate::AsStoreRef;
@@ -222,7 +221,7 @@ impl Module {
&self,
store: &mut impl AsStoreMut,
imports: &Imports,
) -> Result<(StoreHandle<WebAssembly::Instance>, Vec<Extern>), RuntimeError> {
) -> Result<WebAssembly::Instance, RuntimeError> {
// Ensure all imports come from the same store.
if imports
.into_iter()
@@ -232,46 +231,10 @@ impl Module {
InstantiationError::DifferentStores,
)));
}
let imports_object = js_sys::Object::new();
let mut import_externs: Vec<Extern> = vec![];
for import_type in self.imports() {
let resolved_import = imports.get_export(import_type.module(), import_type.name());
if let Some(import) = resolved_import {
let val = js_sys::Reflect::get(&imports_object, &import_type.module().into())?;
if !val.is_undefined() {
// If the namespace is already set
js_sys::Reflect::set(
&val,
&import_type.name().into(),
&import.as_jsvalue(&store.as_store_ref()),
)?;
} else {
// If the namespace doesn't exist
let import_namespace = js_sys::Object::new();
js_sys::Reflect::set(
&import_namespace,
&import_type.name().into(),
&import.as_jsvalue(&store.as_store_ref()),
)?;
js_sys::Reflect::set(
&imports_object,
&import_type.module().into(),
&import_namespace.into(),
)?;
}
import_externs.push(import);
}
// in case the import is not found, the JS Wasm VM will handle
// the error for us, so we don't need to handle it
}
Ok((
StoreHandle::new(
store.as_store_mut().objects_mut(),
WebAssembly::Instance::new(&self.module, &imports_object)
.map_err(|e: JsValue| -> RuntimeError { e.into() })?,
),
import_externs,
))
let imports_js_obj = imports.as_jsvalue(store).into();
Ok(WebAssembly::Instance::new(&self.module, &imports_js_obj)
.map_err(|e: JsValue| -> RuntimeError { e.into() })?)
}
/// Returns the name of the current module.