Merge branch 'master' into capi-wasi-overwrite-stdin-3

This commit is contained in:
Felix Schütt
2022-09-02 17:20:29 +02:00
committed by GitHub
39 changed files with 846 additions and 402 deletions

View File

@@ -14,6 +14,7 @@ Looking for changes that affect our C API? See the [C API Changelog](lib/c-api/C
### Changed
- #[3131](https://github.com/wasmerio/wasmer/pull/3131) Update migration docs for MemoryView changes
- #[3129](https://github.com/wasmerio/wasmer/pull/3129) Fix differences between -sys and -js API
### Fixed
- #[3130](https://github.com/wasmerio/wasmer/pull/3130) Remove panics from Artifact::deserialize

9
Cargo.lock generated
View File

@@ -2923,6 +2923,7 @@ name = "wasmer"
version = "3.0.0-beta"
dependencies = [
"anyhow",
"bytes",
"cfg-if 1.0.0",
"hashbrown 0.11.2",
"indexmap",
@@ -3220,11 +3221,7 @@ dependencies = [
name = "wasmer-vbus"
version = "3.0.0-beta"
dependencies = [
"libc",
"slab",
"thiserror",
"tracing",
"typetag",
"wasmer-vfs",
]
@@ -3268,11 +3265,7 @@ name = "wasmer-vnet"
version = "3.0.0-beta"
dependencies = [
"bytes",
"libc",
"slab",
"thiserror",
"tracing",
"typetag",
"wasmer-vfs",
]

View File

@@ -195,14 +195,13 @@ import_object.define("env", "host_function", host_function);
let instance = Instance::new(&mut store, &module, &import_object).expect("Could not instantiate module.");
```
For WASI, don't forget to import memory to `WasiEnv`
For WASI, don't forget to initialize the `WasiEnv` (it will import the memory)
```rust
let mut wasi_env = WasiState::new("hello").finalize()?;
let import_object = wasi_env.import_object(&mut store, &module)?;
let instance = Instance::new(&mut store, &module, &import_object).expect("Could not instantiate module.");
let memory = instance.exports.get_memory("memory")?;
wasi_env.data_mut(&mut store).set_memory(memory.clone());
wasi_env.initialize(&mut store, &instance).unwrap();
```
#### `ChainableNamedResolver` is removed

View File

@@ -26,6 +26,7 @@ indexmap = { version = "1.6" }
cfg-if = "1.0"
thiserror = "1.0"
more-asserts = "0.2"
bytes = "1"
# - Optional shared dependencies.
wat = { version = "1.0", optional = true }
tracing = { version = "0.1", optional = true }

View File

@@ -4,6 +4,7 @@ use crate::js::trap::RuntimeError;
use std::borrow::Cow;
#[cfg(feature = "std")]
use thiserror::Error;
use wasmer_types::ImportError;
// Compilation Errors
//
@@ -144,6 +145,28 @@ pub enum DeserializeError {
Compiler(CompileError),
}
/// The WebAssembly.LinkError object indicates an error during
/// module instantiation (besides traps from the start function).
///
/// This is based on the [link error][link-error] API.
///
/// [link-error]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/LinkError
#[derive(Error, Debug)]
#[error("Link error: {0}")]
pub enum LinkError {
/// An error occurred when checking the import types.
#[error("Error while importing {0:?}.{1:?}: {2}")]
Import(String, String, ImportError),
#[cfg(not(target_arch = "wasm32"))]
/// A trap ocurred during linking.
#[error("RuntimeError occurred during linking: {0}")]
Trap(#[source] RuntimeError),
/// Insufficient resources available for linking.
#[error("Insufficient resources: {0}")]
Resource(String),
}
/// An error while instantiating a module.
///
/// This is not a common WebAssembly error, however
@@ -156,13 +179,18 @@ pub enum DeserializeError {
#[cfg_attr(feature = "std", derive(Error))]
pub enum InstantiationError {
/// A linking ocurred during instantiation.
#[cfg_attr(feature = "std", error("Link error: {0}"))]
Link(String),
#[cfg_attr(feature = "std", error(transparent))]
Link(LinkError),
/// A runtime error occured while invoking the start function
#[cfg_attr(feature = "std", error(transparent))]
Start(RuntimeError),
/// The module was compiled with a CPU feature that is not available on
/// the current host.
#[cfg_attr(feature = "std", error("missing required CPU features: {0:?}"))]
CpuFeature(String),
/// Import from a different [`Store`].
/// This error occurs when an import from a different store is used.
#[cfg_attr(feature = "std", error("cannot mix imports from different stores"))]
@@ -171,6 +199,10 @@ pub enum InstantiationError {
/// A generic error occured while invoking API functions
#[cfg_attr(feature = "std", error(transparent))]
Wasm(WasmError),
/// Insufficient resources available for execution.
#[cfg_attr(feature = "std", error("Can't get {0} from the instance exports"))]
NotInExports(String),
}
impl From<WasmError> for InstantiationError {

View File

@@ -1,6 +1,6 @@
pub use self::inner::{FromToNativeWasmType, HostFunction, WasmTypeList, WithEnv, WithoutEnv};
use crate::js::exports::{ExportError, Exportable};
use crate::js::externals::Extern;
use crate::js::externals::{Extern, VMExtern};
use crate::js::function_env::FunctionEnvMut;
use crate::js::store::{AsStoreMut, AsStoreRef, InternalStoreHandle, StoreHandle, StoreMut};
use crate::js::types::{param_from_js, AsJs}; /* ValFuncRef */
@@ -26,6 +26,7 @@ fn result_to_js(val: &Value) -> JsValue {
Value::I64(i) => JsValue::from_f64(*i as _),
Value::F32(f) => JsValue::from_f64(*f as _),
Value::F64(f) => JsValue::from_f64(*f),
Value::V128(f) => JsValue::from_f64(*f as _),
val => unimplemented!(
"The value `{:?}` is not yet supported in the JS Function API",
val
@@ -77,6 +78,11 @@ impl Function {
Self::new_with_env(store, &env, ty, wrapped_func)
}
/// To `VMExtern`.
pub fn to_vm_extern(&self) -> VMExtern {
VMExtern::Function(self.handle.internal_handle())
}
/// Creates a new host `Function` (dynamic) with the provided signature.
///
/// If you know the signature of the host function at compile time,
@@ -229,13 +235,13 @@ impl Function {
note = "new_native_with_env() has been renamed to new_typed_with_env()."
)]
/// Creates a new host `Function` with an environment from a typed function.
pub fn new_native_with_env<T, F, Args, Rets>(
pub fn new_native_with_env<T: Send + 'static, F, Args, Rets>(
store: &mut impl AsStoreMut,
env: &FunctionEnv<T>,
func: F,
) -> Self
where
F: HostFunction<T, Args, Rets, WithEnv>,
F: HostFunction<T, Args, Rets, WithEnv> + 'static + Send + Sync,
Args: WasmTypeList,
Rets: WasmTypeList,
{
@@ -309,8 +315,8 @@ impl Function {
/// assert_eq!(f.ty().params(), vec![Type::I32, Type::I32]);
/// assert_eq!(f.ty().results(), vec![Type::I32]);
/// ```
pub fn ty<'context>(&self, store: &'context impl AsStoreRef) -> &'context FunctionType {
&self.handle.get(store.as_store_ref().objects()).ty
pub fn ty(&self, store: &impl AsStoreRef) -> FunctionType {
self.handle.get(store.as_store_ref().objects()).ty.clone()
}
/// Returns the number of parameters that this function takes.
@@ -598,7 +604,7 @@ mod inner {
use super::RuntimeError;
use super::VMFunctionBody;
use crate::js::function_env::{FunctionEnvMut, VMFunctionEnvironment};
use crate::js::store::{AsStoreMut, InternalStoreHandle, StoreHandle, StoreMut};
use crate::js::store::{AsStoreMut, AsStoreRef, InternalStoreHandle, StoreHandle, StoreMut};
use crate::js::FunctionEnv;
use crate::js::NativeWasmTypeInto;
use std::array::TryFromSliceError;
@@ -639,6 +645,9 @@ mod inner {
/// This method panics if `self` cannot fit in the
/// `Self::Native` type.
fn to_native(self) -> Self::Native;
/// Returns whether this native type belongs to the given store
fn is_from_store(&self, _store: &impl AsStoreRef) -> bool;
}
macro_rules! from_to_native_wasm_type {
@@ -657,6 +666,11 @@ mod inner {
fn to_native(self) -> Self::Native {
self as Self::Native
}
#[inline]
fn is_from_store(&self, _store: &impl AsStoreRef) -> bool {
true // Javascript only has one store
}
}
)*
};
@@ -678,6 +692,11 @@ mod inner {
fn to_native(self) -> Self::Native {
Self::Native::from_ne_bytes(Self::to_ne_bytes(self))
}
#[inline]
fn is_from_store(&self, _store: &impl AsStoreRef) -> bool {
true // Javascript only has one store
}
}
)*
};
@@ -887,11 +906,12 @@ mod inner {
Args: WasmTypeList,
Rets: WasmTypeList,
Kind: HostFunctionKind,
T: Sized,
Self: Sized,
{
/// Get the pointer to the function body.
fn function_body_ptr(self) -> *const VMFunctionBody;
fn function_body_ptr(&self) -> *const VMFunctionBody;
// /// Get the pointer to the function call trampoline.
// fn call_trampoline_address() -> VMTrampoline;
}
/// Empty trait to specify the kind of `HostFunction`: With or
@@ -1102,7 +1122,7 @@ mod inner {
Func: Fn(FunctionEnvMut<'_, T>, $( $x , )*) -> RetsAsResult + 'static,
{
#[allow(non_snake_case)]
fn function_body_ptr(self) -> *const VMFunctionBody {
fn function_body_ptr(&self) -> *const VMFunctionBody {
/// This is a function that wraps the real host
/// function. Its address will be used inside the
/// runtime.
@@ -1150,7 +1170,7 @@ mod inner {
Func: Fn($( $x , )*) -> RetsAsResult + 'static,
{
#[allow(non_snake_case)]
fn function_body_ptr(self) -> *const VMFunctionBody {
fn function_body_ptr(&self) -> *const VMFunctionBody {
/// This is a function that wraps the real host
/// function. Its address will be used inside the
/// runtime.

View File

@@ -1,6 +1,6 @@
use crate::js::export::VMGlobal;
use crate::js::exports::{ExportError, Exportable};
use crate::js::externals::Extern;
use crate::js::externals::{Extern, VMExtern};
use crate::js::store::{AsStoreMut, AsStoreRef, InternalStoreHandle, StoreHandle};
use crate::js::value::Value;
use crate::js::wasm_bindgen_polyfill::Global as JSGlobal;
@@ -55,6 +55,11 @@ impl Global {
Self::from_value(store, val, Mutability::Var).unwrap()
}
/// To `VMExtern`.
pub(crate) fn to_vm_extern(&self) -> VMExtern {
VMExtern::Global(self.handle.internal_handle())
}
/// Create a `Global` with the initial value [`Value`] and the provided [`Mutability`].
fn from_value(
store: &mut impl AsStoreMut,
@@ -135,15 +140,6 @@ impl Global {
let ty = self.handle.get(store.as_store_ref().objects()).ty;
Value::from_raw(store, ty.ty, raw)
}
/*
match self.vm_global.ty.ty {
ValType::I32 => Value::I32(self.vm_global.global.value().as_f64().unwrap() as _),
ValType::I64 => Value::I64(self.vm_global.global.value().as_f64().unwrap() as _),
ValType::F32 => Value::F32(self.vm_global.global.value().as_f64().unwrap() as _),
ValType::F64 => Value::F64(self.vm_global.global.value().as_f64().unwrap()),
_ => unimplemented!("The type is not yet supported in the JS Global API"),
}
*/
}
/// Sets a custom value [`Value`] to the runtime Global.

View File

@@ -1,6 +1,6 @@
use crate::js::export::VMMemory;
use crate::js::exports::{ExportError, Exportable};
use crate::js::externals::Extern;
use crate::js::externals::{Extern, VMExtern};
use crate::js::store::{AsStoreMut, AsStoreRef, InternalStoreHandle, StoreHandle, StoreObjects};
use crate::js::{MemoryAccessError, MemoryType};
use std::marker::PhantomData;
@@ -118,6 +118,11 @@ impl Memory {
Self::from_vm_extern(new_store, handle.internal_handle())
}
/// To `VMExtern`.
pub(crate) fn to_vm_extern(&self) -> VMExtern {
VMExtern::Memory(self.handle.internal_handle())
}
/// Returns the [`MemoryType`] of the `Memory`.
///
/// # Example
@@ -218,6 +223,12 @@ impl Memory {
}
}
impl std::cmp::PartialEq for Memory {
fn eq(&self, other: &Self) -> bool {
self.handle == other.handle
}
}
impl<'a> Exportable<'a> for Memory {
fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> {
match _extern {

View File

@@ -10,13 +10,122 @@ pub use self::memory::{Memory, MemoryError};
pub use self::memory_view::MemoryView;
pub use self::table::Table;
use crate::js::export::Export;
use crate::js::export::{Export, VMFunction, VMGlobal, VMMemory, VMTable};
use crate::js::exports::{ExportError, Exportable};
use crate::js::store::StoreObject;
use crate::js::store::{AsStoreMut, AsStoreRef};
use crate::js::types::AsJs;
/*
use crate::js::store::InternalStoreHandle;
use crate::js::store::{AsStoreMut, AsStoreRef};
use crate::js::ExternType;
use std::fmt;
*/
use crate::js::error::WasmError;
use crate::js::store::{AsStoreMut, AsStoreRef, InternalStoreHandle};
use crate::js::wasm_bindgen_polyfill::Global as JsGlobal;
use js_sys::Function as JsFunction;
use js_sys::WebAssembly::{Memory as JsMemory, Table as JsTable};
use std::fmt;
use wasm_bindgen::{JsCast, JsValue};
use wasmer_types::{ExternType, FunctionType, GlobalType, MemoryType, TableType};
/// The value of an export passed from one instance to another.
pub enum VMExtern {
/// A function export value.
Function(InternalStoreHandle<VMFunction>),
/// A table export value.
Table(InternalStoreHandle<VMTable>),
/// A memory export value.
Memory(InternalStoreHandle<VMMemory>),
/// A global export value.
Global(InternalStoreHandle<VMGlobal>),
}
impl VMExtern {
/// Return the export as a `JSValue`.
pub fn as_jsvalue<'context>(&self, store: &'context impl AsStoreRef) -> &'context JsValue {
match self {
Self::Memory(js_wasm_memory) => js_wasm_memory
.get(store.as_store_ref().objects())
.memory
.as_ref(),
Self::Function(js_func) => js_func
.get(store.as_store_ref().objects())
.function
.as_ref(),
Self::Table(js_wasm_table) => js_wasm_table
.get(store.as_store_ref().objects())
.table
.as_ref(),
Self::Global(js_wasm_global) => js_wasm_global
.get(store.as_store_ref().objects())
.global
.as_ref(),
}
}
/// Convert a `JsValue` into an `Export` within a given `Context`.
pub fn from_js_value(
val: JsValue,
store: &mut impl AsStoreMut,
extern_type: ExternType,
) -> Result<Self, WasmError> {
match extern_type {
ExternType::Memory(memory_type) => {
if val.is_instance_of::<JsMemory>() {
Ok(Self::Memory(InternalStoreHandle::new(
&mut store.objects_mut(),
VMMemory::new(val.unchecked_into::<JsMemory>(), memory_type),
)))
} else {
Err(WasmError::TypeMismatch(
val.js_typeof()
.as_string()
.map(Into::into)
.unwrap_or("unknown".into()),
"Memory".into(),
))
}
}
ExternType::Global(global_type) => {
if val.is_instance_of::<JsGlobal>() {
Ok(Self::Global(InternalStoreHandle::new(
&mut store.objects_mut(),
VMGlobal::new(val.unchecked_into::<JsGlobal>(), global_type),
)))
} else {
panic!("Extern type doesn't match js value type");
}
}
ExternType::Function(function_type) => {
if val.is_instance_of::<JsFunction>() {
Ok(Self::Function(InternalStoreHandle::new(
&mut store.objects_mut(),
VMFunction::new(val.unchecked_into::<JsFunction>(), function_type),
)))
} else {
panic!("Extern type doesn't match js value type");
}
}
ExternType::Table(table_type) => {
if val.is_instance_of::<JsTable>() {
Ok(Self::Table(InternalStoreHandle::new(
&mut store.objects_mut(),
VMTable::new(val.unchecked_into::<JsTable>(), table_type),
)))
} else {
panic!("Extern type doesn't match js value type");
}
}
}
}
}
/// An `Extern` is the runtime representation of an entity that
/// can be imported or exported.
@@ -45,13 +154,23 @@ impl Extern {
}
}
/// Create an `Extern` from an `wasmer_compiler::Export`.
pub fn from_vm_extern(store: &mut impl AsStoreMut, export: Export) -> Self {
match export {
Export::Function(f) => Self::Function(Function::from_vm_extern(store, f)),
Export::Memory(m) => Self::Memory(Memory::from_vm_extern(store, m)),
Export::Global(g) => Self::Global(Global::from_vm_extern(store, g)),
Export::Table(t) => Self::Table(Table::from_vm_extern(store, t)),
/// Create an `Extern` from an `wasmer_engine::Export`.
pub fn from_vm_extern(store: &mut impl AsStoreMut, vm_extern: VMExtern) -> Self {
match vm_extern {
VMExtern::Function(f) => Self::Function(Function::from_vm_extern(store, f)),
VMExtern::Memory(m) => Self::Memory(Memory::from_vm_extern(store, m)),
VMExtern::Global(g) => Self::Global(Global::from_vm_extern(store, g)),
VMExtern::Table(t) => Self::Table(Table::from_vm_extern(store, t)),
}
}
/// To `VMExtern`.
pub fn to_vm_extern(&self) -> VMExtern {
match self {
Self::Function(f) => f.to_vm_extern(),
Self::Global(g) => g.to_vm_extern(),
Self::Memory(m) => m.to_vm_extern(),
Self::Table(t) => t.to_vm_extern(),
}
}

View File

@@ -1,6 +1,6 @@
use crate::js::export::{VMFunction, VMTable};
use crate::js::exports::{ExportError, Exportable};
use crate::js::externals::Extern;
use crate::js::externals::{Extern, VMExtern};
use crate::js::store::{AsStoreMut, AsStoreRef, InternalStoreHandle, StoreHandle};
use crate::js::value::Value;
use crate::js::RuntimeError;
@@ -75,6 +75,11 @@ impl Table {
})
}
/// To `VMExtern`.
pub fn to_vm_extern(&self) -> VMExtern {
VMExtern::Table(self.handle.internal_handle())
}
/// Returns the [`TableType`] of the `Table`.
pub fn ty(&self, store: &impl AsStoreRef) -> TableType {
self.handle.get(store.as_store_ref().objects()).ty
@@ -143,6 +148,7 @@ impl Table {
/// Returns an error if the range is out of bounds of either the source or
/// destination tables.
pub fn copy(
_store: &mut impl AsStoreMut,
_dst_table: &Self,
_dst_index: u32,
_src_table: &Self,

View File

@@ -1,14 +1,17 @@
//! 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::InstantiationError;
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;
use wasmer_types::ImportError;
/// All of the import data used when instantiating.
///
@@ -130,7 +133,7 @@ impl Imports {
/// 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>, InstantiationError> {
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
@@ -139,12 +142,11 @@ impl Imports {
{
ret.push(imp.clone());
} else {
return Err(InstantiationError::Link(format!(
"Error while importing {0:?}.{1:?}: unknown import. Expected {2:?}",
import.module(),
import.name(),
import.ty()
)));
return Err(LinkError::Import(
import.module().to_string(),
import.name().to_string(),
ImportError::UnknownImport(import.ty().clone()),
));
}
}
Ok(ret)
@@ -179,6 +181,81 @@ 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> {
use crate::js::externals::VMExtern;
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 = VMExtern::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 +394,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 +418,7 @@ mod test {
"small" => g.clone()
},
"cat" => {
"small" => g.clone()
"small" => g
}
};
@@ -426,62 +432,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,16 +63,37 @@ 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)
}
/// 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(
store: &mut impl AsStoreMut,
module: &Module,
externs: &[Extern],
) -> Result<Self, InstantiationError> {
let mut imports = Imports::new();
for (import_ty, extern_ty) in module.imports().zip(externs.iter()) {
imports.define(import_ty.module(), import_ty.name(), extern_ty.clone());
}
Self::new(store, module, &imports)
}
/// Creates a Wasmer `Instance` from a Wasmer `Module` and a WebAssembly Instance
///
/// # Important
@@ -87,33 +106,27 @@ 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> {
let instance_exports = instance.get(store.as_store_ref().objects()).exports();
use crate::js::externals::VMExtern;
let instance_exports = instance.exports();
let exports = module
.exports()
.map(|export_type| {
let name = export_type.name();
let extern_type = export_type.ty().clone();
let js_export =
js_sys::Reflect::get(&instance_exports, &name.into()).map_err(|_e| {
InstantiationError::Link(format!(
"Can't get {} from the instance exports",
&name
))
})?;
let export: Export =
Export::from_js_value(js_export, &mut store, extern_type)?.into();
let js_export = js_sys::Reflect::get(&instance_exports, &name.into())
.map_err(|_e| InstantiationError::NotInExports(name.to_string()))?;
let export: VMExtern =
VMExtern::from_js_value(js_export, &mut store, extern_type)?.into();
let extern_ = Extern::from_vm_extern(&mut store, export);
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

@@ -197,6 +197,12 @@ impl<'a, T: ValueType> WasmSlice<'a, T> {
self.len
}
/// Return if the slice is empty.
#[inline]
pub fn is_empty(&self) -> bool {
self.len == 0
}
/// Get a `WasmRef` to an element in the slice.
#[inline]
pub fn index(self, idx: u64) -> WasmRef<'a, T> {

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;
@@ -88,6 +86,9 @@ pub use wasmer_types::{
#[cfg(feature = "wat")]
pub use wat::parse_bytes as wat2wasm;
#[cfg(feature = "wasm-types-polyfill")]
pub use wasmparser;
/// Version number of this crate.
pub const VERSION: &str = env!("CARGO_PKG_VERSION");

View File

@@ -3,13 +3,14 @@ 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;
use bytes::Bytes;
use js_sys::{Reflect, Uint8Array, WebAssembly};
use std::borrow::Cow;
use std::fmt;
use std::io;
use std::path::Path;
@@ -49,6 +50,46 @@ pub struct ModuleTypeHints {
pub exports: Vec<ExternType>,
}
pub trait IntoBytes {
fn into_bytes(self) -> Bytes;
}
impl IntoBytes for Bytes {
fn into_bytes(self) -> Bytes {
self
}
}
impl IntoBytes for Vec<u8> {
fn into_bytes(self) -> Bytes {
Bytes::from(self)
}
}
impl IntoBytes for &[u8] {
fn into_bytes(self) -> Bytes {
Bytes::from(self.to_vec())
}
}
impl<const N: usize> IntoBytes for &[u8; N] {
fn into_bytes(self) -> Bytes {
Bytes::from(self.to_vec())
}
}
impl IntoBytes for &str {
fn into_bytes(self) -> Bytes {
Bytes::from(self.as_bytes().to_vec())
}
}
impl IntoBytes for Cow<'_, [u8]> {
fn into_bytes(self) -> Bytes {
Bytes::from(self.to_vec())
}
}
/// A WebAssembly Module contains stateless WebAssembly
/// code that has already been compiled and can be instantiated
/// multiple times.
@@ -64,7 +105,7 @@ pub struct Module {
// WebAssembly type hints
type_hints: Option<ModuleTypeHints>,
#[cfg(feature = "js-serializable-module")]
raw_bytes: Option<Vec<u8>>,
raw_bytes: Option<Bytes>,
}
impl Module {
@@ -200,7 +241,7 @@ impl Module {
type_hints,
name,
#[cfg(feature = "js-serializable-module")]
raw_bytes: Some(binary.to_vec()),
raw_bytes: Some(binary.into_bytes()),
})
}
@@ -222,7 +263,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 +273,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.
@@ -300,7 +305,7 @@ impl Module {
/// can later process via [`Module::deserialize`].
///
#[cfg(feature = "js-serializable-module")]
pub fn serialize(&self) -> Result<Vec<u8>, SerializeError> {
pub fn serialize(&self) -> Result<Bytes, SerializeError> {
self.raw_bytes.clone().ok_or(SerializeError::Generic(
"Not able to serialize module".to_string(),
))
@@ -313,11 +318,38 @@ impl Module {
#[cfg(feature = "js-serializable-module")]
pub unsafe fn deserialize(
_store: &impl AsStoreRef,
bytes: &[u8],
bytes: impl IntoBytes,
) -> Result<Self, DeserializeError> {
let bytes = bytes.into_bytes();
Self::new(_store, bytes).map_err(|e| DeserializeError::Compiler(e))
}
#[cfg(feature = "compiler")]
/// Deserializes a a serialized Module located in a `Path` into a `Module`.
/// > Note: the module has to be serialized before with the `serialize` method.
///
/// # Safety
///
/// Please check [`Module::deserialize`].
///
/// # Usage
///
/// ```ignore
/// # use wasmer::*;
/// # let mut store = Store::default();
/// # fn main() -> anyhow::Result<()> {
/// let module = Module::deserialize_from_file(&store, path)?;
/// # Ok(())
/// # }
/// ```
pub unsafe fn deserialize_from_file(
store: &impl AsStoreRef,
path: impl AsRef<Path>,
) -> Result<Self, DeserializeError> {
let artifact = std::fs::read(path.as_ref())?;
Ok(Self::new(store, bytes).map_err(|e| DeserializeError::Compiler(e)))
}
/// Sets the name of the current module.
/// This is normally useful for stacktraces and debugging.
///
@@ -525,16 +557,26 @@ impl Module {
ExportsIterator::new(iter, exports.length() as usize)
}
// /// Get the custom sections of the module given a `name`.
// ///
// /// # Important
// ///
// /// Following the WebAssembly spec, one name can have multiple
// /// custom sections. That's why an iterator (rather than one element)
// /// is returned.
// pub fn custom_sections<'a>(&'a self, name: &'a str) -> impl Iterator<Item = Arc<[u8]>> + 'a {
// unimplemented!();
// }
/// Get the custom sections of the module given a `name`.
///
/// # Important
///
/// Following the WebAssembly spec, one name can have multiple
/// custom sections. That's why an iterator (rather than one element)
/// is returned.
pub fn custom_sections<'a>(&'a self, name: &'a str) -> impl Iterator<Item = Box<[u8]>> + 'a {
// TODO: implement on JavaScript
DefaultCustomSectionsIterator {}
}
}
pub struct DefaultCustomSectionsIterator {}
impl Iterator for DefaultCustomSectionsIterator {
type Item = Box<[u8]>;
fn next(&mut self) -> Option<Self::Item> {
None
}
}
impl fmt::Debug for Module {

View File

@@ -114,6 +114,28 @@ impl NativeWasmTypeInto for f64 {
}
}
impl NativeWasmTypeInto for u128 {
#[inline]
unsafe fn from_abi(_store: &mut impl AsStoreMut, abi: Self::Abi) -> Self {
abi
}
#[inline]
fn into_abi(self, _store: &mut impl AsStoreMut) -> Self::Abi {
self
}
#[inline]
fn into_raw(self, _store: &mut impl AsStoreMut) -> f64 {
self as _
}
#[inline]
unsafe fn from_raw(_store: &mut impl AsStoreMut, raw: f64) -> Self {
raw as _
}
}
impl NativeWasmType for Function {
const WASM_TYPE: Type = Type::FuncRef;
type Abi = f64;

View File

@@ -1,6 +1,6 @@
use crate::js::NativeWasmTypeInto;
use crate::js::{externals::MemoryView, FromToNativeWasmType};
use crate::js::{MemoryAccessError, WasmRef, WasmSlice};
use crate::{js::NativeWasmTypeInto, AsStoreRef};
use std::convert::TryFrom;
use std::{fmt, marker::PhantomData, mem};
pub use wasmer_types::Memory32;
@@ -71,7 +71,7 @@ impl<T, M: MemorySize> WasmPtr<T, M> {
/// Get the offset into Wasm linear memory for this `WasmPtr`.
#[inline]
pub fn offset(self) -> M::Offset {
pub fn offset(&self) -> M::Offset {
self.offset
}
@@ -234,6 +234,10 @@ where
_phantom: PhantomData,
}
}
#[inline]
fn is_from_store(&self, _store: &impl AsStoreRef) -> bool {
true // in Javascript there are no different stores
}
}
unsafe impl<T: ValueType, M: MemorySize> ValueType for WasmPtr<T, M> {

View File

@@ -43,6 +43,7 @@ impl AsJs for Value {
Self::I64(i) => JsValue::from_f64(*i as f64),
Self::F32(f) => JsValue::from_f64(*f as f64),
Self::F64(f) => JsValue::from_f64(*f),
Self::V128(f) => JsValue::from_f64(*f as f64),
Self::FuncRef(Some(func)) => func
.handle
.get(store.as_store_ref().objects())

View File

@@ -7,7 +7,7 @@ use wasmer_types::Type;
//use crate::ExternRef;
use crate::js::externals::function::Function;
use super::store::AsStoreRef;
use super::store::{AsStoreMut, AsStoreRef};
/// WebAssembly computations manipulate values of basic value types:
/// * Integers (32 or 64 bit width)
@@ -37,6 +37,9 @@ pub enum Value {
/// A first-class reference to a WebAssembly function.
FuncRef(Option<Function>),
/// A 128-bit number
V128(u128),
}
macro_rules! accessors {
@@ -76,6 +79,7 @@ impl Value {
Self::I64(_) => Type::I64,
Self::F32(_) => Type::F32,
Self::F64(_) => Type::F64,
Self::V128(_) => Type::V128,
//Self::ExternRef(_) => Type::ExternRef,
Self::FuncRef(_) => Type::FuncRef,
}
@@ -88,6 +92,7 @@ impl Value {
Self::I64(v) => v as f64,
Self::F32(v) => v as f64,
Self::F64(v) => v,
Self::V128(v) => v as f64,
Self::FuncRef(Some(ref f)) => f
.handle
.get(store.as_store_ref().objects())
@@ -107,12 +112,12 @@ impl Value {
///
pub unsafe fn from_raw(_store: &impl AsStoreRef, ty: Type, raw: f64) -> Self {
match ty {
Type::I32 => Self::I32(raw as i32),
Type::I64 => Self::I64(raw as i64),
Type::F32 => Self::F32(raw as f32),
Type::I32 => Self::I32(raw as _),
Type::I64 => Self::I64(raw as _),
Type::F32 => Self::F32(raw as _),
Type::F64 => Self::F64(raw),
Type::V128 => Self::V128(raw as _),
Type::FuncRef => todo!(),
Type::V128 => todo!(),
Type::ExternRef => todo!(),
//Self::ExternRef(
//{
@@ -134,6 +139,7 @@ impl Value {
| Self::I64(_)
| Self::F32(_)
| Self::F64(_)
| Self::V128(_)
//| Self::ExternRef(None)
| Self::FuncRef(None) => true,
//Self::ExternRef(Some(e)) => e.is_from_store(store),
@@ -147,6 +153,7 @@ impl Value {
(I64(i64) i64 unwrap_i64 *e)
(F32(f32) f32 unwrap_f32 *e)
(F64(f64) f64 unwrap_f64 *e)
(V128(u128) v128 unwrap_v128 *e)
//(ExternRef(&Option<ExternRef>) externref unwrap_externref e)
(FuncRef(&Option<Function>) funcref unwrap_funcref e)
}
@@ -159,6 +166,7 @@ impl fmt::Debug for Value {
Self::I64(v) => write!(f, "I64({:?})", v),
Self::F32(v) => write!(f, "F32({:?})", v),
Self::F64(v) => write!(f, "F64({:?})", v),
Self::V128(v) => write!(f, "V128({:?})", v),
//Self::ExternRef(None) => write!(f, "Null ExternRef"),
//Self::ExternRef(Some(v)) => write!(f, "ExternRef({:?})", v),
Self::FuncRef(None) => write!(f, "Null FuncRef"),
@@ -174,12 +182,19 @@ impl ToString for Value {
Self::I64(v) => v.to_string(),
Self::F32(v) => v.to_string(),
Self::F64(v) => v.to_string(),
Self::V128(v) => v.to_string(),
//Self::ExternRef(_) => "externref".to_string(),
Self::FuncRef(_) => "funcref".to_string(),
}
}
}
impl From<u128> for Value {
fn from(val: u128) -> Self {
Self::V128(val)
}
}
impl From<i32> for Value {
fn from(val: i32) -> Self {
Self::I32(val)
@@ -246,9 +261,18 @@ const NOT_I32: &str = "Value is not of Wasm type i32";
const NOT_I64: &str = "Value is not of Wasm type i64";
const NOT_F32: &str = "Value is not of Wasm type f32";
const NOT_F64: &str = "Value is not of Wasm type f64";
const NOT_V128: &str = "Value is not of Wasm type u128";
const NOT_FUNCREF: &str = "Value is not of Wasm type funcref";
//const NOT_EXTERNREF: &str = "Value is not of Wasm type externref";
impl TryFrom<Value> for u128 {
type Error = &'static str;
fn try_from(value: Value) -> Result<Self, Self::Error> {
value.v128().ok_or(NOT_V128)
}
}
impl TryFrom<Value> for i32 {
type Error = &'static str;

View File

@@ -204,6 +204,7 @@ impl Function {
Rets: WasmTypeList,
{
let env = FunctionEnv::new(store, ());
let func_ptr = func.function_body_ptr();
let host_data = Box::new(StaticFunction {
raw_store: store.as_store_mut().as_raw() as *mut u8,
env,
@@ -211,7 +212,6 @@ impl Function {
});
let function_type = FunctionType::new(Args::wasm_types(), Rets::wasm_types());
let func_ptr = <F as HostFunction<(), Args, Rets, WithoutEnv>>::function_body_ptr();
let type_index = store
.as_store_mut()
.engine()
@@ -289,6 +289,7 @@ impl Function {
{
// println!("new native {:p}", &new_env);
let func_ptr = func.function_body_ptr();
let host_data = Box::new(StaticFunction {
raw_store: store.as_store_mut().as_raw() as *mut u8,
env: env.clone(),
@@ -296,7 +297,6 @@ impl Function {
});
let function_type = FunctionType::new(Args::wasm_types(), Rets::wasm_types());
let func_ptr = <F as HostFunction<T, Args, Rets, WithEnv>>::function_body_ptr();
let type_index = store
.as_store_mut()
.engine()
@@ -1092,7 +1092,7 @@ mod inner {
Kind: HostFunctionKind,
{
/// Get the pointer to the function body.
fn function_body_ptr() -> *const VMFunctionBody;
fn function_body_ptr(&self) -> *const VMFunctionBody;
/// Get the pointer to the function call trampoline.
fn call_trampoline_address() -> VMTrampoline;
@@ -1268,7 +1268,7 @@ mod inner {
Func: Fn(FunctionEnvMut<T>, $( $x , )*) -> RetsAsResult + 'static,
{
#[allow(non_snake_case)]
fn function_body_ptr() -> *const VMFunctionBody {
fn function_body_ptr(&self) -> *const VMFunctionBody {
/// This is a function that wraps the real host
/// function. Its address will be used inside the
/// runtime.
@@ -1352,7 +1352,7 @@ mod inner {
Func: Fn($( $x , )*) -> RetsAsResult + 'static,
{
#[allow(non_snake_case)]
fn function_body_ptr() -> *const VMFunctionBody {
fn function_body_ptr(&self) -> *const VMFunctionBody {
/// This is a function that wraps the real host
/// function. Its address will be used inside the
/// runtime.

View File

@@ -154,6 +154,7 @@ impl Memory {
mem.try_clone().map(|mem| mem.into())
}
/// To `VMExtern`.
pub(crate) fn to_vm_extern(&self) -> VMExtern {
VMExtern::Memory(self.handle.internal_handle())
}

View File

@@ -29,7 +29,7 @@ impl<T> FunctionEnv<T> {
}
/// Get the data as reference
pub fn as_ref<'a>(&self, store: &'a impl AsStoreMut) -> &'a T
pub fn as_ref<'a>(&self, store: &'a impl AsStoreRef) -> &'a T
where
T: Any + Send + 'static + Sized,
{
@@ -105,6 +105,11 @@ impl<T: Send + 'static> FunctionEnvMut<'_, T> {
self.func_env.as_mut(&mut self.store_mut)
}
/// Borrows a new immmutable reference
pub fn as_ref(&self) -> FunctionEnv<T> {
self.func_env.clone()
}
/// Borrows a new mutable reference
pub fn as_mut(&mut self) -> FunctionEnvMut<'_, T> {
FunctionEnvMut {

View File

@@ -1,6 +1,8 @@
use crate::sys::InstantiationError;
use crate::AsStoreMut;
use crate::AsStoreRef;
use bytes::Bytes;
use std::borrow::Cow;
use std::fmt;
use std::io;
use std::path::Path;
@@ -54,6 +56,46 @@ pub struct Module {
module_info: Arc<ModuleInfo>,
}
pub trait IntoBytes {
fn into_bytes(self) -> Bytes;
}
impl IntoBytes for Bytes {
fn into_bytes(self) -> Bytes {
self
}
}
impl IntoBytes for Vec<u8> {
fn into_bytes(self) -> Bytes {
Bytes::from(self)
}
}
impl IntoBytes for &[u8] {
fn into_bytes(self) -> Bytes {
Bytes::from(self.to_vec())
}
}
impl<const N: usize> IntoBytes for &[u8; N] {
fn into_bytes(self) -> Bytes {
Bytes::from(self.to_vec())
}
}
impl IntoBytes for &str {
fn into_bytes(self) -> Bytes {
Bytes::from(self.as_bytes().to_vec())
}
}
impl IntoBytes for Cow<'_, [u8]> {
fn into_bytes(self) -> Bytes {
Bytes::from(self.to_vec())
}
}
impl Module {
#[cfg(feature = "compiler")]
/// Creates a new WebAssembly Module given the configuration
@@ -124,7 +166,6 @@ impl Module {
e
)))
})?;
Self::from_binary(store, bytes.as_ref())
}
@@ -206,8 +247,8 @@ impl Module {
/// # Ok(())
/// # }
/// ```
pub fn serialize(&self) -> Result<Vec<u8>, SerializeError> {
self.artifact.serialize()
pub fn serialize(&self) -> Result<Bytes, SerializeError> {
self.artifact.serialize().map(|bytes| bytes.into())
}
/// Serializes a module into a file that the `Engine`
@@ -254,9 +295,10 @@ impl Module {
/// ```
pub unsafe fn deserialize(
store: &impl AsStoreRef,
bytes: &[u8],
bytes: impl IntoBytes,
) -> Result<Self, DeserializeError> {
let artifact = store.as_store_ref().engine().deserialize(bytes)?;
let bytes = bytes.into_bytes();
let artifact = store.as_store_ref().engine().deserialize(&bytes)?;
Ok(Self::from_artifact(artifact))
}

View File

@@ -17,7 +17,7 @@ use wasmer_types::RawValue;
/// A WebAssembly function that can be called natively
/// (using the Native ABI).
pub struct TypedFunction<Args, Rets> {
func: Function,
pub(crate) func: Function,
_phantom: PhantomData<fn(Args) -> Rets>,
}
@@ -45,16 +45,6 @@ impl<Args: WasmTypeList, Rets: WasmTypeList> Clone for TypedFunction<Args, Rets>
}
}
impl<Args, Rets> From<TypedFunction<Args, Rets>> for Function
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
fn from(other: TypedFunction<Args, Rets>) -> Self {
other.func
}
}
macro_rules! impl_native_traits {
( $( $x:ident ),* ) => {
#[allow(unused_parens, non_snake_case)]

View File

@@ -4,7 +4,7 @@
use wasmer_types::{NativeWasmType, RawValue, Type};
use wasmer_vm::{VMExternRef, VMFuncRef};
use crate::{ExternRef, Function};
use crate::{ExternRef, Function, TypedFunction, WasmTypeList};
use super::store::AsStoreMut;
@@ -165,6 +165,16 @@ impl NativeWasmTypeInto for Option<ExternRef> {
}
}
impl<Args, Rets> From<TypedFunction<Args, Rets>> for Function
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
fn from(other: TypedFunction<Args, Rets>) -> Self {
other.func
}
}
impl NativeWasmType for Function {
const WASM_TYPE: Type = Type::FuncRef;
type Abi = usize;

View File

@@ -76,7 +76,7 @@ impl<T, M: MemorySize> WasmPtr<T, M> {
/// Get the offset into Wasm linear memory for this `WasmPtr`.
#[inline]
pub fn offset(self) -> M::Offset {
pub fn offset(&self) -> M::Offset {
self.offset
}

View File

@@ -29,7 +29,7 @@ mod js {
fn global_get() {
let mut store = Store::default();
let global_i32 = Global::new(&mut store, Value::I32(10));
assert_eq!(global_i32.get(&store), Value::I32(10));
assert_eq!(global_i32.get(&mut store), Value::I32(10));
// 64-bit values are not yet fully supported in some versions of Node
// Commenting this tests for now:

View File

@@ -92,6 +92,61 @@ mod js {
assert_eq!(get_magic.call(&mut store, &[]).unwrap(), expected);
}
#[wasm_bindgen_test]
fn test_imports_from_js_object() {
let mut store = Store::default();
let mut module = Module::new(
&store,
br#"
(module
(func $imported (import "env" "imported") (param i32) (result i32))
(func (export "exported") (param i32) (result i32)
(call $imported (local.get 0))
)
)
"#,
)
.unwrap();
module
.set_type_hints(ModuleTypeHints {
imports: vec![ExternType::Function(FunctionType::new(
vec![Type::I32],
vec![Type::I32],
))],
exports: vec![ExternType::Function(FunctionType::new(
vec![Type::I32],
vec![Type::I32],
))],
})
.unwrap();
let obj: js_sys::Object = js_sys::Function::new_with_args(
"",
"return {
\"env\": {
\"imported\": function(num) {
console.log(\"Calling `imported`...\");
var result = num * 2;
console.log(\"Result of `imported`: \", result);
return result;
}
}
};",
)
.call0(&wasm_bindgen::JsValue::UNDEFINED)
.unwrap()
.into();
let import_object = Imports::new_from_js_object(&mut store, &module, obj)
.expect("Can't get imports from js object");
let instance = Instance::new(&mut store, &module, &import_object).unwrap();
let exported = instance.exports.get_function("exported").unwrap();
let expected = vec![Val::I32(6)].into_boxed_slice();
assert_eq!(exported.call(&mut store, &[Val::I32(3)]).unwrap(), expected);
}
#[wasm_bindgen_test]
fn test_imported_function_dynamic() {
let mut store = Store::default();

View File

@@ -475,7 +475,7 @@ pub unsafe extern "C" fn wasm_module_deserialize(
#[no_mangle]
pub unsafe extern "C" fn wasm_module_serialize(module: &wasm_module_t, out: &mut wasm_byte_vec_t) {
let byte_vec = c_try!(module.inner.serialize(); otherwise ());
out.set_buffer(byte_vec);
out.set_buffer(byte_vec.to_vec());
}
#[cfg(test)]

View File

@@ -25,16 +25,11 @@ impl Inspect {
fn inner_execute(&self) -> Result<()> {
let (store, _compiler_type) = self.store.get_store()?;
let module_contents = std::fs::read(&self.path)?;
let module = Module::new(&store, &module_contents)?;
println!(
"Type: {}",
if !is_wasm(&module_contents) {
"wat"
} else {
"wasm"
}
);
println!("Size: {}", ByteSize(module_contents.len() as _));
let iswasm = is_wasm(&module_contents);
let module_len = module_contents.len();
let module = Module::new(&store, module_contents)?;
println!("Type: {}", if !iswasm { "wat" } else { "wasm" });
println!("Size: {}", ByteSize(module_len as _));
println!("Imports:");
println!(" Functions:");
for f in module.imports().functions() {

View File

@@ -272,7 +272,7 @@ impl Run {
let module_result: Result<Module> = if !self.disable_cache && contents.len() > 0x1000 {
self.get_module_from_cache(&store, &contents, &compiler_type)
} else {
Module::new(&store, &contents).map_err(|e| e.into())
Module::new(&store, contents).map_err(|e| e.into())
};
#[cfg(not(feature = "cache"))]
let module_result = Module::new(&store, &contents);
@@ -319,7 +319,7 @@ impl Run {
warning!("cached module is corrupted: {}", err);
}
}
let module = Module::new(store, &contents)?;
let module = Module::new(store, contents)?;
// Store the compiled Module in cache
cache.store(hash, &module)?;
Ok(module)

View File

@@ -7,11 +7,7 @@ license = "MIT"
edition = "2018"
[dependencies]
libc = { version = "^0.2", default-features = false, optional = true }
thiserror = "1"
tracing = { version = "0.1" }
typetag = { version = "0.1", optional = true }
slab = { version = "0.4", optional = true }
wasmer-vfs = { path = "../vfs", version = "=3.0.0-beta", default-features = false }
[features]

View File

@@ -7,11 +7,7 @@ license = "MIT"
edition = "2018"
[dependencies]
libc = { version = "^0.2", default-features = false, optional = true }
thiserror = "1"
tracing = { version = "0.1" }
typetag = { version = "0.1", optional = true }
slab = { version = "0.4", optional = true }
wasmer-vfs = { path = "../vfs", version = "=3.0.0-beta", default-features = false }
bytes = "1"

View File

@@ -44,8 +44,9 @@ tracing-wasm = "0.2"
[features]
default = ["sys-default"]
wasix = []
sys = ["wasmer/sys"]
sys = ["wasmer/sys", "wasix"]
sys-default = ["wasmer/wat", "wasmer/compiler", "sys", "logging", "host-fs", "sys-poll", "host-vnet" ]
sys-poll = []

View File

@@ -47,9 +47,9 @@ pub use crate::state::{
WasiStateCreationError, ALL_RIGHTS, VIRTUAL_ROOT_FD,
};
pub use crate::syscalls::types;
pub use crate::utils::{
get_wasi_version, get_wasi_versions, is_wasi_module, is_wasix_module, WasiVersion,
};
#[cfg(feature = "wasix")]
pub use crate::utils::is_wasix_module;
pub use crate::utils::{get_wasi_version, get_wasi_versions, is_wasi_module, WasiVersion};
pub use wasmer_vbus::{UnsupportedVirtualBus, VirtualBus};
#[deprecated(since = "2.1.0", note = "Please use `wasmer_vfs::FsError`")]
pub use wasmer_vfs::FsError as WasiFsError;
@@ -62,9 +62,11 @@ use wasmer_wasi_types::__WASI_CLOCK_MONOTONIC;
use derivative::*;
use std::ops::Deref;
use thiserror::Error;
use tracing::trace;
use wasmer::{
imports, namespace, AsStoreMut, AsStoreRef, Exports, Function, FunctionEnv, Imports, Memory,
Memory32, MemoryAccessError, MemorySize, MemoryView, Module, TypedFunction,
imports, namespace, AsStoreMut, AsStoreRef, ExportError, Exports, Function, FunctionEnv,
Imports, Instance, Memory, Memory32, MemoryAccessError, MemorySize, MemoryView, Module,
TypedFunction,
};
pub use runtime::{
@@ -166,6 +168,33 @@ impl WasiFunctionEnv {
self.env.as_mut(store)
}
/// Initializes the WasiEnv using the instance exports
/// (this must be executed before attempting to use it)
/// (as the stores can not by themselves be passed between threads we can store the module
/// in a thread-local variables and use it later - for multithreading)
pub fn initialize(
&mut self,
store: &mut impl AsStoreMut,
instance: &Instance,
) -> Result<(), ExportError> {
// List all the exports and imports
for ns in instance.module().exports() {
//trace!("module::export - {} ({:?})", ns.name(), ns.ty());
trace!("module::export - {}", ns.name());
}
for ns in instance.module().imports() {
trace!("module::import - {}::{}", ns.module(), ns.name());
}
// First we get the malloc function which if it exists will be used to
// create the pthread_self structure
let memory = instance.exports.get_memory("memory")?.clone();
let env = self.data_mut(store);
env.set_memory(memory);
Ok(())
}
/// Like `import_object` but containing all the WASI versions detected in
/// the module.
pub fn import_object_for_all_wasi_versions(
@@ -184,6 +213,7 @@ impl WasiFunctionEnv {
}
}
#[cfg(feature = "wasix")]
if is_wasix_module(module) {
self.data_mut(store)
.state
@@ -394,8 +424,12 @@ pub fn generate_import_object_from_env(
WasiVersion::Snapshot1 | WasiVersion::Latest => {
generate_import_object_snapshot1(store, env)
}
#[cfg(feature = "wasix")]
WasiVersion::Wasix32v1 => generate_import_object_wasix32_v1(store, env),
#[cfg(feature = "wasix")]
WasiVersion::Wasix64v1 => generate_import_object_wasix64_v1(store, env),
#[cfg(not(feature = "wasix"))]
_ => unimplemented!(),
}
}
@@ -537,6 +571,7 @@ fn generate_import_object_snapshot1(
}
/// Combines a state generating function with the import list for snapshot 1
#[cfg(feature = "wasix")]
fn generate_import_object_wasix32_v1(
mut store: &mut impl AsStoreMut,
env: &FunctionEnv<WasiEnv>,
@@ -655,6 +690,7 @@ fn generate_import_object_wasix32_v1(
}
}
#[cfg(feature = "wasix")]
fn generate_import_object_wasix64_v1(
mut store: &mut impl AsStoreMut,
env: &FunctionEnv<WasiEnv>,

View File

@@ -18,7 +18,9 @@ pub mod windows;
pub mod legacy;
//pub mod wasi;
#[cfg(feature = "wasix")]
pub mod wasix32;
#[cfg(feature = "wasix")]
pub mod wasix64;
use self::types::*;

View File

@@ -10,6 +10,7 @@ pub fn is_wasi_module(module: &Module) -> bool {
}
#[allow(dead_code)]
#[cfg(feature = "wasix")]
/// Returns if the module is WASIX or not
pub fn is_wasix_module(module: &Module) -> bool {
match get_wasi_versions(module, false).ok_or(false) {

View File

@@ -45,7 +45,7 @@ fn test_deserialize(config: crate::Config) -> Result<()> {
let serialized_bytes = module.serialize()?;
let headless_store = config.headless_store();
let deserialized_module = unsafe { Module::deserialize(&headless_store, &serialized_bytes)? };
let deserialized_module = unsafe { Module::deserialize(&headless_store, serialized_bytes)? };
assert_eq!(deserialized_module.name(), Some("name"));
assert_eq!(
deserialized_module.exports().collect::<Vec<_>>(),