Improved JS API a bit further

This commit is contained in:
Syrus Akbary
2022-08-29 19:31:34 +02:00
parent b122feeb83
commit d023c1e7d1
3 changed files with 197 additions and 180 deletions

View File

@@ -227,6 +227,36 @@ impl Imports {
} }
} }
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> { pub struct ImportsIterator<'a> {
iter: std::collections::hash_map::Iter<'a, (String, String), Extern>, iter: std::collections::hash_map::Iter<'a, (String, String), Extern>,
} }
@@ -363,89 +393,18 @@ macro_rules! import_namespace {
}; };
} }
/* #[cfg(test)]
mod test { mod test {
use crate::js::exports::Exportable; use crate::js::{Global, Store, Value};
use crate::js::Type;
use crate::js::{Global, Store, Val};
use crate::js::export::Export; // use wasm_bindgen::*;
use wasm_bindgen_test::*; 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() { fn chaining_works() {
let mut store = Store::default(); 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! { let mut imports1 = imports! {
"dog" => { "dog" => {
@@ -458,7 +417,7 @@ mod test {
"small" => g.clone() "small" => g.clone()
}, },
"cat" => { "cat" => {
"small" => g.clone() "small" => g
} }
}; };
@@ -472,62 +431,162 @@ mod test {
assert!(happy.is_some()); assert!(happy.is_some());
assert!(small.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 happy_dog_entry = imports1.get_export("dog", "happy").unwrap();
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! { // assert!(
"dog" => { // if let Export::Global(happy_dog_global) = happy_dog_entry.to_export() {
"happy" => g1, // happy_dog_global.ty.ty == Type::I32
}, // } else {
}; // false
// }
// );
// }
let imports2 = imports! { // fn imports_macro_allows_trailing_comma_and_none() {
"dog" => { // use crate::js::Function;
"happy" => g2,
},
};
imports1.extend(&imports2); // let mut store = Default::default();
let happy_dog_entry = imports1.get_export("dog", "happy").unwrap();
assert!( // fn func(arg: i32) -> i32 {
if let Export::Global(happy_dog_global) = happy_dog_entry.to_export() { // arg + 1
happy_dog_global.ty.ty == Type::F32 // }
} else {
false
}
);
// now test it in reverse // let _ = imports! {
let mut store = Store::default(); // "env" => {
let g1 = Global::new(&store, Val::I32(0)); // "func" => Function::new_typed(&store, func),
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),
// },
// "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! { // fn chaining_works() {
"dog" => { // let mut store = Store::default();
"happy" => g1, // let g = Global::new(&store, Val::I32(0));
},
};
let mut imports2 = imports! { // let mut imports1 = imports! {
"dog" => { // "dog" => {
"happy" => g2, // "happy" => g.clone()
}, // }
}; // };
imports2.extend(&imports1); // let imports2 = imports! {
let happy_dog_entry = imports2.get_export("dog", "happy").unwrap(); // "dog" => {
// "small" => g.clone()
// },
// "cat" => {
// "small" => g.clone()
// }
// };
assert!( // imports1.extend(&imports2);
if let Export::Global(happy_dog_global) = happy_dog_entry.to_export() {
happy_dog_global.ty.ty == Type::I32 // let small_cat_export = imports1.get_export("cat", "small");
} else { // assert!(small_cat_export.is_some());
false
} // 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 { pub struct Instance {
_handle: StoreHandle<WebAssembly::Instance>, _handle: StoreHandle<WebAssembly::Instance>,
module: Module, module: Module,
#[allow(dead_code)]
imports: Imports,
/// The exports for an instance. /// The exports for an instance.
pub exports: Exports, pub exports: Exports,
} }
@@ -65,12 +63,11 @@ impl Instance {
module: &Module, module: &Module,
imports: &Imports, imports: &Imports,
) -> Result<Self, InstantiationError> { ) -> Result<Self, InstantiationError> {
let import_copy = imports.clone(); let instance: WebAssembly::Instance = module
let (instance, _imports): (StoreHandle<WebAssembly::Instance>, Vec<Extern>) = module
.instantiate(&mut store, imports) .instantiate(&mut store, imports)
.map_err(|e| InstantiationError::Start(e))?; .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<_>>())?; //self_instance.init_envs(&imports.iter().map(Extern::to_export).collect::<Vec<_>>())?;
Ok(self_instance) Ok(self_instance)
} }
@@ -87,10 +84,9 @@ impl Instance {
pub fn from_module_and_instance( pub fn from_module_and_instance(
mut store: &mut impl AsStoreMut, mut store: &mut impl AsStoreMut,
module: &Module, module: &Module,
instance: StoreHandle<WebAssembly::Instance>, instance: WebAssembly::Instance,
imports: Imports,
) -> Result<Self, InstantiationError> { ) -> Result<Self, InstantiationError> {
let instance_exports = instance.get(store.as_store_ref().objects()).exports(); let instance_exports = instance.exports();
let exports = module let exports = module
.exports() .exports()
.map(|export_type| { .map(|export_type| {
@@ -109,11 +105,10 @@ impl Instance {
Ok((name.to_string(), extern_)) Ok((name.to_string(), extern_))
}) })
.collect::<Result<Exports, InstantiationError>>()?; .collect::<Result<Exports, InstantiationError>>()?;
let handle = StoreHandle::new(store.as_store_mut().objects_mut(), instance);
Ok(Self { Ok(Self {
_handle: instance, _handle: handle,
module: module.clone(), module: module.clone(),
imports,
exports, exports,
}) })
} }

View File

@@ -3,9 +3,8 @@ use crate::js::error::WasmError;
use crate::js::error::{CompileError, InstantiationError}; use crate::js::error::{CompileError, InstantiationError};
#[cfg(feature = "js-serializable-module")] #[cfg(feature = "js-serializable-module")]
use crate::js::error::{DeserializeError, SerializeError}; use crate::js::error::{DeserializeError, SerializeError};
use crate::js::externals::Extern;
use crate::js::imports::Imports; 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::types::{AsJs, ExportType, ImportType};
use crate::js::RuntimeError; use crate::js::RuntimeError;
use crate::AsStoreRef; use crate::AsStoreRef;
@@ -222,7 +221,7 @@ impl Module {
&self, &self,
store: &mut impl AsStoreMut, store: &mut impl AsStoreMut,
imports: &Imports, imports: &Imports,
) -> Result<(StoreHandle<WebAssembly::Instance>, Vec<Extern>), RuntimeError> { ) -> Result<WebAssembly::Instance, RuntimeError> {
// Ensure all imports come from the same store. // Ensure all imports come from the same store.
if imports if imports
.into_iter() .into_iter()
@@ -232,46 +231,10 @@ impl Module {
InstantiationError::DifferentStores, InstantiationError::DifferentStores,
))); )));
} }
let imports_object = js_sys::Object::new();
let mut import_externs: Vec<Extern> = vec![]; let imports_js_obj = imports.as_jsvalue(store).into();
for import_type in self.imports() { Ok(WebAssembly::Instance::new(&self.module, &imports_js_obj)
let resolved_import = imports.get_export(import_type.module(), import_type.name()); .map_err(|e: JsValue| -> RuntimeError { e.into() })?)
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,
))
} }
/// Returns the name of the current module. /// Returns the name of the current module.