feat(api) Merge js-api into api.

This patch takes the entire `wasmer-js` crate and merges it into the
`wasmer` crate.

Inside the `lib/api/src/` directory, there are 2 new directories:

1. a new `sys` directory, which contains the usual `wasmer` crate
   implementation,
2. a new directory `js`, which contains the implementation of
   `wasmer-js`.

The `Cargo.toml` file is still compatible. The `default` feature
fallbacks to `sys-default`, which enables the `sys` feature. All
features related to compilers or engines or anything else prior this
patch, activates the `sys` feature.

Parallel to that, there is a `js-default` and `js` features.

The `Cargo.toml` file is extensively documented to explain what are
dependencies, dev-dependencies, features and other sections related to
`sys` or to `js`.

There is a bug with `wasm_bindgen_test` where it doesn't compile or
look for tests in `tests/*/<test>.rs`. The hack is to name files
`tests/js_<test>.rs`. Ugly, but it works.
This commit is contained in:
Ivan Enderlin
2021-07-23 12:10:49 +02:00
parent 6b2ec7209d
commit b30284897e
65 changed files with 3808 additions and 3783 deletions

24
Cargo.lock generated
View File

@@ -2428,13 +2428,17 @@ version = "2.0.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"cfg-if 1.0.0", "cfg-if 1.0.0",
"hashbrown 0.11.2",
"indexmap", "indexmap",
"js-sys",
"libc", "libc",
"loupe", "loupe",
"more-asserts", "more-asserts",
"target-lexicon 0.12.0", "target-lexicon 0.12.0",
"tempfile", "tempfile",
"thiserror", "thiserror",
"wasm-bindgen",
"wasm-bindgen-test",
"wasmer-compiler", "wasmer-compiler",
"wasmer-compiler-cranelift", "wasmer-compiler-cranelift",
"wasmer-compiler-llvm", "wasmer-compiler-llvm",
@@ -2445,6 +2449,7 @@ dependencies = [
"wasmer-engine-universal", "wasmer-engine-universal",
"wasmer-types", "wasmer-types",
"wasmer-vm", "wasmer-vm",
"wasmparser",
"wat", "wat",
"winapi", "winapi",
] ]
@@ -2745,25 +2750,6 @@ dependencies = [
"tempfile", "tempfile",
] ]
[[package]]
name = "wasmer-js"
version = "2.0.0"
dependencies = [
"anyhow",
"cfg-if 1.0.0",
"hashbrown 0.11.2",
"indexmap",
"js-sys",
"more-asserts",
"thiserror",
"wasm-bindgen",
"wasm-bindgen-test",
"wasmer-derive",
"wasmer-types",
"wasmparser",
"wat",
]
[[package]] [[package]]
name = "wasmer-middlewares" name = "wasmer-middlewares"
version = "2.0.0" version = "2.0.0"

View File

@@ -43,7 +43,6 @@ members = [
"lib/engine-universal", "lib/engine-universal",
"lib/engine-dylib", "lib/engine-dylib",
"lib/engine-staticlib", "lib/engine-staticlib",
"lib/js-api",
"lib/object", "lib/object",
"lib/vm", "lib/vm",
"lib/wasi", "lib/wasi",

View File

@@ -10,96 +10,209 @@ license = "MIT"
readme = "README.md" readme = "README.md"
edition = "2018" edition = "2018"
[lib]
crate-type = ["cdylib", "rlib"]
#####
#
# This crate comes in 2 major flavors:
#
# * `sys`, where `wasmer` will be compiled to a native executable
# which provides compilers, engines, a full VM etc.
# * `js`, where `wasmer` will be compiled to WebAssembly to run in a
# JavaScript host.
#
#####
#####
#
# Shared dependencies.
[dependencies] [dependencies]
#
# ## Mandatory shared dependencies.
#
indexmap = { version = "1.6", features = ["serde-1"] }
cfg-if = "1.0"
thiserror = "1.0"
more-asserts = "0.2"
#
# ## Optional shared dependencies.
#
wat = { version = "1.0", optional = true }
#
#####
#####
#
# # Dependencies and Development Dependencies for `sys`.
#
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
#
# ## Mandatory dependencies for `sys`.
#
wasmer-vm = { path = "../vm", version = "2.0.0" } wasmer-vm = { path = "../vm", version = "2.0.0" }
wasmer-compiler-singlepass = { path = "../compiler-singlepass", version = "2.0.0", optional = true }
wasmer-compiler-cranelift = { path = "../compiler-cranelift", version = "2.0.0", optional = true }
wasmer-compiler-llvm = { path = "../compiler-llvm", version = "2.0.0", optional = true }
wasmer-compiler = { path = "../compiler", version = "2.0.0" } wasmer-compiler = { path = "../compiler", version = "2.0.0" }
wasmer-derive = { path = "../derive", version = "2.0.0" } wasmer-derive = { path = "../derive", version = "2.0.0" }
wasmer-engine = { path = "../engine", version = "2.0.0" } wasmer-engine = { path = "../engine", version = "2.0.0" }
wasmer-engine-universal = { path = "../engine-universal", version = "2.0.0", optional = true }
wasmer-engine-dylib = { path = "../engine-dylib", version = "2.0.0", optional = true }
wasmer-types = { path = "../types", version = "2.0.0" } wasmer-types = { path = "../types", version = "2.0.0" }
indexmap = { version = "1.6", features = ["serde-1"] }
cfg-if = "1.0"
wat = { version = "1.0", optional = true }
thiserror = "1.0"
more-asserts = "0.2"
target-lexicon = { version = "0.12", default-features = false } target-lexicon = { version = "0.12", default-features = false }
loupe = "0.1" loupe = "0.1"
#
[target.'cfg(target_os = "windows")'.dependencies] # ## Optional dependencies for `sys`.
#
wasmer-compiler-singlepass = { path = "../compiler-singlepass", version = "2.0.0", optional = true }
wasmer-compiler-cranelift = { path = "../compiler-cranelift", version = "2.0.0", optional = true }
wasmer-compiler-llvm = { path = "../compiler-llvm", version = "2.0.0", optional = true }
wasmer-engine-universal = { path = "../engine-universal", version = "2.0.0", optional = true }
wasmer-engine-dylib = { path = "../engine-dylib", version = "2.0.0", optional = true }
#
# ## Mandatory dependencies for `sys` on Windows.
#
[target.'cfg(all(not(target_arch = "wasm32"), target_os = "windows"))'.dependencies]
#
winapi = "0.3" winapi = "0.3"
#
[dev-dependencies] # ## Development Dependencies for `sys`.
# for the binary wasmer.rs #
libc = { version = "^0.2", default-features = false } [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
#
libc = { version = "^0.2", default-features = false } # for the binary wasmer.rs
wat = "1.0" wat = "1.0"
tempfile = "3.1" tempfile = "3.1"
anyhow = "1.0" anyhow = "1.0"
#
#####
#####
#
# # Dependencies and Develoment Dependencies for `js`.
#
[target.'cfg(target_arch = "wasm32")'.dependencies]
#
# ## Mandatory dependencies for `js`.
#
wasmer-types = { path = "../types", version = "2.0.0", default-features = false, features = ["std"] }
wasm-bindgen = "0.2.74"
js-sys = "0.3.51"
wasmer-derive = { path = "../derive", version = "2.0.0" }
#
# ## Optional dependencies for `js`.
#
wasmparser = { version = "0.78", default-features = false, optional = true }
hashbrown = { version = "0.11", optional = true }
#
# ## Development Dependencies for `js`.
#
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
#
wat = "1.0"
anyhow = "1.0"
wasm-bindgen-test = "0.3.0"
#
#####
#####
#
# # Specific to `js`.
#
# `wasm-opt` is on by default in for the release profile, but it can be
# disabled by setting it to `false`
[package.metadata.wasm-pack.profile.release]
wasm-opt = false
#
####
[badges] [badges]
maintenance = { status = "actively-developed" } maintenance = { status = "actively-developed" }
[features] [features]
default = ["wat", "default-cranelift", "default-universal"] default = ["sys-default"]
#####
#
# # Features for `sys`.
#
sys = []
sys-default = ["sys", "wat", "default-cranelift", "default-universal"]
#
# ## Compilers.
#
compiler = [ compiler = [
"sys",
"wasmer-compiler/translator", "wasmer-compiler/translator",
"wasmer-engine-universal/compiler", "wasmer-engine-universal/compiler",
"wasmer-engine-dylib/compiler", "wasmer-engine-dylib/compiler",
] ]
engine = [] singlepass = [
universal = [ "compiler",
"wasmer-engine-universal", "wasmer-compiler-singlepass",
"engine" ]
] cranelift = [
dylib = [ "compiler",
"wasmer-engine-dylib", "wasmer-compiler-cranelift",
"engine" ]
] llvm = [
singlepass = [ "compiler",
"wasmer-compiler-singlepass", "wasmer-compiler-llvm",
"compiler", ]
]
cranelift = [
"wasmer-compiler-cranelift",
"compiler",
]
llvm = [
"wasmer-compiler-llvm",
"compiler",
]
default-singlepass = [
"singlepass",
"default-compiler"
]
default-cranelift = [
"cranelift",
"default-compiler"
]
default-llvm = [
"llvm",
"default-compiler"
]
default-universal = [
"universal",
"default-engine"
]
default-dylib = [
"dylib",
"default-engine"
]
default-compiler = [] default-compiler = []
default-singlepass = [
"default-compiler",
"singlepass",
]
default-cranelift = [
"default-compiler",
"cranelift",
]
default-llvm = [
"default-compiler",
"llvm",
]
#
# ## Engines.
#
engine = ["sys"]
universal = [
"engine",
"wasmer-engine-universal",
]
dylib = [
"engine",
"wasmer-engine-dylib",
]
default-engine = [] default-engine = []
default-universal = [
# experimental / in-development features "default-engine",
"universal",
]
default-dylib = [
"default-engine",
"dylib",
]
#
# ## Experimental / in-development features
#
experimental-reference-types-extern-ref = [ experimental-reference-types-extern-ref = [
"sys",
"wasmer-types/experimental-reference-types-extern-ref", "wasmer-types/experimental-reference-types-extern-ref",
] ]
#
# Deprecated features. # ## Deprecated features.
#
jit = ["universal"] jit = ["universal"]
native = ["dylib"] native = ["dylib"]
#
#####
#####
#
# # Features for `js`.
#
js = []
js-default = ["js", "std", "wasm-types-polyfill", "wat"]
#
wasm-types-polyfill = ["js", "wasmparser"]
std = ["js"]
core = ["js", "hashbrown"]
#
#####

View File

@@ -1,4 +1,4 @@
use crate::{ExportError, Instance}; use crate::js::{ExportError, Instance};
use thiserror::Error; use thiserror::Error;
/// An error while initializing the user supplied host env with the `WasmerEnv` trait. /// An error while initializing the user supplied host env with the `WasmerEnv` trait.

View File

@@ -1,4 +1,4 @@
use crate::lib::std::string::String; use crate::js::lib::std::string::String;
#[cfg(feature = "std")] #[cfg(feature = "std")]
use thiserror::Error; use thiserror::Error;

View File

@@ -1,7 +1,7 @@
use crate::instance::Instance; use crate::js::instance::Instance;
use crate::wasm_bindgen_polyfill::Global; use crate::js::wasm_bindgen_polyfill::Global;
use crate::HostEnvInitError; use crate::js::HostEnvInitError;
use crate::WasmerEnv; use crate::js::WasmerEnv;
use js_sys::Function; use js_sys::Function;
use js_sys::WebAssembly::{Memory, Table}; use js_sys::WebAssembly::{Memory, Table};
use std::cell::RefCell; use std::cell::RefCell;

View File

@@ -1,8 +1,8 @@
use crate::export::Export; use crate::js::export::Export;
use crate::externals::{Extern, Function, Global, Memory, Table}; use crate::js::externals::{Extern, Function, Global, Memory, Table};
use crate::import_object::LikeNamespace; use crate::js::import_object::LikeNamespace;
use crate::native::NativeFunc; use crate::js::native::NativeFunc;
use crate::WasmTypeList; use crate::js::WasmTypeList;
use indexmap::IndexMap; use indexmap::IndexMap;
use std::fmt; use std::fmt;
use std::iter::{ExactSizeIterator, FromIterator}; use std::iter::{ExactSizeIterator, FromIterator};
@@ -12,7 +12,7 @@ use thiserror::Error;
/// The `ExportError` can happen when trying to get a specific /// The `ExportError` can happen when trying to get a specific
/// export [`Extern`] from the [`Instance`] exports. /// export [`Extern`] from the [`Instance`] exports.
/// ///
/// [`Instance`]: crate::Instance /// [`Instance`]: crate::js::Instance
/// ///
/// # Examples /// # Examples
/// ///
@@ -292,18 +292,18 @@ impl LikeNamespace for Exports {
/// This trait is used to mark types as gettable from an [`Instance`]. /// This trait is used to mark types as gettable from an [`Instance`].
/// ///
/// [`Instance`]: crate::Instance /// [`Instance`]: crate::js::Instance
pub trait Exportable<'a>: Sized { pub trait Exportable<'a>: Sized {
/// This function is used when providedd the [`Extern`] as exportable, so it /// This function is used when providedd the [`Extern`] as exportable, so it
/// can be used while instantiating the [`Module`]. /// can be used while instantiating the [`Module`].
/// ///
/// [`Module`]: crate::Module /// [`Module`]: crate::js::Module
fn to_export(&self) -> Export; fn to_export(&self) -> Export;
/// Implementation of how to get the export corresponding to the implementing type /// Implementation of how to get the export corresponding to the implementing type
/// from an [`Instance`] by name. /// from an [`Instance`] by name.
/// ///
/// [`Instance`]: crate::Instance /// [`Instance`]: crate::js::Instance
fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError>; fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError>;
} }

View File

@@ -1,18 +1,18 @@
use crate::exports::{ExportError, Exportable}; use crate::js::exports::{ExportError, Exportable};
use crate::externals::Extern; use crate::js::externals::Extern;
use crate::store::Store; use crate::js::store::Store;
use crate::types::{param_from_js, AsJs /* ValFuncRef */, Val}; use crate::js::types::{param_from_js, AsJs /* ValFuncRef */, Val};
use crate::FunctionType; use crate::js::FunctionType;
use crate::NativeFunc; use crate::js::NativeFunc;
use crate::RuntimeError; use crate::js::RuntimeError;
use crate::WasmerEnv; use crate::js::WasmerEnv;
pub use inner::{FromToNativeWasmType, HostFunction, WasmTypeList, WithEnv, WithoutEnv}; pub use inner::{FromToNativeWasmType, HostFunction, WasmTypeList, WithEnv, WithoutEnv};
use js_sys::{Array, Function as JSFunction}; use js_sys::{Array, Function as JSFunction};
use std::iter::FromIterator; use std::iter::FromIterator;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast; use wasm_bindgen::JsCast;
use crate::export::{Export, VMFunction}; use crate::js::export::{Export, VMFunction};
use std::fmt; use std::fmt;
#[repr(C)] #[repr(C)]

View File

@@ -1,13 +1,13 @@
use crate::export::Export; use crate::js::export::Export;
use crate::export::VMGlobal; use crate::js::export::VMGlobal;
use crate::exports::{ExportError, Exportable}; use crate::js::exports::{ExportError, Exportable};
use crate::externals::Extern; use crate::js::externals::Extern;
use crate::store::Store; use crate::js::store::Store;
use crate::types::{Val, ValType}; use crate::js::types::{Val, ValType};
use crate::wasm_bindgen_polyfill::Global as JSGlobal; use crate::js::wasm_bindgen_polyfill::Global as JSGlobal;
use crate::GlobalType; use crate::js::GlobalType;
use crate::Mutability; use crate::js::Mutability;
use crate::RuntimeError; use crate::js::RuntimeError;
use wasm_bindgen::JsValue; use wasm_bindgen::JsValue;
/// A WebAssembly `global` instance. /// A WebAssembly `global` instance.

View File

@@ -1,8 +1,8 @@
use crate::export::{Export, VMMemory}; use crate::js::export::{Export, VMMemory};
use crate::exports::{ExportError, Exportable}; use crate::js::exports::{ExportError, Exportable};
use crate::externals::Extern; use crate::js::externals::Extern;
use crate::store::Store; use crate::js::store::Store;
use crate::{MemoryType, MemoryView}; use crate::js::{MemoryType, MemoryView};
use std::convert::TryInto; use std::convert::TryInto;
use thiserror::Error; use thiserror::Error;
@@ -83,7 +83,7 @@ impl Memory {
/// Creates a new host `Memory` from the provided [`MemoryType`]. /// Creates a new host `Memory` from the provided [`MemoryType`].
/// ///
/// This function will construct the `Memory` using the store /// This function will construct the `Memory` using the store
/// [`BaseTunables`][crate::tunables::BaseTunables]. /// [`BaseTunables`][crate::js::tunables::BaseTunables].
/// ///
/// # Example /// # Example
/// ///

View File

@@ -11,10 +11,10 @@ pub use self::global::Global;
pub use self::memory::{Memory, MemoryError}; pub use self::memory::{Memory, MemoryError};
pub use self::table::Table; pub use self::table::Table;
use crate::export::Export; use crate::js::export::Export;
use crate::exports::{ExportError, Exportable}; use crate::js::exports::{ExportError, Exportable};
use crate::store::{Store, StoreObject}; use crate::js::store::{Store, StoreObject};
use crate::ExternType; use crate::js::ExternType;
use std::fmt; use std::fmt;
/// An `Extern` is the runtime representation of an entity that /// An `Extern` is the runtime representation of an entity that

View File

@@ -1,11 +1,11 @@
use crate::export::VMFunction; use crate::js::export::VMFunction;
use crate::export::{Export, VMTable}; use crate::js::export::{Export, VMTable};
use crate::exports::{ExportError, Exportable}; use crate::js::exports::{ExportError, Exportable};
use crate::externals::{Extern, Function as WasmerFunction}; use crate::js::externals::{Extern, Function as WasmerFunction};
use crate::store::Store; use crate::js::store::Store;
use crate::types::Val; use crate::js::types::Val;
use crate::RuntimeError; use crate::js::RuntimeError;
use crate::TableType; use crate::js::TableType;
use js_sys::Function; use js_sys::Function;
use wasmer_types::FunctionType; use wasmer_types::FunctionType;
@@ -42,7 +42,7 @@ impl Table {
/// All the elements in the table will be set to the `init` value. /// All the elements in the table will be set to the `init` value.
/// ///
/// This function will construct the `Table` using the store /// This function will construct the `Table` using the store
/// [`BaseTunables`][crate::tunables::BaseTunables]. /// [`BaseTunables`][crate::js::tunables::BaseTunables].
pub fn new(store: &Store, ty: TableType, init: Val) -> Result<Self, RuntimeError> { pub fn new(store: &Store, ty: TableType, init: Val) -> Result<Self, RuntimeError> {
let descriptor = js_sys::Object::new(); let descriptor = js_sys::Object::new();
js_sys::Reflect::set(&descriptor, &"initial".into(), &ty.minimum.into())?; js_sys::Reflect::set(&descriptor, &"initial".into(), &ty.minimum.into())?;

View File

@@ -1,8 +1,8 @@
//! The import module contains the implementation data structures and helper functions used to //! 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 //! manipulate and access a wasm module's imports including memories, tables, globals, and
//! functions. //! functions.
use crate::export::Export; use crate::js::export::Export;
use crate::resolver::NamedResolver; use crate::js::resolver::NamedResolver;
use std::borrow::{Borrow, BorrowMut}; use std::borrow::{Borrow, BorrowMut};
use std::collections::VecDeque; use std::collections::VecDeque;
use std::collections::{hash_map::Entry, HashMap}; use std::collections::{hash_map::Entry, HashMap};
@@ -248,9 +248,9 @@ macro_rules! import_namespace {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use crate::ChainableNamedResolver; use crate::js::ChainableNamedResolver;
use crate::Type; use crate::js::Type;
use crate::{Global, Store, Val}; use crate::js::{Global, Store, Val};
use wasm_bindgen_test::*; use wasm_bindgen_test::*;
#[wasm_bindgen_test] #[wasm_bindgen_test]
@@ -360,7 +360,7 @@ mod test {
#[wasm_bindgen_test] #[wasm_bindgen_test]
fn imports_macro_allows_trailing_comma_and_none() { fn imports_macro_allows_trailing_comma_and_none() {
use crate::Function; use crate::js::Function;
let store = Default::default(); let store = Default::default();

View File

@@ -1,11 +1,11 @@
use crate::env::HostEnvInitError; use crate::js::env::HostEnvInitError;
use crate::export::Export; use crate::js::export::Export;
use crate::exports::Exports; use crate::js::exports::Exports;
use crate::externals::Extern; use crate::js::externals::Extern;
use crate::module::Module; use crate::js::module::Module;
use crate::resolver::Resolver; use crate::js::resolver::Resolver;
use crate::store::Store; use crate::js::store::Store;
use crate::trap::RuntimeError; use crate::js::trap::RuntimeError;
use js_sys::WebAssembly; use js_sys::WebAssembly;
use std::fmt; use std::fmt;
#[cfg(feature = "std")] #[cfg(feature = "std")]
@@ -68,7 +68,7 @@ impl Instance {
/// ///
/// The [`ImportObject`] is the easiest way to provide imports to the instance. /// The [`ImportObject`] is the easiest way to provide imports to the instance.
/// ///
/// [`ImportObject`]: crate::ImportObject /// [`ImportObject`]: crate::js::ImportObject
/// ///
/// ``` /// ```
/// # use wasmer_js::{imports, Store, Module, Global, Value, Instance}; /// # use wasmer_js::{imports, Store, Module, Global, Value, Instance};

View File

@@ -109,28 +109,28 @@ mod wasm_bindgen_polyfill;
/// See the [`WasmerEnv`] trait for more information. /// See the [`WasmerEnv`] trait for more information.
pub use wasmer_derive::WasmerEnv; pub use wasmer_derive::WasmerEnv;
pub use crate::cell::WasmCell; pub use crate::js::cell::WasmCell;
pub use crate::env::{HostEnvInitError, LazyInit, WasmerEnv}; pub use crate::js::env::{HostEnvInitError, LazyInit, WasmerEnv};
pub use crate::exports::{ExportError, Exportable, Exports, ExportsIterator}; pub use crate::js::exports::{ExportError, Exportable, Exports, ExportsIterator};
pub use crate::externals::{ pub use crate::js::externals::{
Extern, FromToNativeWasmType, Function, Global, HostFunction, Memory, MemoryError, Table, Extern, FromToNativeWasmType, Function, Global, HostFunction, Memory, MemoryError, Table,
WasmTypeList, WasmTypeList,
}; };
pub use crate::import_object::{ImportObject, ImportObjectIterator, LikeNamespace}; pub use crate::js::import_object::{ImportObject, ImportObjectIterator, LikeNamespace};
pub use crate::instance::{Instance, InstantiationError}; pub use crate::js::instance::{Instance, InstantiationError};
pub use crate::module::{Module, ModuleTypeHints}; pub use crate::js::module::{Module, ModuleTypeHints};
pub use crate::native::NativeFunc; pub use crate::js::native::NativeFunc;
pub use crate::ptr::{Array, Item, WasmPtr}; pub use crate::js::ptr::{Array, Item, WasmPtr};
pub use crate::resolver::{ChainableNamedResolver, NamedResolver, NamedResolverChain, Resolver}; pub use crate::js::resolver::{ChainableNamedResolver, NamedResolver, NamedResolverChain, Resolver};
pub use crate::trap::RuntimeError; pub use crate::js::trap::RuntimeError;
pub use crate::store::{Store, StoreObject}; pub use crate::js::store::{Store, StoreObject};
pub use crate::types::{ pub use crate::js::types::{
ExportType, ExternType, FunctionType, GlobalType, ImportType, MemoryType, Mutability, ExportType, ExternType, FunctionType, GlobalType, ImportType, MemoryType, Mutability,
TableType, Val, ValType, TableType, Val, ValType,
}; };
pub use crate::types::{Val as Value, ValType as Type}; pub use crate::js::types::{Val as Value, ValType as Type};
pub use crate::utils::is_wasm; pub use crate::js::utils::is_wasm;
pub use wasmer_types::{ pub use wasmer_types::{
Atomically, Bytes, ExportIndex, GlobalInit, LocalFunctionIndex, MemoryView, Pages, ValueType, Atomically, Bytes, ExportIndex, GlobalInit, LocalFunctionIndex, MemoryView, Pages, ValueType,

View File

@@ -1,12 +1,12 @@
use crate::export::{Export, VMFunction}; use crate::js::export::{Export, VMFunction};
use crate::resolver::Resolver; use crate::js::resolver::Resolver;
use crate::store::Store; use crate::js::store::Store;
use crate::types::{ExportType, ImportType}; use crate::js::types::{ExportType, ImportType};
// use crate::InstantiationError; // use crate::js::InstantiationError;
use crate::error::CompileError; use crate::js::error::CompileError;
#[cfg(feature = "wat")] #[cfg(feature = "wat")]
use crate::error::WasmError; use crate::js::error::WasmError;
use crate::RuntimeError; use crate::js::RuntimeError;
use js_sys::{Reflect, Uint8Array, WebAssembly}; use js_sys::{Reflect, Uint8Array, WebAssembly};
use std::fmt; use std::fmt;
use std::io; use std::io;
@@ -168,7 +168,7 @@ impl Module {
// The module is now validated, so we can safely parse it's types // The module is now validated, so we can safely parse it's types
#[cfg(feature = "wasm-types-polyfill")] #[cfg(feature = "wasm-types-polyfill")]
let (type_hints, name) = { let (type_hints, name) = {
let info = crate::module_info_polyfill::translate_module(binary).unwrap(); let info = crate::js::module_info_polyfill::translate_module(binary).unwrap();
( (
Some(ModuleTypeHints { Some(ModuleTypeHints {

View File

@@ -9,10 +9,10 @@
//! ``` //! ```
use std::marker::PhantomData; use std::marker::PhantomData;
use crate::{FromToNativeWasmType, Function, RuntimeError, Store, WasmTypeList}; use crate::js::{FromToNativeWasmType, Function, RuntimeError, Store, WasmTypeList};
// use std::panic::{catch_unwind, AssertUnwindSafe}; // use std::panic::{catch_unwind, AssertUnwindSafe};
use crate::export::VMFunction; use crate::js::export::VMFunction;
use crate::types::param_from_js; use crate::js::types::param_from_js;
use js_sys::Array; use js_sys::Array;
use std::iter::FromIterator; use std::iter::FromIterator;
use wasm_bindgen::JsValue; use wasm_bindgen::JsValue;
@@ -107,14 +107,14 @@ macro_rules! impl_native_traits {
} }
#[allow(unused_parens)] #[allow(unused_parens)]
impl<'a, $( $x, )* Rets> crate::exports::ExportableWithGenerics<'a, ($( $x ),*), Rets> for NativeFunc<( $( $x ),* ), Rets> impl<'a, $( $x, )* Rets> crate::js::exports::ExportableWithGenerics<'a, ($( $x ),*), Rets> for NativeFunc<( $( $x ),* ), Rets>
where where
$( $x: FromToNativeWasmType, )* $( $x: FromToNativeWasmType, )*
Rets: WasmTypeList, Rets: WasmTypeList,
{ {
fn get_self_from_extern_with_generics(_extern: &crate::externals::Extern) -> Result<Self, crate::exports::ExportError> { fn get_self_from_extern_with_generics(_extern: &crate::js::externals::Extern) -> Result<Self, crate::js::exports::ExportError> {
use crate::exports::Exportable; use crate::js::exports::Exportable;
crate::Function::get_self_from_extern(_extern)?.native().map_err(|_| crate::exports::ExportError::IncompatibleType) crate::js::Function::get_self_from_extern(_extern)?.native().map_err(|_| crate::js::exports::ExportError::IncompatibleType)
} }
} }
}; };

View File

@@ -6,8 +6,8 @@
//! Therefore, you should use this abstraction whenever possible to avoid memory //! Therefore, you should use this abstraction whenever possible to avoid memory
//! related bugs when implementing an ABI. //! related bugs when implementing an ABI.
use crate::cell::WasmCell; use crate::js::cell::WasmCell;
use crate::{externals::Memory, FromToNativeWasmType}; use crate::js::{externals::Memory, FromToNativeWasmType};
use std::{fmt, marker::PhantomData, mem}; use std::{fmt, marker::PhantomData, mem};
use wasmer_types::ValueType; use wasmer_types::ValueType;
@@ -246,7 +246,7 @@ impl<T: Copy, Ty> fmt::Debug for WasmPtr<T, Ty> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use crate::{Memory, MemoryType, Store}; use crate::js::{Memory, MemoryType, Store};
use wasm_bindgen_test::*; use wasm_bindgen_test::*;
/// Ensure that memory accesses work on the edges of memory and that out of /// Ensure that memory accesses work on the edges of memory and that out of

View File

@@ -1,4 +1,4 @@
use crate::export::Export; use crate::js::export::Export;
/// Import resolver connects imports with available exported values. /// Import resolver connects imports with available exported values.
pub trait Resolver { pub trait Resolver {

View File

@@ -1,6 +1,6 @@
use crate::externals::Function; use crate::js::externals::Function;
// use crate::store::{Store, StoreObject}; // use crate::js::store::{Store, StoreObject};
// use crate::RuntimeError; // use crate::js::RuntimeError;
use wasm_bindgen::JsValue; use wasm_bindgen::JsValue;
use wasmer_types::Value; use wasmer_types::Value;
pub use wasmer_types::{ pub use wasmer_types::{

View File

@@ -1,384 +1,24 @@
#![doc( #[cfg(all(feature = "sys", feature = "js"))]
html_logo_url = "https://github.com/wasmerio.png?size=200",
html_favicon_url = "https://wasmer.io/images/icons/favicon-32x32.png"
)]
#![deny(
missing_docs,
trivial_numeric_casts,
unused_extern_crates,
broken_intra_doc_links
)]
#![warn(unused_import_braces)]
#![cfg_attr(
feature = "cargo-clippy",
allow(clippy::new_without_default, vtable_address_comparisons)
)]
#![cfg_attr(
feature = "cargo-clippy",
warn(
clippy::float_arithmetic,
clippy::mut_mut,
clippy::nonminimal_bool,
clippy::option_map_unwrap_or,
clippy::option_map_unwrap_or_else,
clippy::print_stdout,
clippy::unicode_not_nfc,
clippy::use_self
)
)]
//! This crate contains the `wasmer` API. The `wasmer` API facilitates the efficient,
//! sandboxed execution of [WebAssembly (Wasm)][wasm] modules.
//!
//! Here's an example of the `wasmer` API in action:
//! ```
//! use wasmer::{Store, Module, Instance, Value, imports};
//!
//! fn main() -> anyhow::Result<()> {
//! let module_wat = r#"
//! (module
//! (type $t0 (func (param i32) (result i32)))
//! (func $add_one (export "add_one") (type $t0) (param $p0 i32) (result i32)
//! get_local $p0
//! i32.const 1
//! i32.add))
//! "#;
//!
//! let store = Store::default();
//! let module = Module::new(&store, &module_wat)?;
//! // The module doesn't import anything, so we create an empty import object.
//! let import_object = imports! {};
//! let instance = Instance::new(&module, &import_object)?;
//!
//! let add_one = instance.exports.get_function("add_one")?;
//! let result = add_one.call(&[Value::I32(42)])?;
//! assert_eq!(result[0], Value::I32(43));
//!
//! Ok(())
//! }
//! ```
//!
//! For more examples of using the `wasmer` API, check out the
//! [wasmer examples][wasmer-examples].
//!
//! ---------
//!
//! # Table of Contents
//!
//! - [Wasm Primitives](#wasm-primitives)
//! - [Externs](#externs)
//! - [Functions](#functions)
//! - [Memories](#memories)
//! - [Globals](#globals)
//! - [Tables](#tables)
//! - [Project Layout](#project-layout)
//! - [Engines](#engines)
//! - [Compilers](#compilers)
//! - [Features](#features)
//!
//!
//! # Wasm Primitives
//! In order to make use of the power of the `wasmer` API, it's important
//! to understand the primitives around which the API is built.
//!
//! Wasm only deals with a small number of core data types, these data
//! types can be found in the [`Value`] type.
//!
//! In addition to the core Wasm types, the core types of the API are
//! referred to as "externs".
//!
//! ## Externs
//! An [`Extern`] is a type that can be imported or exported from a Wasm
//! module.
//!
//! To import an extern, simply give it a namespace and a name with the
//! [`imports`] macro:
//!
//! ```
//! # use wasmer::{imports, Function, Memory, MemoryType, Store, ImportObject};
//! # fn imports_example(store: &Store) -> ImportObject {
//! let memory = Memory::new(&store, MemoryType::new(1, None, false)).unwrap();
//! imports! {
//! "env" => {
//! "my_function" => Function::new_native(store, || println!("Hello")),
//! "memory" => memory,
//! }
//! }
//! # }
//! ```
//!
//! And to access an exported extern, see the [`Exports`] API, accessible
//! from any instance via `instance.exports`:
//!
//! ```
//! # use wasmer::{imports, Instance, Function, Memory, NativeFunc};
//! # fn exports_example(instance: &Instance) -> anyhow::Result<()> {
//! let memory = instance.exports.get_memory("memory")?;
//! let memory: &Memory = instance.exports.get("some_other_memory")?;
//! let add: NativeFunc<(i32, i32), i32> = instance.exports.get_native_function("add")?;
//! let result = add.call(5, 37)?;
//! assert_eq!(result, 42);
//! # Ok(())
//! # }
//! ```
//!
//! These are the primary types that the `wasmer` API uses.
//!
//! ### Functions
//! There are 2 types of functions in `wasmer`:
//! 1. Wasm functions
//! 2. Host functions
//!
//! A Wasm function is a function defined in a WebAssembly module that can
//! only perform computation without side effects and call other functions.
//!
//! Wasm functions take 0 or more arguments and return 0 or more results.
//! Wasm functions can only deal with the primitive types defined in
//! [`Value`].
//!
//! A Host function is any function implemented on the host, in this case in
//! Rust.
//!
//! Host functions can optionally be created with an environment that
//! implements [`WasmerEnv`]. This environment is useful for maintaining
//! host state (for example the filesystem in WASI).
//!
//! Thus WebAssembly modules by themselves cannot do anything but computation
//! on the core types in [`Value`]. In order to make them more useful we
//! give them access to the outside world with [`imports`].
//!
//! If you're looking for a sandboxed, POSIX-like environment to execute Wasm
//! in, check out the [`wasmer-wasi`][wasmer-wasi] crate for our implementation of WASI,
//! the WebAssembly System Interface.
//!
//! In the `wasmer` API we support functions which take their arguments and
//! return their results dynamically, [`Function`], and functions which
//! take their arguments and return their results statically, [`NativeFunc`].
//!
//! ### Memories
//! Memories store data.
//!
//! In most Wasm programs, nearly all data will live in a [`Memory`].
//!
//! This data can be shared between the host and guest to allow for more
//! interesting programs.
//!
//! ### Globals
//! A [`Global`] is a type that may be either mutable or immutable, and
//! contains one of the core Wasm types defined in [`Value`].
//!
//! ### Tables
//! A [`Table`] is an indexed list of items.
//!
//!
//! ## Project Layout
//!
//! The Wasmer project is divided into a number of crates, below is a dependency
//! graph with transitive dependencies removed.
//!
//! <div>
//! <img src="https://raw.githubusercontent.com/wasmerio/wasmer/master/docs/deps_dedup.svg" />
//! </div>
//!
//! While this crate is the top level API, we also publish crates built
//! on top of this API that you may be interested in using, including:
//!
//! - [wasmer-cache][] for caching compiled Wasm modules.
//! - [wasmer-emscripten][] for running Wasm modules compiled to the
//! Emscripten ABI.
//! - [wasmer-wasi][] for running Wasm modules compiled to the WASI ABI.
//!
//! --------
//!
//! The Wasmer project has two major abstractions:
//! 1. [Engines][wasmer-engine]
//! 2. [Compilers][wasmer-compiler]
//!
//! These two abstractions have multiple options that can be enabled
//! with features.
//!
//! ### Engines
//!
//! An engine is a system that uses a compiler to make a WebAssembly
//! module executable.
//!
//! ### Compilers
//!
//! A compiler is a system that handles the details of making a Wasm
//! module executable. For example, by generating native machine code
//! for each Wasm function.
//!
//!
//! ## Features
//!
//! This crate's features can be broken down into 2 kinds, features that
//! enable new functionality and features that set defaults.
//!
//! The features that enable new functionality are:
//! - `universal` - enable the Universal engine. (See [wasmer-universal][])
//! - `native` - enable the native engine. (See [wasmer-native][])
//! - `cranelift` - enable Wasmer's Cranelift compiler. (See [wasmer-cranelift][])
//! - `llvm` - enable Wasmer's LLVM compiler. (See [wasmer-llvm][])
//! - `singlepass` - enable Wasmer's Singlepass compiler. (See [wasmer-singlepass][])
//! - `wat` - enable `wasmer` to parse the WebAssembly text format.
//!
//! The features that set defaults come in sets that are mutually exclusive.
//!
//! The first set is the default compiler set:
//! - `default-cranelift` - set Wasmer's Cranelift compiler as the default.
//! - `default-llvm` - set Wasmer's LLVM compiler as the default.
//! - `default-singlepass` - set Wasmer's Singlepass compiler as the default.
//!
//! The next set is the default engine set:
//! - `default-universal` - set the Universal engine as the default.
//! - `default-native` - set the native engine as the default.
//!
//! --------
//!
//! By default the `wat`, `default-cranelift`, and `default-universal` features
//! are enabled.
//!
//!
//!
//! [wasm]: https://webassembly.org/
//! [wasmer-examples]: https://github.com/wasmerio/wasmer/tree/master/examples
//! [wasmer-cache]: https://docs.rs/wasmer-cache/*/wasmer_cache/
//! [wasmer-compiler]: https://docs.rs/wasmer-compiler/*/wasmer_compiler/
//! [wasmer-cranelift]: https://docs.rs/wasmer-compiler-cranelift/*/wasmer_compiler_cranelift/
//! [wasmer-emscripten]: https://docs.rs/wasmer-emscripten/*/wasmer_emscripten/
//! [wasmer-engine]: https://docs.rs/wasmer-engine/*/wasmer_engine/
//! [wasmer-universal]: https://docs.rs/wasmer-engine-universal/*/wasmer_engine_universal/
//! [wasmer-native]: https://docs.rs/wasmer-engine-dylib/*/wasmer_engine_dylib/
//! [wasmer-singlepass]: https://docs.rs/wasmer-compiler-singlepass/*/wasmer_compiler_singlepass/
//! [wasmer-llvm]: https://docs.rs/wasmer-compiler-llvm/*/wasmer_compiler_llvm/
//! [wasmer-wasi]: https://docs.rs/wasmer-wasi/*/wasmer_wasi/
mod cell;
mod env;
mod exports;
mod externals;
mod import_object;
mod instance;
mod module;
mod native;
mod ptr;
mod store;
mod tunables;
mod types;
mod utils;
/// Implement [`WasmerEnv`] for your type with `#[derive(WasmerEnv)]`.
///
/// See the [`WasmerEnv`] trait for more information.
pub use wasmer_derive::WasmerEnv;
#[doc(hidden)]
pub mod internals {
//! We use the internals module for exporting types that are only
//! intended to use in internal crates such as the compatibility crate
//! `wasmer-vm`. Please don't use any of this types directly, as
//! they might change frequently or be removed in the future.
pub use crate::externals::{WithEnv, WithoutEnv};
}
pub use crate::cell::WasmCell;
pub use crate::env::{HostEnvInitError, LazyInit, WasmerEnv};
pub use crate::exports::{ExportError, Exportable, Exports, ExportsIterator};
pub use crate::externals::{
Extern, FromToNativeWasmType, Function, Global, HostFunction, Memory, Table, WasmTypeList,
};
pub use crate::import_object::{ImportObject, ImportObjectIterator, LikeNamespace};
pub use crate::instance::{Instance, InstantiationError};
pub use crate::module::Module;
pub use crate::native::NativeFunc;
pub use crate::ptr::{Array, Item, WasmPtr};
pub use crate::store::{Store, StoreObject};
pub use crate::tunables::BaseTunables;
pub use crate::types::{
ExportType, ExternType, FunctionType, GlobalType, ImportType, MemoryType, Mutability,
TableType, Val, ValType,
};
pub use crate::types::{Val as Value, ValType as Type};
pub use crate::utils::is_wasm;
pub use target_lexicon::{Architecture, CallingConvention, OperatingSystem, Triple, HOST};
#[cfg(feature = "compiler")]
pub use wasmer_compiler::{
wasmparser, CompilerConfig, FunctionMiddleware, MiddlewareError, MiddlewareReaderState,
ModuleMiddleware,
};
pub use wasmer_compiler::{
CompileError, CpuFeature, Features, ParseCpuFeatureError, Target, WasmError, WasmResult,
};
pub use wasmer_engine::{
ChainableNamedResolver, DeserializeError, Engine, Export, FrameInfo, LinkError, NamedResolver,
NamedResolverChain, Resolver, RuntimeError, SerializeError, Tunables,
};
#[cfg(feature = "experimental-reference-types-extern-ref")]
pub use wasmer_types::ExternRef;
pub use wasmer_types::{
Atomically, Bytes, ExportIndex, GlobalInit, LocalFunctionIndex, MemoryView, Pages, ValueType,
WASM_MAX_PAGES, WASM_MIN_PAGES, WASM_PAGE_SIZE,
};
// TODO: should those be moved into wasmer::vm as well?
pub use wasmer_vm::{raise_user_trap, MemoryError};
pub mod vm {
//! The vm module re-exports wasmer-vm types.
pub use wasmer_vm::{
Memory, MemoryError, MemoryStyle, Table, TableStyle, VMExtern, VMMemoryDefinition,
VMTableDefinition,
};
}
#[cfg(feature = "wat")]
pub use wat::parse_bytes as wat2wasm;
// The compilers are mutually exclusive
#[cfg(any(
all(
feature = "default-llvm",
any(feature = "default-cranelift", feature = "default-singlepass")
),
all(feature = "default-cranelift", feature = "default-singlepass")
))]
compile_error!( compile_error!(
r#"The `default-singlepass`, `default-cranelift` and `default-llvm` features are mutually exclusive. "Cannot have both `sys` and `js` features enabled at the same time. Please, pick one."
If you wish to use more than one compiler, you can simply create the own store. Eg.:
```
use wasmer::{Store, Universal, Singlepass};
let engine = Universal::new(Singlepass::default()).engine();
let store = Store::new(&engine);
```"#
); );
#[cfg(feature = "singlepass")] #[cfg(all(feature = "sys", target_arch = "wasm32"))]
pub use wasmer_compiler_singlepass::Singlepass; compile_error!("The `sys` feature must be enabled only for non-`wasm32` target.");
#[cfg(feature = "cranelift")] #[cfg(all(feature = "js", not(target_arch = "wasm32")))]
pub use wasmer_compiler_cranelift::{Cranelift, CraneliftOptLevel}; compile_error!(
"The `js` feature must be enabled only for the `wasm32` target (either `wasm32-unknown-unknown` or `wasm32-wasi`)."
);
#[cfg(feature = "llvm")] #[cfg(feature = "sys")]
pub use wasmer_compiler_llvm::{LLVMOptLevel, LLVM}; mod sys;
#[cfg(feature = "universal")] #[cfg(feature = "sys")]
pub use wasmer_engine_universal::{Universal, UniversalArtifact, UniversalEngine}; pub use sys::*;
#[cfg(feature = "dylib")] #[cfg(feature = "js")]
pub use wasmer_engine_dylib::{Dylib, DylibArtifact, DylibEngine}; mod js;
/// Version number of this crate. #[cfg(feature = "js")]
pub const VERSION: &str = env!("CARGO_PKG_VERSION"); pub use js::*;
/// The Deprecated JIT Engine (please use `Universal` instead)
#[cfg(feature = "jit")]
#[deprecated(since = "2.0.0", note = "Please use the `universal` feature instead")]
pub type JIT = Universal;
/// The Deprecated Native Engine (please use `Dylib` instead)
#[cfg(feature = "native")]
#[deprecated(since = "2.0.0", note = "Please use the `native` feature instead")]
pub type Native = Dylib;

View File

@@ -1,4 +1,4 @@
use crate::{ExportError, Instance}; use crate::sys::{ExportError, Instance};
use thiserror::Error; use thiserror::Error;
/// An error while initializing the user supplied host env with the `WasmerEnv` trait. /// An error while initializing the user supplied host env with the `WasmerEnv` trait.

View File

@@ -1,7 +1,7 @@
use crate::externals::{Extern, Function, Global, Memory, Table}; use crate::sys::externals::{Extern, Function, Global, Memory, Table};
use crate::import_object::LikeNamespace; use crate::sys::import_object::LikeNamespace;
use crate::native::NativeFunc; use crate::sys::native::NativeFunc;
use crate::WasmTypeList; use crate::sys::WasmTypeList;
use indexmap::IndexMap; use indexmap::IndexMap;
use loupe::MemoryUsage; use loupe::MemoryUsage;
use std::fmt; use std::fmt;

View File

@@ -1,11 +1,11 @@
use crate::exports::{ExportError, Exportable}; use crate::sys::exports::{ExportError, Exportable};
use crate::externals::Extern; use crate::sys::externals::Extern;
use crate::store::Store; use crate::sys::store::Store;
use crate::types::{Val, ValFuncRef}; use crate::sys::types::{Val, ValFuncRef};
use crate::FunctionType; use crate::sys::FunctionType;
use crate::NativeFunc; use crate::sys::NativeFunc;
use crate::RuntimeError; use crate::sys::RuntimeError;
use crate::WasmerEnv; use crate::sys::WasmerEnv;
pub use inner::{FromToNativeWasmType, HostFunction, WasmTypeList, WithEnv, WithoutEnv}; pub use inner::{FromToNativeWasmType, HostFunction, WasmTypeList, WithEnv, WithoutEnv};
use loupe::MemoryUsage; use loupe::MemoryUsage;

View File

@@ -1,10 +1,10 @@
use crate::exports::{ExportError, Exportable}; use crate::sys::exports::{ExportError, Exportable};
use crate::externals::Extern; use crate::sys::externals::Extern;
use crate::store::{Store, StoreObject}; use crate::sys::store::{Store, StoreObject};
use crate::types::Val; use crate::sys::types::Val;
use crate::GlobalType; use crate::sys::GlobalType;
use crate::Mutability; use crate::sys::Mutability;
use crate::RuntimeError; use crate::sys::RuntimeError;
use loupe::MemoryUsage; use loupe::MemoryUsage;
use std::fmt; use std::fmt;
use std::sync::Arc; use std::sync::Arc;

View File

@@ -1,7 +1,7 @@
use crate::exports::{ExportError, Exportable}; use crate::sys::exports::{ExportError, Exportable};
use crate::externals::Extern; use crate::sys::externals::Extern;
use crate::store::Store; use crate::sys::store::Store;
use crate::{MemoryType, MemoryView}; use crate::sys::{MemoryType, MemoryView};
use loupe::MemoryUsage; use loupe::MemoryUsage;
use std::convert::TryInto; use std::convert::TryInto;
use std::slice; use std::slice;

View File

@@ -11,9 +11,9 @@ pub use self::global::Global;
pub use self::memory::Memory; pub use self::memory::Memory;
pub use self::table::Table; pub use self::table::Table;
use crate::exports::{ExportError, Exportable}; use crate::sys::exports::{ExportError, Exportable};
use crate::store::{Store, StoreObject}; use crate::sys::store::{Store, StoreObject};
use crate::ExternType; use crate::sys::ExternType;
use loupe::MemoryUsage; use loupe::MemoryUsage;
use std::fmt; use std::fmt;
use wasmer_engine::Export; use wasmer_engine::Export;

View File

@@ -1,9 +1,9 @@
use crate::exports::{ExportError, Exportable}; use crate::sys::exports::{ExportError, Exportable};
use crate::externals::Extern; use crate::sys::externals::Extern;
use crate::store::Store; use crate::sys::store::Store;
use crate::types::{Val, ValFuncRef}; use crate::sys::types::{Val, ValFuncRef};
use crate::RuntimeError; use crate::sys::RuntimeError;
use crate::TableType; use crate::sys::TableType;
use loupe::MemoryUsage; use loupe::MemoryUsage;
use std::sync::Arc; use std::sync::Arc;
use wasmer_engine::Export; use wasmer_engine::Export;

View File

@@ -247,7 +247,7 @@ macro_rules! import_namespace {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use crate::{Global, Store, Val}; use crate::sys::{Global, Store, Val};
use wasmer_engine::ChainableNamedResolver; use wasmer_engine::ChainableNamedResolver;
use wasmer_types::Type; use wasmer_types::Type;
@@ -358,7 +358,7 @@ mod test {
#[test] #[test]
fn imports_macro_allows_trailing_comma_and_none() { fn imports_macro_allows_trailing_comma_and_none() {
use crate::Function; use crate::sys::Function;
let store = Default::default(); let store = Default::default();

View File

@@ -1,8 +1,8 @@
use crate::exports::Exports; use crate::sys::exports::Exports;
use crate::externals::Extern; use crate::sys::externals::Extern;
use crate::module::Module; use crate::sys::module::Module;
use crate::store::Store; use crate::sys::store::Store;
use crate::{HostEnvInitError, LinkError, RuntimeError}; use crate::sys::{HostEnvInitError, LinkError, RuntimeError};
use loupe::MemoryUsage; use loupe::MemoryUsage;
use std::fmt; use std::fmt;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};

384
lib/api/src/sys/mod.rs Normal file
View File

@@ -0,0 +1,384 @@
#![doc(
html_logo_url = "https://github.com/wasmerio.png?size=200",
html_favicon_url = "https://wasmer.io/images/icons/favicon-32x32.png"
)]
#![deny(
missing_docs,
trivial_numeric_casts,
unused_extern_crates,
broken_intra_doc_links
)]
#![warn(unused_import_braces)]
#![cfg_attr(
feature = "cargo-clippy",
allow(clippy::new_without_default, vtable_address_comparisons)
)]
#![cfg_attr(
feature = "cargo-clippy",
warn(
clippy::float_arithmetic,
clippy::mut_mut,
clippy::nonminimal_bool,
clippy::option_map_unwrap_or,
clippy::option_map_unwrap_or_else,
clippy::print_stdout,
clippy::unicode_not_nfc,
clippy::use_self
)
)]
//! This crate contains the `wasmer` API. The `wasmer` API facilitates the efficient,
//! sandboxed execution of [WebAssembly (Wasm)][wasm] modules.
//!
//! Here's an example of the `wasmer` API in action:
//! ```
//! use wasmer::{Store, Module, Instance, Value, imports};
//!
//! fn main() -> anyhow::Result<()> {
//! let module_wat = r#"
//! (module
//! (type $t0 (func (param i32) (result i32)))
//! (func $add_one (export "add_one") (type $t0) (param $p0 i32) (result i32)
//! get_local $p0
//! i32.const 1
//! i32.add))
//! "#;
//!
//! let store = Store::default();
//! let module = Module::new(&store, &module_wat)?;
//! // The module doesn't import anything, so we create an empty import object.
//! let import_object = imports! {};
//! let instance = Instance::new(&module, &import_object)?;
//!
//! let add_one = instance.exports.get_function("add_one")?;
//! let result = add_one.call(&[Value::I32(42)])?;
//! assert_eq!(result[0], Value::I32(43));
//!
//! Ok(())
//! }
//! ```
//!
//! For more examples of using the `wasmer` API, check out the
//! [wasmer examples][wasmer-examples].
//!
//! ---------
//!
//! # Table of Contents
//!
//! - [Wasm Primitives](#wasm-primitives)
//! - [Externs](#externs)
//! - [Functions](#functions)
//! - [Memories](#memories)
//! - [Globals](#globals)
//! - [Tables](#tables)
//! - [Project Layout](#project-layout)
//! - [Engines](#engines)
//! - [Compilers](#compilers)
//! - [Features](#features)
//!
//!
//! # Wasm Primitives
//! In order to make use of the power of the `wasmer` API, it's important
//! to understand the primitives around which the API is built.
//!
//! Wasm only deals with a small number of core data types, these data
//! types can be found in the [`Value`] type.
//!
//! In addition to the core Wasm types, the core types of the API are
//! referred to as "externs".
//!
//! ## Externs
//! An [`Extern`] is a type that can be imported or exported from a Wasm
//! module.
//!
//! To import an extern, simply give it a namespace and a name with the
//! [`imports`] macro:
//!
//! ```
//! # use wasmer::{imports, Function, Memory, MemoryType, Store, ImportObject};
//! # fn imports_example(store: &Store) -> ImportObject {
//! let memory = Memory::new(&store, MemoryType::new(1, None, false)).unwrap();
//! imports! {
//! "env" => {
//! "my_function" => Function::new_native(store, || println!("Hello")),
//! "memory" => memory,
//! }
//! }
//! # }
//! ```
//!
//! And to access an exported extern, see the [`Exports`] API, accessible
//! from any instance via `instance.exports`:
//!
//! ```
//! # use wasmer::{imports, Instance, Function, Memory, NativeFunc};
//! # fn exports_example(instance: &Instance) -> anyhow::Result<()> {
//! let memory = instance.exports.get_memory("memory")?;
//! let memory: &Memory = instance.exports.get("some_other_memory")?;
//! let add: NativeFunc<(i32, i32), i32> = instance.exports.get_native_function("add")?;
//! let result = add.call(5, 37)?;
//! assert_eq!(result, 42);
//! # Ok(())
//! # }
//! ```
//!
//! These are the primary types that the `wasmer` API uses.
//!
//! ### Functions
//! There are 2 types of functions in `wasmer`:
//! 1. Wasm functions
//! 2. Host functions
//!
//! A Wasm function is a function defined in a WebAssembly module that can
//! only perform computation without side effects and call other functions.
//!
//! Wasm functions take 0 or more arguments and return 0 or more results.
//! Wasm functions can only deal with the primitive types defined in
//! [`Value`].
//!
//! A Host function is any function implemented on the host, in this case in
//! Rust.
//!
//! Host functions can optionally be created with an environment that
//! implements [`WasmerEnv`]. This environment is useful for maintaining
//! host state (for example the filesystem in WASI).
//!
//! Thus WebAssembly modules by themselves cannot do anything but computation
//! on the core types in [`Value`]. In order to make them more useful we
//! give them access to the outside world with [`imports`].
//!
//! If you're looking for a sandboxed, POSIX-like environment to execute Wasm
//! in, check out the [`wasmer-wasi`][wasmer-wasi] crate for our implementation of WASI,
//! the WebAssembly System Interface.
//!
//! In the `wasmer` API we support functions which take their arguments and
//! return their results dynamically, [`Function`], and functions which
//! take their arguments and return their results statically, [`NativeFunc`].
//!
//! ### Memories
//! Memories store data.
//!
//! In most Wasm programs, nearly all data will live in a [`Memory`].
//!
//! This data can be shared between the host and guest to allow for more
//! interesting programs.
//!
//! ### Globals
//! A [`Global`] is a type that may be either mutable or immutable, and
//! contains one of the core Wasm types defined in [`Value`].
//!
//! ### Tables
//! A [`Table`] is an indexed list of items.
//!
//!
//! ## Project Layout
//!
//! The Wasmer project is divided into a number of crates, below is a dependency
//! graph with transitive dependencies removed.
//!
//! <div>
//! <img src="https://raw.githubusercontent.com/wasmerio/wasmer/master/docs/deps_dedup.svg" />
//! </div>
//!
//! While this crate is the top level API, we also publish crates built
//! on top of this API that you may be interested in using, including:
//!
//! - [wasmer-cache][] for caching compiled Wasm modules.
//! - [wasmer-emscripten][] for running Wasm modules compiled to the
//! Emscripten ABI.
//! - [wasmer-wasi][] for running Wasm modules compiled to the WASI ABI.
//!
//! --------
//!
//! The Wasmer project has two major abstractions:
//! 1. [Engines][wasmer-engine]
//! 2. [Compilers][wasmer-compiler]
//!
//! These two abstractions have multiple options that can be enabled
//! with features.
//!
//! ### Engines
//!
//! An engine is a system that uses a compiler to make a WebAssembly
//! module executable.
//!
//! ### Compilers
//!
//! A compiler is a system that handles the details of making a Wasm
//! module executable. For example, by generating native machine code
//! for each Wasm function.
//!
//!
//! ## Features
//!
//! This crate's features can be broken down into 2 kinds, features that
//! enable new functionality and features that set defaults.
//!
//! The features that enable new functionality are:
//! - `universal` - enable the Universal engine. (See [wasmer-universal][])
//! - `native` - enable the native engine. (See [wasmer-native][])
//! - `cranelift` - enable Wasmer's Cranelift compiler. (See [wasmer-cranelift][])
//! - `llvm` - enable Wasmer's LLVM compiler. (See [wasmer-llvm][])
//! - `singlepass` - enable Wasmer's Singlepass compiler. (See [wasmer-singlepass][])
//! - `wat` - enable `wasmer` to parse the WebAssembly text format.
//!
//! The features that set defaults come in sets that are mutually exclusive.
//!
//! The first set is the default compiler set:
//! - `default-cranelift` - set Wasmer's Cranelift compiler as the default.
//! - `default-llvm` - set Wasmer's LLVM compiler as the default.
//! - `default-singlepass` - set Wasmer's Singlepass compiler as the default.
//!
//! The next set is the default engine set:
//! - `default-universal` - set the Universal engine as the default.
//! - `default-native` - set the native engine as the default.
//!
//! --------
//!
//! By default the `wat`, `default-cranelift`, and `default-universal` features
//! are enabled.
//!
//!
//!
//! [wasm]: https://webassembly.org/
//! [wasmer-examples]: https://github.com/wasmerio/wasmer/tree/master/examples
//! [wasmer-cache]: https://docs.rs/wasmer-cache/*/wasmer_cache/
//! [wasmer-compiler]: https://docs.rs/wasmer-compiler/*/wasmer_compiler/
//! [wasmer-cranelift]: https://docs.rs/wasmer-compiler-cranelift/*/wasmer_compiler_cranelift/
//! [wasmer-emscripten]: https://docs.rs/wasmer-emscripten/*/wasmer_emscripten/
//! [wasmer-engine]: https://docs.rs/wasmer-engine/*/wasmer_engine/
//! [wasmer-universal]: https://docs.rs/wasmer-engine-universal/*/wasmer_engine_universal/
//! [wasmer-native]: https://docs.rs/wasmer-engine-dylib/*/wasmer_engine_dylib/
//! [wasmer-singlepass]: https://docs.rs/wasmer-compiler-singlepass/*/wasmer_compiler_singlepass/
//! [wasmer-llvm]: https://docs.rs/wasmer-compiler-llvm/*/wasmer_compiler_llvm/
//! [wasmer-wasi]: https://docs.rs/wasmer-wasi/*/wasmer_wasi/
mod cell;
mod env;
mod exports;
mod externals;
mod import_object;
mod instance;
mod module;
mod native;
mod ptr;
mod store;
mod tunables;
mod types;
mod utils;
/// Implement [`WasmerEnv`] for your type with `#[derive(WasmerEnv)]`.
///
/// See the [`WasmerEnv`] trait for more information.
pub use wasmer_derive::WasmerEnv;
#[doc(hidden)]
pub mod internals {
//! We use the internals module for exporting types that are only
//! intended to use in internal crates such as the compatibility crate
//! `wasmer-vm`. Please don't use any of this types directly, as
//! they might change frequently or be removed in the future.
pub use crate::sys::externals::{WithEnv, WithoutEnv};
}
pub use crate::sys::cell::WasmCell;
pub use crate::sys::env::{HostEnvInitError, LazyInit, WasmerEnv};
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::instance::{Instance, InstantiationError};
pub use crate::sys::module::Module;
pub use crate::sys::native::NativeFunc;
pub use crate::sys::ptr::{Array, Item, WasmPtr};
pub use crate::sys::store::{Store, StoreObject};
pub use crate::sys::tunables::BaseTunables;
pub use crate::sys::types::{
ExportType, ExternType, FunctionType, GlobalType, ImportType, MemoryType, Mutability,
TableType, Val, ValType,
};
pub use crate::sys::types::{Val as Value, ValType as Type};
pub use crate::sys::utils::is_wasm;
pub use target_lexicon::{Architecture, CallingConvention, OperatingSystem, Triple, HOST};
#[cfg(feature = "compiler")]
pub use wasmer_compiler::{
wasmparser, CompilerConfig, FunctionMiddleware, MiddlewareError, MiddlewareReaderState,
ModuleMiddleware,
};
pub use wasmer_compiler::{
CompileError, CpuFeature, Features, ParseCpuFeatureError, Target, WasmError, WasmResult,
};
pub use wasmer_engine::{
ChainableNamedResolver, DeserializeError, Engine, Export, FrameInfo, LinkError, NamedResolver,
NamedResolverChain, Resolver, RuntimeError, SerializeError, Tunables,
};
#[cfg(feature = "experimental-reference-types-extern-ref")]
pub use wasmer_types::ExternRef;
pub use wasmer_types::{
Atomically, Bytes, ExportIndex, GlobalInit, LocalFunctionIndex, MemoryView, Pages, ValueType,
WASM_MAX_PAGES, WASM_MIN_PAGES, WASM_PAGE_SIZE,
};
// TODO: should those be moved into wasmer::vm as well?
pub use wasmer_vm::{raise_user_trap, MemoryError};
pub mod vm {
//! The vm module re-exports wasmer-vm types.
pub use wasmer_vm::{
Memory, MemoryError, MemoryStyle, Table, TableStyle, VMExtern, VMMemoryDefinition,
VMTableDefinition,
};
}
#[cfg(feature = "wat")]
pub use wat::parse_bytes as wat2wasm;
// The compilers are mutually exclusive
#[cfg(any(
all(
feature = "default-llvm",
any(feature = "default-cranelift", feature = "default-singlepass")
),
all(feature = "default-cranelift", feature = "default-singlepass")
))]
compile_error!(
r#"The `default-singlepass`, `default-cranelift` and `default-llvm` features are mutually exclusive.
If you wish to use more than one compiler, you can simply create the own store. Eg.:
```
use wasmer::{Store, Universal, Singlepass};
let engine = Universal::new(Singlepass::default()).engine();
let store = Store::new(&engine);
```"#
);
#[cfg(feature = "singlepass")]
pub use wasmer_compiler_singlepass::Singlepass;
#[cfg(feature = "cranelift")]
pub use wasmer_compiler_cranelift::{Cranelift, CraneliftOptLevel};
#[cfg(feature = "llvm")]
pub use wasmer_compiler_llvm::{LLVMOptLevel, LLVM};
#[cfg(feature = "universal")]
pub use wasmer_engine_universal::{Universal, UniversalArtifact, UniversalEngine};
#[cfg(feature = "dylib")]
pub use wasmer_engine_dylib::{Dylib, DylibArtifact, DylibEngine};
/// Version number of this crate.
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
/// The Deprecated JIT Engine (please use `Universal` instead)
#[cfg(feature = "jit")]
#[deprecated(since = "2.0.0", note = "Please use the `universal` feature instead")]
pub type JIT = Universal;
/// The Deprecated Native Engine (please use `Dylib` instead)
#[cfg(feature = "native")]
#[deprecated(since = "2.0.0", note = "Please use the `native` feature instead")]
pub type Native = Dylib;

View File

@@ -1,6 +1,6 @@
use crate::store::Store; use crate::sys::store::Store;
use crate::types::{ExportType, ImportType}; use crate::sys::types::{ExportType, ImportType};
use crate::InstantiationError; use crate::sys::InstantiationError;
use loupe::MemoryUsage; use loupe::MemoryUsage;
use std::fmt; use std::fmt;
use std::io; use std::io;

View File

@@ -9,8 +9,8 @@
//! ``` //! ```
use std::marker::PhantomData; use std::marker::PhantomData;
use crate::externals::function::{DynamicFunction, VMDynamicFunction}; use crate::sys::externals::function::{DynamicFunction, VMDynamicFunction};
use crate::{FromToNativeWasmType, Function, RuntimeError, Store, WasmTypeList}; use crate::sys::{FromToNativeWasmType, Function, RuntimeError, Store, WasmTypeList};
use std::panic::{catch_unwind, AssertUnwindSafe}; use std::panic::{catch_unwind, AssertUnwindSafe};
use wasmer_engine::ExportFunction; use wasmer_engine::ExportFunction;
use wasmer_types::NativeWasmType; use wasmer_types::NativeWasmType;
@@ -223,14 +223,14 @@ macro_rules! impl_native_traits {
} }
#[allow(unused_parens)] #[allow(unused_parens)]
impl<'a, $( $x, )* Rets> crate::exports::ExportableWithGenerics<'a, ($( $x ),*), Rets> for NativeFunc<( $( $x ),* ), Rets> impl<'a, $( $x, )* Rets> crate::sys::exports::ExportableWithGenerics<'a, ($( $x ),*), Rets> for NativeFunc<( $( $x ),* ), Rets>
where where
$( $x: FromToNativeWasmType, )* $( $x: FromToNativeWasmType, )*
Rets: WasmTypeList, Rets: WasmTypeList,
{ {
fn get_self_from_extern_with_generics(_extern: &crate::externals::Extern) -> Result<Self, crate::exports::ExportError> { fn get_self_from_extern_with_generics(_extern: &crate::sys::externals::Extern) -> Result<Self, crate::sys::exports::ExportError> {
use crate::exports::Exportable; use crate::sys::exports::Exportable;
crate::Function::get_self_from_extern(_extern)?.native().map_err(|_| crate::exports::ExportError::IncompatibleType) crate::Function::get_self_from_extern(_extern)?.native().map_err(|_| crate::sys::exports::ExportError::IncompatibleType)
} }
fn into_weak_instance_ref(&mut self) { fn into_weak_instance_ref(&mut self) {

View File

@@ -6,8 +6,8 @@
//! Therefore, you should use this abstraction whenever possible to avoid memory //! Therefore, you should use this abstraction whenever possible to avoid memory
//! related bugs when implementing an ABI. //! related bugs when implementing an ABI.
use crate::cell::WasmCell; use crate::sys::cell::WasmCell;
use crate::{externals::Memory, FromToNativeWasmType}; use crate::sys::{externals::Memory, FromToNativeWasmType};
use std::{cell::Cell, fmt, marker::PhantomData, mem}; use std::{cell::Cell, fmt, marker::PhantomData, mem};
use wasmer_types::ValueType; use wasmer_types::ValueType;
@@ -294,7 +294,7 @@ impl<T: Copy, Ty> fmt::Debug for WasmPtr<T, Ty> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use crate::{Memory, MemoryType, Store}; use crate::sys::{Memory, MemoryType, Store};
/// Ensure that memory accesses work on the edges of memory and that out of /// Ensure that memory accesses work on the edges of memory and that out of
/// bounds errors are caught with `deref` /// bounds errors are caught with `deref`

View File

@@ -1,4 +1,4 @@
use crate::tunables::BaseTunables; use crate::sys::tunables::BaseTunables;
use loupe::MemoryUsage; use loupe::MemoryUsage;
use std::any::Any; use std::any::Any;
use std::fmt; use std::fmt;

View File

@@ -1,4 +1,4 @@
use crate::{MemoryType, Pages, TableType}; use crate::sys::{MemoryType, Pages, TableType};
use loupe::MemoryUsage; use loupe::MemoryUsage;
use std::cmp::min; use std::cmp::min;
use std::ptr::NonNull; use std::ptr::NonNull;

View File

@@ -1,6 +1,6 @@
use crate::externals::Function; use crate::sys::externals::Function;
use crate::store::{Store, StoreObject}; use crate::sys::store::{Store, StoreObject};
use crate::RuntimeError; use crate::sys::RuntimeError;
use wasmer_types::Value; use wasmer_types::Value;
pub use wasmer_types::{ pub use wasmer_types::{
ExportType, ExternType, FunctionType, GlobalType, ImportType, MemoryType, Mutability, ExportType, ExternType, FunctionType, GlobalType, ImportType, MemoryType, Mutability,

View File

@@ -1,334 +0,0 @@
use anyhow::Result;
use wasmer::*;
use wasmer_vm::WeakOrStrongInstanceRef;
const MEM_WAT: &str = "
(module
(func $host_fn (import \"env\" \"host_fn\") (param) (result))
(func (export \"call_host_fn\") (param) (result)
(call $host_fn))
(memory $mem 0)
(export \"memory\" (memory $mem))
)
";
const GLOBAL_WAT: &str = "
(module
(func $host_fn (import \"env\" \"host_fn\") (param) (result))
(func (export \"call_host_fn\") (param) (result)
(call $host_fn))
(global $global i32 (i32.const 11))
(export \"global\" (global $global))
)
";
const TABLE_WAT: &str = "
(module
(func $host_fn (import \"env\" \"host_fn\") (param) (result))
(func (export \"call_host_fn\") (param) (result)
(call $host_fn))
(table $table 4 4 funcref)
(export \"table\" (table $table))
)
";
const FUNCTION_WAT: &str = "
(module
(func $host_fn (import \"env\" \"host_fn\") (param) (result))
(func (export \"call_host_fn\") (param) (result)
(call $host_fn))
)
";
fn is_memory_instance_ref_strong(memory: &Memory) -> Option<bool> {
// This is safe because we're calling it from a test to test the internals
unsafe {
memory
.get_vm_memory()
.instance_ref
.as_ref()
.map(|v| matches!(v, WeakOrStrongInstanceRef::Strong(_)))
}
}
fn is_table_instance_ref_strong(table: &Table) -> Option<bool> {
// This is safe because we're calling it from a test to test the internals
unsafe {
table
.get_vm_table()
.instance_ref
.as_ref()
.map(|v| matches!(v, WeakOrStrongInstanceRef::Strong(_)))
}
}
fn is_global_instance_ref_strong(global: &Global) -> Option<bool> {
// This is safe because we're calling it from a test to test the internals
unsafe {
global
.get_vm_global()
.instance_ref
.as_ref()
.map(|v| matches!(v, WeakOrStrongInstanceRef::Strong(_)))
}
}
fn is_function_instance_ref_strong(f: &Function) -> Option<bool> {
// This is safe because we're calling it from a test to test the internals
unsafe {
f.get_vm_function()
.instance_ref
.as_ref()
.map(|v| matches!(v, WeakOrStrongInstanceRef::Strong(_)))
}
}
fn is_native_function_instance_ref_strong<Args, Rets>(f: &NativeFunc<Args, Rets>) -> Option<bool>
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
// This is safe because we're calling it from a test to test the internals
unsafe {
f.get_vm_function()
.instance_ref
.as_ref()
.map(|v| matches!(v, WeakOrStrongInstanceRef::Strong(_)))
}
}
#[test]
fn strong_weak_behavior_works_memory() -> Result<()> {
#[derive(Clone, Debug, WasmerEnv, Default)]
struct MemEnv {
#[wasmer(export)]
memory: LazyInit<Memory>,
}
let host_fn = |env: &MemEnv| {
let mem = env.memory_ref().unwrap();
assert_eq!(is_memory_instance_ref_strong(&mem), Some(false));
let mem_clone = mem.clone();
assert_eq!(is_memory_instance_ref_strong(&mem_clone), Some(true));
assert_eq!(is_memory_instance_ref_strong(&mem), Some(false));
};
let f: NativeFunc<(), ()> = {
let store = Store::default();
let module = Module::new(&store, MEM_WAT)?;
let env = MemEnv::default();
let instance = Instance::new(
&module,
&imports! {
"env" => {
"host_fn" => Function::new_native_with_env(&store, env, host_fn)
}
},
)?;
{
let mem = instance.exports.get_memory("memory")?;
assert_eq!(is_memory_instance_ref_strong(&mem), Some(true));
}
let f: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_fn")?;
f.call()?;
f
};
f.call()?;
Ok(())
}
#[test]
fn strong_weak_behavior_works_global() -> Result<()> {
#[derive(Clone, Debug, WasmerEnv, Default)]
struct GlobalEnv {
#[wasmer(export)]
global: LazyInit<Global>,
}
let host_fn = |env: &GlobalEnv| {
let global = env.global_ref().unwrap();
assert_eq!(is_global_instance_ref_strong(&global), Some(false));
let global_clone = global.clone();
assert_eq!(is_global_instance_ref_strong(&global_clone), Some(true));
assert_eq!(is_global_instance_ref_strong(&global), Some(false));
};
let f: NativeFunc<(), ()> = {
let store = Store::default();
let module = Module::new(&store, GLOBAL_WAT)?;
let env = GlobalEnv::default();
let instance = Instance::new(
&module,
&imports! {
"env" => {
"host_fn" => Function::new_native_with_env(&store, env, host_fn)
}
},
)?;
{
let global = instance.exports.get_global("global")?;
assert_eq!(is_global_instance_ref_strong(&global), Some(true));
}
let f: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_fn")?;
f.call()?;
f
};
f.call()?;
Ok(())
}
#[test]
fn strong_weak_behavior_works_table() -> Result<()> {
#[derive(Clone, WasmerEnv, Default)]
struct TableEnv {
#[wasmer(export)]
table: LazyInit<Table>,
}
let host_fn = |env: &TableEnv| {
let table = env.table_ref().unwrap();
assert_eq!(is_table_instance_ref_strong(&table), Some(false));
let table_clone = table.clone();
assert_eq!(is_table_instance_ref_strong(&table_clone), Some(true));
assert_eq!(is_table_instance_ref_strong(&table), Some(false));
};
let f: NativeFunc<(), ()> = {
let store = Store::default();
let module = Module::new(&store, TABLE_WAT)?;
let env = TableEnv::default();
let instance = Instance::new(
&module,
&imports! {
"env" => {
"host_fn" => Function::new_native_with_env(&store, env, host_fn)
}
},
)?;
{
let table = instance.exports.get_table("table")?;
assert_eq!(is_table_instance_ref_strong(&table), Some(true));
}
let f: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_fn")?;
f.call()?;
f
};
f.call()?;
Ok(())
}
#[test]
fn strong_weak_behavior_works_function() -> Result<()> {
#[derive(Clone, WasmerEnv, Default)]
struct FunctionEnv {
#[wasmer(export)]
call_host_fn: LazyInit<Function>,
}
let host_fn = |env: &FunctionEnv| {
let function = env.call_host_fn_ref().unwrap();
assert_eq!(is_function_instance_ref_strong(&function), Some(false));
let function_clone = function.clone();
assert_eq!(is_function_instance_ref_strong(&function_clone), Some(true));
assert_eq!(is_function_instance_ref_strong(&function), Some(false));
};
let f: NativeFunc<(), ()> = {
let store = Store::default();
let module = Module::new(&store, FUNCTION_WAT)?;
let env = FunctionEnv::default();
let instance = Instance::new(
&module,
&imports! {
"env" => {
"host_fn" => Function::new_native_with_env(&store, env, host_fn)
}
},
)?;
{
let function = instance.exports.get_function("call_host_fn")?;
assert_eq!(is_function_instance_ref_strong(&function), Some(true));
}
let f: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_fn")?;
f.call()?;
f
};
f.call()?;
Ok(())
}
#[test]
fn strong_weak_behavior_works_native_function() -> Result<()> {
#[derive(Clone, WasmerEnv, Default)]
struct FunctionEnv {
#[wasmer(export)]
call_host_fn: LazyInit<NativeFunc<(), ()>>,
}
let host_fn = |env: &FunctionEnv| {
let function = env.call_host_fn_ref().unwrap();
assert_eq!(
is_native_function_instance_ref_strong(&function),
Some(false)
);
let function_clone = function.clone();
assert_eq!(
is_native_function_instance_ref_strong(&function_clone),
Some(true)
);
assert_eq!(
is_native_function_instance_ref_strong(&function),
Some(false)
);
};
let f: NativeFunc<(), ()> = {
let store = Store::default();
let module = Module::new(&store, FUNCTION_WAT)?;
let env = FunctionEnv::default();
let instance = Instance::new(
&module,
&imports! {
"env" => {
"host_fn" => Function::new_native_with_env(&store, env, host_fn)
}
},
)?;
{
let function: NativeFunc<(), ()> =
instance.exports.get_native_function("call_host_fn")?;
assert_eq!(
is_native_function_instance_ref_strong(&function),
Some(true)
);
}
let f: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_fn")?;
f.call()?;
f
};
f.call()?;
Ok(())
}

View File

@@ -1,458 +0,0 @@
use anyhow::Result;
use wasmer::*;
#[test]
fn global_new() -> Result<()> {
let store = Store::default();
let global = Global::new(&store, Value::I32(10));
assert_eq!(
*global.ty(),
GlobalType {
ty: Type::I32,
mutability: Mutability::Const
}
);
let global_mut = Global::new_mut(&store, Value::I32(10));
assert_eq!(
*global_mut.ty(),
GlobalType {
ty: Type::I32,
mutability: Mutability::Var
}
);
Ok(())
}
#[test]
fn global_get() -> Result<()> {
let store = Store::default();
let global_i32 = Global::new(&store, Value::I32(10));
assert_eq!(global_i32.get(), Value::I32(10));
let global_i64 = Global::new(&store, Value::I64(20));
assert_eq!(global_i64.get(), Value::I64(20));
let global_f32 = Global::new(&store, Value::F32(10.0));
assert_eq!(global_f32.get(), Value::F32(10.0));
let global_f64 = Global::new(&store, Value::F64(20.0));
assert_eq!(global_f64.get(), Value::F64(20.0));
Ok(())
}
#[test]
fn global_set() -> Result<()> {
let store = Store::default();
let global_i32 = Global::new(&store, Value::I32(10));
// Set on a constant should error
assert!(global_i32.set(Value::I32(20)).is_err());
let global_i32_mut = Global::new_mut(&store, Value::I32(10));
// Set on different type should error
assert!(global_i32_mut.set(Value::I64(20)).is_err());
// Set on same type should succeed
global_i32_mut.set(Value::I32(20))?;
assert_eq!(global_i32_mut.get(), Value::I32(20));
Ok(())
}
#[test]
fn table_new() -> Result<()> {
let store = Store::default();
let table_type = TableType {
ty: Type::FuncRef,
minimum: 0,
maximum: None,
};
let f = Function::new_native(&store, || {});
let table = Table::new(&store, table_type, Value::FuncRef(Some(f)))?;
assert_eq!(*table.ty(), table_type);
// Anyrefs not yet supported
// let table_type = TableType {
// ty: Type::ExternRef,
// minimum: 0,
// maximum: None,
// };
// let table = Table::new(&store, table_type, Value::ExternRef(ExternRef::Null))?;
// assert_eq!(*table.ty(), table_type);
Ok(())
}
#[test]
#[ignore]
fn table_get() -> Result<()> {
let store = Store::default();
let table_type = TableType {
ty: Type::FuncRef,
minimum: 0,
maximum: Some(1),
};
let f = Function::new_native(&store, |num: i32| num + 1);
let table = Table::new(&store, table_type, Value::FuncRef(Some(f.clone())))?;
assert_eq!(*table.ty(), table_type);
let _elem = table.get(0).unwrap();
// assert_eq!(elem.funcref().unwrap(), f);
Ok(())
}
#[test]
#[ignore]
fn table_set() -> Result<()> {
// Table set not yet tested
Ok(())
}
#[test]
fn table_grow() -> Result<()> {
let store = Store::default();
let table_type = TableType {
ty: Type::FuncRef,
minimum: 0,
maximum: Some(10),
};
let f = Function::new_native(&store, |num: i32| num + 1);
let table = Table::new(&store, table_type, Value::FuncRef(Some(f.clone())))?;
// Growing to a bigger maximum should return None
let old_len = table.grow(12, Value::FuncRef(Some(f.clone())));
assert!(old_len.is_err());
// Growing to a bigger maximum should return None
let old_len = table.grow(5, Value::FuncRef(Some(f.clone())))?;
assert_eq!(old_len, 0);
Ok(())
}
#[test]
#[ignore]
fn table_copy() -> Result<()> {
// TODO: table copy test not yet implemented
Ok(())
}
#[test]
fn memory_new() -> Result<()> {
let store = Store::default();
let memory_type = MemoryType {
shared: false,
minimum: Pages(0),
maximum: Some(Pages(10)),
};
let memory = Memory::new(&store, memory_type)?;
assert_eq!(memory.size(), Pages(0));
assert_eq!(memory.ty(), memory_type);
Ok(())
}
#[test]
fn memory_grow() -> Result<()> {
let store = Store::default();
let desc = MemoryType::new(Pages(10), Some(Pages(16)), false);
let memory = Memory::new(&store, desc)?;
assert_eq!(memory.size(), Pages(10));
let result = memory.grow(Pages(2)).unwrap();
assert_eq!(result, Pages(10));
assert_eq!(memory.size(), Pages(12));
let result = memory.grow(Pages(10));
assert_eq!(
result,
Err(MemoryError::CouldNotGrow {
current: 12.into(),
attempted_delta: 10.into()
})
);
let bad_desc = MemoryType::new(Pages(15), Some(Pages(10)), false);
let bad_result = Memory::new(&store, bad_desc);
assert!(matches!(bad_result, Err(MemoryError::InvalidMemory { .. })));
Ok(())
}
#[test]
fn function_new() -> Result<()> {
let store = Store::default();
let function = Function::new_native(&store, || {});
assert_eq!(function.ty().clone(), FunctionType::new(vec![], vec![]));
let function = Function::new_native(&store, |_a: i32| {});
assert_eq!(
function.ty().clone(),
FunctionType::new(vec![Type::I32], vec![])
);
let function = Function::new_native(&store, |_a: i32, _b: i64, _c: f32, _d: f64| {});
assert_eq!(
function.ty().clone(),
FunctionType::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![])
);
let function = Function::new_native(&store, || -> i32 { 1 });
assert_eq!(
function.ty().clone(),
FunctionType::new(vec![], vec![Type::I32])
);
let function = Function::new_native(&store, || -> (i32, i64, f32, f64) { (1, 2, 3.0, 4.0) });
assert_eq!(
function.ty().clone(),
FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64])
);
Ok(())
}
#[test]
fn function_new_env() -> Result<()> {
let store = Store::default();
#[derive(Clone, WasmerEnv)]
struct MyEnv {}
let my_env = MyEnv {};
let function = Function::new_native_with_env(&store, my_env.clone(), |_env: &MyEnv| {});
assert_eq!(function.ty().clone(), FunctionType::new(vec![], vec![]));
let function =
Function::new_native_with_env(&store, my_env.clone(), |_env: &MyEnv, _a: i32| {});
assert_eq!(
function.ty().clone(),
FunctionType::new(vec![Type::I32], vec![])
);
let function = Function::new_native_with_env(
&store,
my_env.clone(),
|_env: &MyEnv, _a: i32, _b: i64, _c: f32, _d: f64| {},
);
assert_eq!(
function.ty().clone(),
FunctionType::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![])
);
let function =
Function::new_native_with_env(&store, my_env.clone(), |_env: &MyEnv| -> i32 { 1 });
assert_eq!(
function.ty().clone(),
FunctionType::new(vec![], vec![Type::I32])
);
let function = Function::new_native_with_env(
&store,
my_env.clone(),
|_env: &MyEnv| -> (i32, i64, f32, f64) { (1, 2, 3.0, 4.0) },
);
assert_eq!(
function.ty().clone(),
FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64])
);
Ok(())
}
#[test]
fn function_new_dynamic() -> Result<()> {
let store = Store::default();
// Using &FunctionType signature
let function_type = FunctionType::new(vec![], vec![]);
let function = Function::new(&store, &function_type, |_values: &[Value]| unimplemented!());
assert_eq!(function.ty().clone(), function_type);
let function_type = FunctionType::new(vec![Type::I32], vec![]);
let function = Function::new(&store, &function_type, |_values: &[Value]| unimplemented!());
assert_eq!(function.ty().clone(), function_type);
let function_type = FunctionType::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![]);
let function = Function::new(&store, &function_type, |_values: &[Value]| unimplemented!());
assert_eq!(function.ty().clone(), function_type);
let function_type = FunctionType::new(vec![], vec![Type::I32]);
let function = Function::new(&store, &function_type, |_values: &[Value]| unimplemented!());
assert_eq!(function.ty().clone(), function_type);
let function_type = FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64]);
let function = Function::new(&store, &function_type, |_values: &[Value]| unimplemented!());
assert_eq!(function.ty().clone(), function_type);
// Using array signature
let function_type = ([Type::V128], [Type::I32, Type::F32, Type::F64]);
let function = Function::new(&store, function_type, |_values: &[Value]| unimplemented!());
assert_eq!(function.ty().params(), [Type::V128]);
assert_eq!(function.ty().results(), [Type::I32, Type::F32, Type::F64]);
Ok(())
}
#[test]
fn function_new_dynamic_env() -> Result<()> {
let store = Store::default();
#[derive(Clone, WasmerEnv)]
struct MyEnv {}
let my_env = MyEnv {};
// Using &FunctionType signature
let function_type = FunctionType::new(vec![], vec![]);
let function = Function::new_with_env(
&store,
&function_type,
my_env.clone(),
|_env: &MyEnv, _values: &[Value]| unimplemented!(),
);
assert_eq!(function.ty().clone(), function_type);
let function_type = FunctionType::new(vec![Type::I32], vec![]);
let function = Function::new_with_env(
&store,
&function_type,
my_env.clone(),
|_env: &MyEnv, _values: &[Value]| unimplemented!(),
);
assert_eq!(function.ty().clone(), function_type);
let function_type = FunctionType::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![]);
let function = Function::new_with_env(
&store,
&function_type,
my_env.clone(),
|_env: &MyEnv, _values: &[Value]| unimplemented!(),
);
assert_eq!(function.ty().clone(), function_type);
let function_type = FunctionType::new(vec![], vec![Type::I32]);
let function = Function::new_with_env(
&store,
&function_type,
my_env.clone(),
|_env: &MyEnv, _values: &[Value]| unimplemented!(),
);
assert_eq!(function.ty().clone(), function_type);
let function_type = FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64]);
let function = Function::new_with_env(
&store,
&function_type,
my_env.clone(),
|_env: &MyEnv, _values: &[Value]| unimplemented!(),
);
assert_eq!(function.ty().clone(), function_type);
// Using array signature
let function_type = ([Type::V128], [Type::I32, Type::F32, Type::F64]);
let function = Function::new_with_env(
&store,
function_type,
my_env.clone(),
|_env: &MyEnv, _values: &[Value]| unimplemented!(),
);
assert_eq!(function.ty().params(), [Type::V128]);
assert_eq!(function.ty().results(), [Type::I32, Type::F32, Type::F64]);
Ok(())
}
#[test]
fn native_function_works() -> Result<()> {
let store = Store::default();
let function = Function::new_native(&store, || {});
let native_function: NativeFunc<(), ()> = function.native().unwrap();
let result = native_function.call();
assert!(result.is_ok());
let function = Function::new_native(&store, |a: i32| -> i32 { a + 1 });
let native_function: NativeFunc<i32, i32> = function.native().unwrap();
assert_eq!(native_function.call(3).unwrap(), 4);
fn rust_abi(a: i32, b: i64, c: f32, d: f64) -> u64 {
(a as u64 * 1000) + (b as u64 * 100) + (c as u64 * 10) + (d as u64)
}
let function = Function::new_native(&store, rust_abi);
let native_function: NativeFunc<(i32, i64, f32, f64), u64> = function.native().unwrap();
assert_eq!(native_function.call(8, 4, 1.5, 5.).unwrap(), 8415);
let function = Function::new_native(&store, || -> i32 { 1 });
let native_function: NativeFunc<(), i32> = function.native().unwrap();
assert_eq!(native_function.call().unwrap(), 1);
let function = Function::new_native(&store, |_a: i32| {});
let native_function: NativeFunc<i32, ()> = function.native().unwrap();
assert!(native_function.call(4).is_ok());
let function = Function::new_native(&store, || -> (i32, i64, f32, f64) { (1, 2, 3.0, 4.0) });
let native_function: NativeFunc<(), (i32, i64, f32, f64)> = function.native().unwrap();
assert_eq!(native_function.call().unwrap(), (1, 2, 3.0, 4.0));
Ok(())
}
#[test]
fn function_outlives_instance() -> Result<()> {
let store = Store::default();
let wat = r#"(module
(type $sum_t (func (param i32 i32) (result i32)))
(func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32)
local.get $x
local.get $y
i32.add)
(export "sum" (func $sum_f)))
"#;
let f = {
let module = Module::new(&store, wat)?;
let instance = Instance::new(&module, &imports! {})?;
let f: NativeFunc<(i32, i32), i32> = instance.exports.get_native_function("sum")?;
assert_eq!(f.call(4, 5)?, 9);
f
};
assert_eq!(f.call(4, 5)?, 9);
Ok(())
}
#[test]
fn weak_instance_ref_externs_after_instance() -> Result<()> {
let store = Store::default();
let wat = r#"(module
(memory (export "mem") 1)
(type $sum_t (func (param i32 i32) (result i32)))
(func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32)
local.get $x
local.get $y
i32.add)
(export "sum" (func $sum_f)))
"#;
let f = {
let module = Module::new(&store, wat)?;
let instance = Instance::new(&module, &imports! {})?;
let f: NativeFunc<(i32, i32), i32> = instance.exports.get_with_generics_weak("sum")?;
assert_eq!(f.call(4, 5)?, 9);
f
};
assert_eq!(f.call(4, 5)?, 9);
Ok(())
}
#[test]
fn manually_generate_wasmer_env() -> Result<()> {
let store = Store::default();
#[derive(WasmerEnv, Clone)]
struct MyEnv {
val: u32,
memory: LazyInit<Memory>,
}
fn host_function(env: &mut MyEnv, arg1: u32, arg2: u32) -> u32 {
env.val + arg1 + arg2
}
let mut env = MyEnv {
val: 5,
memory: LazyInit::new(),
};
let result = host_function(&mut env, 7, 9);
assert_eq!(result, 21);
let memory = Memory::new(&store, MemoryType::new(0, None, false))?;
env.memory.initialize(memory);
let result = host_function(&mut env, 1, 2);
assert_eq!(result, 8);
Ok(())
}

View File

@@ -1,39 +0,0 @@
use anyhow::Result;
use wasmer::*;
#[test]
fn exports_work_after_multiple_instances_have_been_freed() -> Result<()> {
let store = Store::default();
let module = Module::new(
&store,
"
(module
(type $sum_t (func (param i32 i32) (result i32)))
(func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32)
local.get $x
local.get $y
i32.add)
(export \"sum\" (func $sum_f)))
",
)?;
let import_object = ImportObject::new();
let instance = Instance::new(&module, &import_object)?;
let instance2 = instance.clone();
let instance3 = instance.clone();
// The function is cloned to “break” the connection with `instance`.
let sum = instance.exports.get_function("sum")?.clone();
drop(instance);
drop(instance2);
drop(instance3);
// All instances have been dropped, but `sum` continues to work!
assert_eq!(
sum.call(&[Value::I32(1), Value::I32(2)])?.into_vec(),
vec![Value::I32(3)],
);
Ok(())
}

View File

@@ -0,0 +1,425 @@
#[cfg(feature = "js")]
mod js {
use wasm_bindgen_test::*;
use wasmer::*;
#[wasm_bindgen_test]
fn global_new() {
let store = Store::default();
let global = Global::new(&store, Value::I32(10));
assert_eq!(
*global.ty(),
GlobalType {
ty: Type::I32,
mutability: Mutability::Const
}
);
let global_mut = Global::new_mut(&store, Value::I32(10));
assert_eq!(
*global_mut.ty(),
GlobalType {
ty: Type::I32,
mutability: Mutability::Var
}
);
}
#[wasm_bindgen_test]
fn global_get() {
let store = Store::default();
let global_i32 = Global::new(&store, Value::I32(10));
assert_eq!(global_i32.get(), Value::I32(10));
// 64-bit values are not yet fully supported in some versions of Node
// Commenting this tests for now:
// let global_i64 = Global::new(&store, Value::I64(20));
// assert_eq!(global_i64.get(), Value::I64(20));
let global_f32 = Global::new(&store, Value::F32(10.0));
assert_eq!(global_f32.get(), Value::F32(10.0));
// let global_f64 = Global::new(&store, Value::F64(20.0));
// assert_eq!(global_f64.get(), Value::F64(20.0));
}
#[wasm_bindgen_test]
fn global_set() {
let store = Store::default();
let global_i32 = Global::new(&store, Value::I32(10));
// Set on a constant should error
assert!(global_i32.set(Value::I32(20)).is_err());
let global_i32_mut = Global::new_mut(&store, Value::I32(10));
// Set on different type should error
assert!(global_i32_mut.set(Value::I64(20)).is_err());
// Set on same type should succeed
global_i32_mut.set(Value::I32(20)).unwrap();
assert_eq!(global_i32_mut.get(), Value::I32(20));
}
#[wasm_bindgen_test]
fn table_new() {
let store = Store::default();
let table_type = TableType {
ty: Type::FuncRef,
minimum: 0,
maximum: None,
};
let f = Function::new_native(&store, || {});
let table = Table::new(&store, table_type, Value::FuncRef(Some(f))).unwrap();
assert_eq!(*table.ty(), table_type);
// table.get()
// Anyrefs not yet supported
// let table_type = TableType {
// ty: Type::ExternRef,
// minimum: 0,
// maximum: None,
// };
// let table = Table::new(&store, table_type, Value::ExternRef(ExternRef::Null))?;
// assert_eq!(*table.ty(), table_type);
}
// Tables are not yet fully supported in Wasm
// Commenting this tests for now
// #[test]
// #[ignore]
// fn table_get() -> Result<()> {
// let store = Store::default();
// let table_type = TableType {
// ty: Type::FuncRef,
// minimum: 0,
// maximum: Some(1),
// };
// let f = Function::new_native(&store, |num: i32| num + 1);
// let table = Table::new(&store, table_type, Value::FuncRef(Some(f.clone())))?;
// assert_eq!(*table.ty(), table_type);
// let _elem = table.get(0).unwrap();
// // assert_eq!(elem.funcref().unwrap(), f);
// Ok(())
// }
// #[test]
// #[ignore]
// fn table_set() -> Result<()> {
// // Table set not yet tested
// Ok(())
// }
// #[test]
// fn table_grow() -> Result<()> {
// let store = Store::default();
// let table_type = TableType {
// ty: Type::FuncRef,
// minimum: 0,
// maximum: Some(10),
// };
// let f = Function::new_native(&store, |num: i32| num + 1);
// let table = Table::new(&store, table_type, Value::FuncRef(Some(f.clone())))?;
// // Growing to a bigger maximum should return None
// let old_len = table.grow(12, Value::FuncRef(Some(f.clone())));
// assert!(old_len.is_err());
// // Growing to a bigger maximum should return None
// let old_len = table.grow(5, Value::FuncRef(Some(f.clone())))?;
// assert_eq!(old_len, 0);
// Ok(())
// }
// #[test]
// #[ignore]
// fn table_copy() -> Result<()> {
// // TODO: table copy test not yet implemented
// Ok(())
// }
#[wasm_bindgen_test]
fn memory_new() {
let store = Store::default();
let memory_type = MemoryType {
shared: false,
minimum: Pages(0),
maximum: Some(Pages(10)),
};
let memory = Memory::new(&store, memory_type).unwrap();
assert_eq!(memory.size(), Pages(0));
assert_eq!(memory.ty(), memory_type);
}
#[wasm_bindgen_test]
fn memory_grow() {
let store = Store::default();
let desc = MemoryType::new(Pages(10), Some(Pages(16)), false);
let memory = Memory::new(&store, desc).unwrap();
assert_eq!(memory.size(), Pages(10));
let result = memory.grow(Pages(2)).unwrap();
assert_eq!(result, Pages(10));
assert_eq!(memory.size(), Pages(12));
let result = memory.grow(Pages(10));
assert!(result.is_err());
assert_eq!(
result,
Err(MemoryError::CouldNotGrow {
current: 12.into(),
attempted_delta: 10.into()
})
);
}
#[wasm_bindgen_test]
fn function_new() {
let store = Store::default();
let function = Function::new_native(&store, || {});
assert_eq!(function.ty().clone(), FunctionType::new(vec![], vec![]));
let function = Function::new_native(&store, |_a: i32| {});
assert_eq!(
function.ty().clone(),
FunctionType::new(vec![Type::I32], vec![])
);
let function = Function::new_native(&store, |_a: i32, _b: i64, _c: f32, _d: f64| {});
assert_eq!(
function.ty().clone(),
FunctionType::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![])
);
let function = Function::new_native(&store, || -> i32 { 1 });
assert_eq!(
function.ty().clone(),
FunctionType::new(vec![], vec![Type::I32])
);
let function =
Function::new_native(&store, || -> (i32, i64, f32, f64) { (1, 2, 3.0, 4.0) });
assert_eq!(
function.ty().clone(),
FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64])
);
}
#[wasm_bindgen_test]
fn function_new_env() {
let store = Store::default();
#[derive(Clone, WasmerEnv)]
struct MyEnv {}
let my_env = MyEnv {};
let function = Function::new_native_with_env(&store, my_env.clone(), |_env: &MyEnv| {});
assert_eq!(function.ty().clone(), FunctionType::new(vec![], vec![]));
let function =
Function::new_native_with_env(&store, my_env.clone(), |_env: &MyEnv, _a: i32| {});
assert_eq!(
function.ty().clone(),
FunctionType::new(vec![Type::I32], vec![])
);
let function = Function::new_native_with_env(
&store,
my_env.clone(),
|_env: &MyEnv, _a: i32, _b: i64, _c: f32, _d: f64| {},
);
assert_eq!(
function.ty().clone(),
FunctionType::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![])
);
let function =
Function::new_native_with_env(&store, my_env.clone(), |_env: &MyEnv| -> i32 { 1 });
assert_eq!(
function.ty().clone(),
FunctionType::new(vec![], vec![Type::I32])
);
let function = Function::new_native_with_env(
&store,
my_env.clone(),
|_env: &MyEnv| -> (i32, i64, f32, f64) { (1, 2, 3.0, 4.0) },
);
assert_eq!(
function.ty().clone(),
FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64])
);
}
#[wasm_bindgen_test]
fn function_new_dynamic() {
let store = Store::default();
// Using &FunctionType signature
let function_type = FunctionType::new(vec![], vec![]);
let function = Function::new(&store, &function_type, |_values: &[Value]| unimplemented!());
assert_eq!(function.ty().clone(), function_type);
let function_type = FunctionType::new(vec![Type::I32], vec![]);
let function = Function::new(&store, &function_type, |_values: &[Value]| unimplemented!());
assert_eq!(function.ty().clone(), function_type);
let function_type =
FunctionType::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![]);
let function = Function::new(&store, &function_type, |_values: &[Value]| unimplemented!());
assert_eq!(function.ty().clone(), function_type);
let function_type = FunctionType::new(vec![], vec![Type::I32]);
let function = Function::new(&store, &function_type, |_values: &[Value]| unimplemented!());
assert_eq!(function.ty().clone(), function_type);
let function_type =
FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64]);
let function = Function::new(&store, &function_type, |_values: &[Value]| unimplemented!());
assert_eq!(function.ty().clone(), function_type);
// Using array signature
let function_type = ([Type::V128], [Type::I32, Type::F32, Type::F64]);
let function = Function::new(&store, function_type, |_values: &[Value]| unimplemented!());
assert_eq!(function.ty().params(), [Type::V128]);
assert_eq!(function.ty().results(), [Type::I32, Type::F32, Type::F64]);
}
#[wasm_bindgen_test]
fn function_new_dynamic_env() {
let store = Store::default();
#[derive(Clone, WasmerEnv)]
struct MyEnv {}
let my_env = MyEnv {};
// Using &FunctionType signature
let function_type = FunctionType::new(vec![], vec![]);
let function = Function::new_with_env(
&store,
&function_type,
my_env.clone(),
|_env: &MyEnv, _values: &[Value]| unimplemented!(),
);
assert_eq!(function.ty().clone(), function_type);
let function_type = FunctionType::new(vec![Type::I32], vec![]);
let function = Function::new_with_env(
&store,
&function_type,
my_env.clone(),
|_env: &MyEnv, _values: &[Value]| unimplemented!(),
);
assert_eq!(function.ty().clone(), function_type);
let function_type =
FunctionType::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![]);
let function = Function::new_with_env(
&store,
&function_type,
my_env.clone(),
|_env: &MyEnv, _values: &[Value]| unimplemented!(),
);
assert_eq!(function.ty().clone(), function_type);
let function_type = FunctionType::new(vec![], vec![Type::I32]);
let function = Function::new_with_env(
&store,
&function_type,
my_env.clone(),
|_env: &MyEnv, _values: &[Value]| unimplemented!(),
);
assert_eq!(function.ty().clone(), function_type);
let function_type =
FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64]);
let function = Function::new_with_env(
&store,
&function_type,
my_env.clone(),
|_env: &MyEnv, _values: &[Value]| unimplemented!(),
);
assert_eq!(function.ty().clone(), function_type);
// Using array signature
let function_type = ([Type::V128], [Type::I32, Type::F32, Type::F64]);
let function = Function::new_with_env(
&store,
function_type,
my_env.clone(),
|_env: &MyEnv, _values: &[Value]| unimplemented!(),
);
assert_eq!(function.ty().params(), [Type::V128]);
assert_eq!(function.ty().results(), [Type::I32, Type::F32, Type::F64]);
}
#[wasm_bindgen_test]
fn native_function_works() {
let store = Store::default();
let function = Function::new_native(&store, || {});
let native_function: NativeFunc<(), ()> = function.native().unwrap();
let result = native_function.call();
assert!(result.is_ok());
let function = Function::new_native(&store, |a: i32| -> i32 { a + 1 });
let native_function: NativeFunc<i32, i32> = function.native().unwrap();
assert_eq!(native_function.call(3).unwrap(), 4);
// fn rust_abi(a: i32, b: i64, c: f32, d: f64) -> u64 {
// (a as u64 * 1000) + (b as u64 * 100) + (c as u64 * 10) + (d as u64)
// }
// let function = Function::new_native(&store, rust_abi);
// let native_function: NativeFunc<(i32, i64, f32, f64), u64> = function.native().unwrap();
// assert_eq!(native_function.call(8, 4, 1.5, 5.).unwrap(), 8415);
let function = Function::new_native(&store, || -> i32 { 1 });
let native_function: NativeFunc<(), i32> = function.native().unwrap();
assert_eq!(native_function.call().unwrap(), 1);
let function = Function::new_native(&store, |_a: i32| {});
let native_function: NativeFunc<i32, ()> = function.native().unwrap();
assert!(native_function.call(4).is_ok());
// let function = Function::new_native(&store, || -> (i32, i64, f32, f64) { (1, 2, 3.0, 4.0) });
// let native_function: NativeFunc<(), (i32, i64, f32, f64)> = function.native().unwrap();
// assert_eq!(native_function.call().unwrap(), (1, 2, 3.0, 4.0));
}
#[wasm_bindgen_test]
fn function_outlives_instance() {
let store = Store::default();
let wat = r#"(module
(type $sum_t (func (param i32 i32) (result i32)))
(func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32)
local.get $x
local.get $y
i32.add)
(export "sum" (func $sum_f)))
"#;
let f = {
let module = Module::new(&store, wat).unwrap();
let instance = Instance::new(&module, &imports! {}).unwrap();
let f = instance.exports.get_function("sum").unwrap();
assert_eq!(
f.call(&[Val::I32(4), Val::I32(5)]).unwrap(),
vec![Val::I32(9)].into_boxed_slice()
);
f.clone()
};
assert_eq!(
f.call(&[Val::I32(4), Val::I32(5)]).unwrap(),
vec![Val::I32(9)].into_boxed_slice()
);
}
#[wasm_bindgen_test]
fn manually_generate_wasmer_env() {
let store = Store::default();
#[derive(WasmerEnv, Clone)]
struct MyEnv {
val: u32,
memory: LazyInit<Memory>,
}
fn host_function(env: &mut MyEnv, arg1: u32, arg2: u32) -> u32 {
env.val + arg1 + arg2
}
let mut env = MyEnv {
val: 5,
memory: LazyInit::new(),
};
let result = host_function(&mut env, 7, 9);
assert_eq!(result, 21);
let memory = Memory::new(&store, MemoryType::new(0, None, false)).unwrap();
env.memory.initialize(memory);
let result = host_function(&mut env, 1, 2);
assert_eq!(result, 8);
}
}

View File

@@ -0,0 +1,735 @@
#[cfg(feature = "js")]
mod js {
use anyhow::Result;
use wasm_bindgen_test::*;
use wasmer::*;
#[wasm_bindgen_test]
fn test_exported_memory() {
let store = Store::default();
let mut module = Module::new(
&store,
br#"
(module
(memory (export "mem") 1)
)
"#,
)
.unwrap();
module
.set_type_hints(ModuleTypeHints {
imports: vec![],
exports: vec![ExternType::Memory(MemoryType::new(Pages(1), None, false))],
})
.unwrap();
let import_object = imports! {};
let instance = Instance::new(&module, &import_object).unwrap();
let memory = instance.exports.get_memory("mem").unwrap();
assert_eq!(memory.ty(), MemoryType::new(Pages(1), None, false));
assert_eq!(memory.size(), Pages(1));
assert_eq!(memory.data_size(), 65536);
memory.grow(Pages(1)).unwrap();
assert_eq!(memory.ty(), MemoryType::new(Pages(2), None, false));
assert_eq!(memory.size(), Pages(2));
assert_eq!(memory.data_size(), 65536 * 2);
}
#[wasm_bindgen_test]
fn test_exported_function() {
let store = Store::default();
let mut module = Module::new(
&store,
br#"
(module
(func (export "get_magic") (result i32)
(i32.const 42)
)
)
"#,
)
.unwrap();
module
.set_type_hints(ModuleTypeHints {
imports: vec![],
exports: vec![ExternType::Function(FunctionType::new(
vec![],
vec![Type::I32],
))],
})
.unwrap();
let import_object = imports! {};
let instance = Instance::new(&module, &import_object).unwrap();
let get_magic = instance.exports.get_function("get_magic").unwrap();
assert_eq!(
get_magic.ty().clone(),
FunctionType::new(vec![], vec![Type::I32])
);
let expected = vec![Val::I32(42)].into_boxed_slice();
assert_eq!(get_magic.call(&[]), Ok(expected));
}
#[wasm_bindgen_test]
fn test_imported_function_dynamic() {
let 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 imported_signature = FunctionType::new(vec![Type::I32], vec![Type::I32]);
let imported = Function::new(&store, &imported_signature, |args| {
println!("Calling `imported`...");
let result = args[0].unwrap_i32() * 2;
println!("Result of `imported`: {:?}", result);
Ok(vec![Value::I32(result)])
});
let import_object = imports! {
"env" => {
"imported" => imported,
}
};
let instance = Instance::new(&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(&[Val::I32(3)]), Ok(expected));
}
// We comment it for now because in old versions of Node, only single return values are supported
// #[wasm_bindgen_test]
// fn test_imported_function_dynamic_multivalue() {
// let store = Store::default();
// let mut module = Module::new(
// &store,
// br#"
// (module
// (func $multivalue (import "env" "multivalue") (param i32 i32) (result i32 i32))
// (func (export "multivalue") (param i32 i32) (result i32 i32)
// (call $multivalue (local.get 0) (local.get 1))
// )
// )
// "#,
// )
// .unwrap();
// module.set_type_hints(ModuleTypeHints {
// imports: vec![
// ExternType::Function(FunctionType::new(
// vec![Type::I32, Type::I32],
// vec![Type::I32, Type::I32],
// )),
// ],
// exports: vec![
// ExternType::Function(FunctionType::new(
// vec![Type::I32, Type::I32],
// vec![Type::I32, Type::I32],
// )),
// ],
// });
// let multivalue_signature =
// FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32, Type::I32]);
// let multivalue = Function::new(&store, &multivalue_signature, |args| {
// println!("Calling `imported`...");
// // let result = args[0].unwrap_i32() * ;
// // println!("Result of `imported`: {:?}", result);
// Ok(vec![args[1].clone(), args[0].clone()])
// });
// let import_object = imports! {
// "env" => {
// "multivalue" => multivalue,
// }
// };
// let instance = Instance::new(&module, &import_object).unwrap();
// let exported_multivalue = instance
// .exports
// .get_function("multivalue")
// .unwrap();
// let expected = vec![Val::I32(2), Val::I32(3)].into_boxed_slice();
// assert_eq!(
// exported_multivalue.call(&[Val::I32(3), Val::I32(2)]),
// Ok(expected)
// );
// }
#[wasm_bindgen_test]
fn test_imported_function_dynamic_with_env() {
let 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();
#[derive(WasmerEnv, Clone)]
struct Env {
multiplier: i32,
}
let imported_signature = FunctionType::new(vec![Type::I32], vec![Type::I32]);
let imported = Function::new_with_env(
&store,
&imported_signature,
Env { multiplier: 3 },
|env, args| {
println!("Calling `imported`...");
let result = args[0].unwrap_i32() * env.multiplier;
println!("Result of `imported`: {:?}", result);
Ok(vec![Value::I32(result)])
},
);
let import_object = imports! {
"env" => {
"imported" => imported,
}
};
let instance = Instance::new(&module, &import_object).unwrap();
let exported = instance.exports.get_function("exported").unwrap();
let expected = vec![Val::I32(9)].into_boxed_slice();
assert_eq!(exported.call(&[Val::I32(3)]), Ok(expected));
}
#[wasm_bindgen_test]
fn test_imported_function_native() {
let 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();
fn imported_fn(arg: u32) -> u32 {
return arg + 1;
}
let imported = Function::new_native(&store, imported_fn);
let import_object = imports! {
"env" => {
"imported" => imported,
}
};
let instance = Instance::new(&module, &import_object).unwrap();
let exported = instance.exports.get_function("exported").unwrap();
let expected = vec![Val::I32(5)].into_boxed_slice();
assert_eq!(exported.call(&[Val::I32(4)]), Ok(expected));
}
#[wasm_bindgen_test]
fn test_imported_function_native_with_env() {
let 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();
#[derive(WasmerEnv, Clone)]
struct Env {
multiplier: u32,
}
fn imported_fn(env: &Env, arg: u32) -> u32 {
return env.multiplier * arg;
}
let imported = Function::new_native_with_env(&store, Env { multiplier: 3 }, imported_fn);
let import_object = imports! {
"env" => {
"imported" => imported,
}
};
let instance = Instance::new(&module, &import_object).unwrap();
let exported = instance.exports.get_function("exported").unwrap();
let expected = vec![Val::I32(12)].into_boxed_slice();
assert_eq!(exported.call(&[Val::I32(4)]), Ok(expected));
}
#[wasm_bindgen_test]
fn test_imported_function_native_with_wasmer_env() {
let 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))
)
(memory (export "memory") 1)
)
"#,
)
.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])),
ExternType::Memory(MemoryType::new(Pages(1), None, false)),
],
})
.unwrap();
#[derive(WasmerEnv, Clone)]
struct Env {
multiplier: u32,
#[wasmer(export)]
memory: LazyInit<Memory>,
}
fn imported_fn(env: &Env, arg: u32) -> u32 {
let memory = env.memory_ref().unwrap();
let memory_val = memory.uint8view().get_index(0);
return (memory_val as u32) * env.multiplier * arg;
}
let imported = Function::new_native_with_env(
&store,
Env {
multiplier: 3,
memory: LazyInit::new(),
},
imported_fn,
);
let import_object = imports! {
"env" => {
"imported" => imported,
}
};
let instance = Instance::new(&module, &import_object).unwrap();
let memory = instance.exports.get_memory("memory").unwrap();
assert_eq!(memory.data_size(), 65536);
let memory_val = memory.uint8view().get_index(0);
assert_eq!(memory_val, 0);
memory.uint8view().set_index(0, 2);
let memory_val = memory.uint8view().get_index(0);
assert_eq!(memory_val, 2);
let exported = instance.exports.get_function("exported").unwrap();
// It works with the provided memory
let expected = vec![Val::I32(24)].into_boxed_slice();
assert_eq!(exported.call(&[Val::I32(4)]), Ok(expected));
// It works if we update the memory
memory.uint8view().set_index(0, 3);
let expected = vec![Val::I32(36)].into_boxed_slice();
assert_eq!(exported.call(&[Val::I32(4)]), Ok(expected));
}
#[wasm_bindgen_test]
fn test_imported_function_with_wasmer_env() {
let 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))
)
(memory (export "memory") 1)
)
"#,
)
.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])),
ExternType::Memory(MemoryType::new(Pages(1), None, false)),
],
})
.unwrap();
#[derive(WasmerEnv, Clone)]
struct Env {
multiplier: u32,
#[wasmer(export)]
memory: LazyInit<Memory>,
}
fn imported_fn(env: &Env, args: &[Val]) -> Result<Vec<Val>, RuntimeError> {
let memory = env.memory_ref().unwrap();
let memory_val = memory.uint8view().get_index(0);
let value = (memory_val as u32) * env.multiplier * args[0].unwrap_i32() as u32;
return Ok(vec![Val::I32(value as _)]);
}
let imported_signature = FunctionType::new(vec![Type::I32], vec![Type::I32]);
let imported = Function::new_with_env(
&store,
imported_signature,
Env {
multiplier: 3,
memory: LazyInit::new(),
},
imported_fn,
);
let import_object = imports! {
"env" => {
"imported" => imported,
}
};
let instance = Instance::new(&module, &import_object).unwrap();
let memory = instance.exports.get_memory("memory").unwrap();
assert_eq!(memory.data_size(), 65536);
let memory_val = memory.uint8view().get_index(0);
assert_eq!(memory_val, 0);
memory.uint8view().set_index(0, 2);
let memory_val = memory.uint8view().get_index(0);
assert_eq!(memory_val, 2);
let exported = instance.exports.get_function("exported").unwrap();
// It works with the provided memory
let expected = vec![Val::I32(24)].into_boxed_slice();
assert_eq!(exported.call(&[Val::I32(4)]), Ok(expected));
// It works if we update the memory
memory.uint8view().set_index(0, 3);
let expected = vec![Val::I32(36)].into_boxed_slice();
assert_eq!(exported.call(&[Val::I32(4)]), Ok(expected));
}
#[wasm_bindgen_test]
fn test_imported_exported_global() {
let store = Store::default();
let mut module = Module::new(
&store,
br#"
(module
(global $mut_i32_import (import "" "global") (mut i32))
(func (export "getGlobal") (result i32) (global.get $mut_i32_import))
(func (export "incGlobal") (global.set $mut_i32_import (
i32.add (i32.const 1) (global.get $mut_i32_import)
)))
)
"#,
)
.unwrap();
module
.set_type_hints(ModuleTypeHints {
imports: vec![ExternType::Global(GlobalType::new(
ValType::I32,
Mutability::Var,
))],
exports: vec![
ExternType::Function(FunctionType::new(vec![], vec![Type::I32])),
ExternType::Function(FunctionType::new(vec![], vec![])),
],
})
.unwrap();
let global = Global::new_mut(&store, Value::I32(0));
let import_object = imports! {
"" => {
"global" => global.clone()
}
};
let instance = Instance::new(&module, &import_object).unwrap();
let get_global = instance.exports.get_function("getGlobal").unwrap();
assert_eq!(
get_global.call(&[]),
Ok(vec![Val::I32(0)].into_boxed_slice())
);
global.set(Value::I32(42)).unwrap();
assert_eq!(
get_global.call(&[]),
Ok(vec![Val::I32(42)].into_boxed_slice())
);
let inc_global = instance.exports.get_function("incGlobal").unwrap();
inc_global.call(&[]).unwrap();
assert_eq!(
get_global.call(&[]),
Ok(vec![Val::I32(43)].into_boxed_slice())
);
assert_eq!(global.get(), Val::I32(43));
}
#[wasm_bindgen_test]
fn test_native_function() {
let store = Store::default();
let module = Module::new(
&store,
br#"(module
(func $add (import "env" "sum") (param i32 i32) (result i32))
(func (export "add_one") (param i32) (result i32)
(call $add (local.get 0) (i32.const 1))
)
)"#,
)
.unwrap();
fn sum(a: i32, b: i32) -> i32 {
a + b
}
let import_object = imports! {
"env" => {
"sum" => Function::new_native(&store, sum),
}
};
let instance = Instance::new(&module, &import_object).unwrap();
let add_one: NativeFunc<i32, i32> =
instance.exports.get_native_function("add_one").unwrap();
assert_eq!(add_one.call(1), Ok(2));
}
#[wasm_bindgen_test]
fn test_panic() {
let store = Store::default();
let module = Module::new(
&store,
br#"
(module
(type $run_t (func (param i32 i32) (result i32)))
(type $early_exit_t (func (param) (result)))
(import "env" "early_exit" (func $early_exit (type $early_exit_t)))
(func $run (type $run_t) (param $x i32) (param $y i32) (result i32)
(call $early_exit)
(i32.add
local.get $x
local.get $y))
(export "run" (func $run)))
"#,
)
.unwrap();
fn early_exit() {
panic!("Do panic")
}
let import_object = imports! {
"env" => {
"early_exit" => Function::new_native(&store, early_exit),
}
};
let instance = Instance::new(&module, &import_object).unwrap();
let run_func: NativeFunc<(i32, i32), i32> =
instance.exports.get_native_function("run").unwrap();
assert!(run_func.call(1, 7).is_err(), "Expected early termination",);
let run_func = instance.exports.get_function("run").unwrap();
assert!(
run_func.call(&[Val::I32(1), Val::I32(7)]).is_err(),
"Expected early termination",
);
}
#[wasm_bindgen_test]
fn test_custom_error() {
let store = Store::default();
let module = Module::new(
&store,
br#"
(module
(type $run_t (func (param i32 i32) (result i32)))
(type $early_exit_t (func (param) (result)))
(import "env" "early_exit" (func $early_exit (type $early_exit_t)))
(func $run (type $run_t) (param $x i32) (param $y i32) (result i32)
(call $early_exit)
(i32.add
local.get $x
local.get $y))
(export "run" (func $run)))
"#,
)
.unwrap();
use std::fmt;
#[derive(Debug, Clone, Copy)]
struct ExitCode(u32);
impl fmt::Display for ExitCode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl std::error::Error for ExitCode {}
fn early_exit() {
RuntimeError::raise(Box::new(ExitCode(1)));
}
let import_object = imports! {
"env" => {
"early_exit" => Function::new_native(&store, early_exit),
}
};
let instance = Instance::new(&module, &import_object).unwrap();
fn test_result<T: core::fmt::Debug>(result: Result<T, RuntimeError>) {
match result {
Ok(result) => {
assert!(
false,
"Expected early termination with `ExitCode`, found: {:?}",
result
);
}
Err(e) => {
match e.downcast::<ExitCode>() {
// We found the exit code used to terminate execution.
Ok(exit_code) => {
assert_eq!(exit_code.0, 1);
}
Err(e) => {
assert!(false, "Unknown error `{:?}` found. expected `ErrorCode`", e);
}
}
}
}
}
let run_func: NativeFunc<(i32, i32), i32> =
instance.exports.get_native_function("run").unwrap();
test_result(run_func.call(1, 7));
let run_func = instance.exports.get_function("run").unwrap();
test_result(run_func.call(&[Val::I32(1), Val::I32(7)]));
}
#[wasm_bindgen_test]
fn test_start_function_fails() {
let store = Store::default();
let module = Module::new(
&store,
br#"
(module
(func $start_function
(i32.div_u
(i32.const 1)
(i32.const 0)
)
drop
)
(start $start_function)
)
"#,
)
.unwrap();
let import_object = imports! {};
let result = Instance::new(&module, &import_object);
let err = result.unwrap_err();
assert!(format!("{:?}", err).contains("zero"))
}
}

294
lib/api/tests/js_module.rs Normal file
View File

@@ -0,0 +1,294 @@
#[cfg(feature = "js")]
mod js {
use js_sys::{Uint8Array, WebAssembly};
use wasm_bindgen_test::*;
use wasmer::*;
#[wasm_bindgen_test]
fn module_get_name() {
let store = Store::default();
let wat = r#"(module)"#;
let module = Module::new(&store, wat).unwrap();
assert_eq!(module.name(), None);
}
#[wasm_bindgen_test]
fn module_set_name() {
let store = Store::default();
let wat = r#"(module $name)"#;
let mut module = Module::new(&store, wat).unwrap();
#[cfg(feature = "wasm-types-polyfill")]
assert_eq!(module.name(), Some("name"));
module.set_name("new_name");
assert_eq!(module.name(), Some("new_name"));
}
#[wasm_bindgen_test]
fn module_from_jsmodule() {
let wat = br#"(module $name)"#;
let binary = wat2wasm(wat).unwrap();
let js_bytes = unsafe { Uint8Array::view(&binary) };
let js_module = WebAssembly::Module::new(&js_bytes.into()).unwrap();
let module: Module = js_module.into();
assert_eq!(module.store(), &Store::default());
}
#[wasm_bindgen_test]
fn imports() {
let store = Store::default();
let wat = r#"(module
(import "host" "func" (func))
(import "host" "memory" (memory 1))
(import "host" "table" (table 1 anyfunc))
(import "host" "global" (global i32))
)"#;
let module = Module::new(&store, wat).unwrap();
assert_eq!(
module.imports().collect::<Vec<_>>(),
vec![
ImportType::new(
"host",
"func",
ExternType::Function(FunctionType::new(vec![], vec![]))
),
ImportType::new(
"host",
"memory",
ExternType::Memory(MemoryType::new(Pages(1), None, false))
),
ImportType::new(
"host",
"table",
ExternType::Table(TableType::new(Type::FuncRef, 1, None))
),
ImportType::new(
"host",
"global",
ExternType::Global(GlobalType::new(Type::I32, Mutability::Const))
)
]
);
// Now we test the iterators
assert_eq!(
module.imports().functions().collect::<Vec<_>>(),
vec![ImportType::new(
"host",
"func",
FunctionType::new(vec![], vec![])
),]
);
assert_eq!(
module.imports().memories().collect::<Vec<_>>(),
vec![ImportType::new(
"host",
"memory",
MemoryType::new(Pages(1), None, false)
),]
);
assert_eq!(
module.imports().tables().collect::<Vec<_>>(),
vec![ImportType::new(
"host",
"table",
TableType::new(Type::FuncRef, 1, None)
),]
);
assert_eq!(
module.imports().globals().collect::<Vec<_>>(),
vec![ImportType::new(
"host",
"global",
GlobalType::new(Type::I32, Mutability::Const)
),]
);
}
#[wasm_bindgen_test]
fn exports() {
let store = Store::default();
let wat = r#"(module
(func (export "func") nop)
(memory (export "memory") 2)
(table (export "table") 2 funcref)
(global (export "global") i32 (i32.const 0))
)"#;
let mut module = Module::new(&store, wat).unwrap();
module
.set_type_hints(ModuleTypeHints {
exports: vec![
ExternType::Function(FunctionType::new(vec![], vec![])),
ExternType::Memory(MemoryType::new(Pages(2), None, false)),
ExternType::Table(TableType::new(Type::FuncRef, 2, None)),
ExternType::Global(GlobalType::new(Type::I32, Mutability::Const)),
],
imports: vec![],
})
.unwrap();
assert_eq!(
module.exports().collect::<Vec<_>>(),
vec![
ExportType::new(
"func",
ExternType::Function(FunctionType::new(vec![], vec![]))
),
ExportType::new(
"memory",
ExternType::Memory(MemoryType::new(Pages(2), None, false))
),
ExportType::new(
"table",
ExternType::Table(TableType::new(Type::FuncRef, 2, None))
),
ExportType::new(
"global",
ExternType::Global(GlobalType::new(Type::I32, Mutability::Const))
)
]
);
// Now we test the iterators
assert_eq!(
module.exports().functions().collect::<Vec<_>>(),
vec![ExportType::new("func", FunctionType::new(vec![], vec![])),]
);
assert_eq!(
module.exports().memories().collect::<Vec<_>>(),
vec![ExportType::new(
"memory",
MemoryType::new(Pages(2), None, false)
),]
);
assert_eq!(
module.exports().tables().collect::<Vec<_>>(),
vec![ExportType::new(
"table",
TableType::new(Type::FuncRef, 2, None)
),]
);
assert_eq!(
module.exports().globals().collect::<Vec<_>>(),
vec![ExportType::new(
"global",
GlobalType::new(Type::I32, Mutability::Const)
),]
);
}
// Test commented because it doesn't work in old versions of Node
// which makes the CI to fail.
// #[wasm_bindgen_test]
// fn calling_host_functions_with_negative_values_works() {
// let store = Store::default();
// let wat = r#"(module
// (import "host" "host_func1" (func (param i64)))
// (import "host" "host_func2" (func (param i32)))
// (import "host" "host_func3" (func (param i64)))
// (import "host" "host_func4" (func (param i32)))
// (import "host" "host_func5" (func (param i32)))
// (import "host" "host_func6" (func (param i32)))
// (import "host" "host_func7" (func (param i32)))
// (import "host" "host_func8" (func (param i32)))
// (func (export "call_host_func1")
// (call 0 (i64.const -1)))
// (func (export "call_host_func2")
// (call 1 (i32.const -1)))
// (func (export "call_host_func3")
// (call 2 (i64.const -1)))
// (func (export "call_host_func4")
// (call 3 (i32.const -1)))
// (func (export "call_host_func5")
// (call 4 (i32.const -1)))
// (func (export "call_host_func6")
// (call 5 (i32.const -1)))
// (func (export "call_host_func7")
// (call 6 (i32.const -1)))
// (func (export "call_host_func8")
// (call 7 (i32.const -1)))
// )"#;
// let module = Module::new(&store, wat).unwrap();
// let imports = imports! {
// "host" => {
// "host_func1" => Function::new_native(&store, |p: u64| {
// println!("host_func1: Found number {}", p);
// assert_eq!(p, u64::max_value());
// }),
// "host_func2" => Function::new_native(&store, |p: u32| {
// println!("host_func2: Found number {}", p);
// assert_eq!(p, u32::max_value());
// }),
// "host_func3" => Function::new_native(&store, |p: i64| {
// println!("host_func3: Found number {}", p);
// assert_eq!(p, -1);
// }),
// "host_func4" => Function::new_native(&store, |p: i32| {
// println!("host_func4: Found number {}", p);
// assert_eq!(p, -1);
// }),
// "host_func5" => Function::new_native(&store, |p: i16| {
// println!("host_func5: Found number {}", p);
// assert_eq!(p, -1);
// }),
// "host_func6" => Function::new_native(&store, |p: u16| {
// println!("host_func6: Found number {}", p);
// assert_eq!(p, u16::max_value());
// }),
// "host_func7" => Function::new_native(&store, |p: i8| {
// println!("host_func7: Found number {}", p);
// assert_eq!(p, -1);
// }),
// "host_func8" => Function::new_native(&store, |p: u8| {
// println!("host_func8: Found number {}", p);
// assert_eq!(p, u8::max_value());
// }),
// }
// };
// let instance = Instance::new(&module, &imports).unwrap();
// let f1: NativeFunc<(), ()> = instance
// .exports
// .get_native_function("call_host_func1")
// .unwrap();
// let f2: NativeFunc<(), ()> = instance
// .exports
// .get_native_function("call_host_func2")
// .unwrap();
// let f3: NativeFunc<(), ()> = instance
// .exports
// .get_native_function("call_host_func3")
// .unwrap();
// let f4: NativeFunc<(), ()> = instance
// .exports
// .get_native_function("call_host_func4")
// .unwrap();
// let f5: NativeFunc<(), ()> = instance
// .exports
// .get_native_function("call_host_func5")
// .unwrap();
// let f6: NativeFunc<(), ()> = instance
// .exports
// .get_native_function("call_host_func6")
// .unwrap();
// let f7: NativeFunc<(), ()> = instance
// .exports
// .get_native_function("call_host_func7")
// .unwrap();
// let f8: NativeFunc<(), ()> = instance
// .exports
// .get_native_function("call_host_func8")
// .unwrap();
// f1.call().unwrap();
// f2.call().unwrap();
// f3.call().unwrap();
// f4.call().unwrap();
// f5.call().unwrap();
// f6.call().unwrap();
// f7.call().unwrap();
// f8.call().unwrap();
// }
}

View File

@@ -1,248 +0,0 @@
use anyhow::Result;
use wasmer::*;
#[test]
fn module_get_name() -> Result<()> {
let store = Store::default();
let wat = r#"(module)"#;
let module = Module::new(&store, wat)?;
assert_eq!(module.name(), None);
Ok(())
}
#[test]
fn module_set_name() -> Result<()> {
let store = Store::default();
let wat = r#"(module $name)"#;
let mut module = Module::new(&store, wat)?;
assert_eq!(module.name(), Some("name"));
module.set_name("new_name");
assert_eq!(module.name(), Some("new_name"));
Ok(())
}
#[test]
fn imports() -> Result<()> {
let store = Store::default();
let wat = r#"(module
(import "host" "func" (func))
(import "host" "memory" (memory 1))
(import "host" "table" (table 1 anyfunc))
(import "host" "global" (global i32))
)"#;
let module = Module::new(&store, wat)?;
assert_eq!(
module.imports().collect::<Vec<_>>(),
vec![
ImportType::new(
"host",
"func",
ExternType::Function(FunctionType::new(vec![], vec![]))
),
ImportType::new(
"host",
"memory",
ExternType::Memory(MemoryType::new(Pages(1), None, false))
),
ImportType::new(
"host",
"table",
ExternType::Table(TableType::new(Type::FuncRef, 1, None))
),
ImportType::new(
"host",
"global",
ExternType::Global(GlobalType::new(Type::I32, Mutability::Const))
)
]
);
// Now we test the iterators
assert_eq!(
module.imports().functions().collect::<Vec<_>>(),
vec![ImportType::new(
"host",
"func",
FunctionType::new(vec![], vec![])
),]
);
assert_eq!(
module.imports().memories().collect::<Vec<_>>(),
vec![ImportType::new(
"host",
"memory",
MemoryType::new(Pages(1), None, false)
),]
);
assert_eq!(
module.imports().tables().collect::<Vec<_>>(),
vec![ImportType::new(
"host",
"table",
TableType::new(Type::FuncRef, 1, None)
),]
);
assert_eq!(
module.imports().globals().collect::<Vec<_>>(),
vec![ImportType::new(
"host",
"global",
GlobalType::new(Type::I32, Mutability::Const)
),]
);
Ok(())
}
#[test]
fn exports() -> Result<()> {
let store = Store::default();
let wat = r#"(module
(func (export "func") nop)
(memory (export "memory") 1)
(table (export "table") 1 funcref)
(global (export "global") i32 (i32.const 0))
)"#;
let module = Module::new(&store, wat)?;
assert_eq!(
module.exports().collect::<Vec<_>>(),
vec![
ExportType::new(
"func",
ExternType::Function(FunctionType::new(vec![], vec![]))
),
ExportType::new(
"memory",
ExternType::Memory(MemoryType::new(Pages(1), None, false))
),
ExportType::new(
"table",
ExternType::Table(TableType::new(Type::FuncRef, 1, None))
),
ExportType::new(
"global",
ExternType::Global(GlobalType::new(Type::I32, Mutability::Const))
)
]
);
// Now we test the iterators
assert_eq!(
module.exports().functions().collect::<Vec<_>>(),
vec![ExportType::new("func", FunctionType::new(vec![], vec![])),]
);
assert_eq!(
module.exports().memories().collect::<Vec<_>>(),
vec![ExportType::new(
"memory",
MemoryType::new(Pages(1), None, false)
),]
);
assert_eq!(
module.exports().tables().collect::<Vec<_>>(),
vec![ExportType::new(
"table",
TableType::new(Type::FuncRef, 1, None)
),]
);
assert_eq!(
module.exports().globals().collect::<Vec<_>>(),
vec![ExportType::new(
"global",
GlobalType::new(Type::I32, Mutability::Const)
),]
);
Ok(())
}
#[test]
fn calling_host_functions_with_negative_values_works() -> Result<()> {
let store = Store::default();
let wat = r#"(module
(import "host" "host_func1" (func (param i64)))
(import "host" "host_func2" (func (param i32)))
(import "host" "host_func3" (func (param i64)))
(import "host" "host_func4" (func (param i32)))
(import "host" "host_func5" (func (param i32)))
(import "host" "host_func6" (func (param i32)))
(import "host" "host_func7" (func (param i32)))
(import "host" "host_func8" (func (param i32)))
(func (export "call_host_func1")
(call 0 (i64.const -1)))
(func (export "call_host_func2")
(call 1 (i32.const -1)))
(func (export "call_host_func3")
(call 2 (i64.const -1)))
(func (export "call_host_func4")
(call 3 (i32.const -1)))
(func (export "call_host_func5")
(call 4 (i32.const -1)))
(func (export "call_host_func6")
(call 5 (i32.const -1)))
(func (export "call_host_func7")
(call 6 (i32.const -1)))
(func (export "call_host_func8")
(call 7 (i32.const -1)))
)"#;
let module = Module::new(&store, wat)?;
let imports = imports! {
"host" => {
"host_func1" => Function::new_native(&store, |p: u64| {
println!("host_func1: Found number {}", p);
assert_eq!(p, u64::max_value());
}),
"host_func2" => Function::new_native(&store, |p: u32| {
println!("host_func2: Found number {}", p);
assert_eq!(p, u32::max_value());
}),
"host_func3" => Function::new_native(&store, |p: i64| {
println!("host_func3: Found number {}", p);
assert_eq!(p, -1);
}),
"host_func4" => Function::new_native(&store, |p: i32| {
println!("host_func4: Found number {}", p);
assert_eq!(p, -1);
}),
"host_func5" => Function::new_native(&store, |p: i16| {
println!("host_func5: Found number {}", p);
assert_eq!(p, -1);
}),
"host_func6" => Function::new_native(&store, |p: u16| {
println!("host_func6: Found number {}", p);
assert_eq!(p, u16::max_value());
}),
"host_func7" => Function::new_native(&store, |p: i8| {
println!("host_func7: Found number {}", p);
assert_eq!(p, -1);
}),
"host_func8" => Function::new_native(&store, |p: u8| {
println!("host_func8: Found number {}", p);
assert_eq!(p, u8::max_value());
}),
}
};
let instance = Instance::new(&module, &imports)?;
let f1: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_func1")?;
let f2: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_func2")?;
let f3: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_func3")?;
let f4: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_func4")?;
let f5: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_func5")?;
let f6: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_func6")?;
let f7: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_func7")?;
let f8: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_func8")?;
f1.call()?;
f2.call()?;
f3.call()?;
f4.call()?;
f5.call()?;
f6.call()?;
f7.call()?;
f8.call()?;
Ok(())
}

View File

@@ -1,497 +0,0 @@
use anyhow::Result;
use std::collections::HashMap;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use wasmer::*;
#[test]
fn func_ref_passed_and_returned() -> Result<()> {
let store = Store::default();
let wat = r#"(module
(import "env" "func_ref_identity" (func (param funcref) (result funcref)))
(type $ret_i32_ty (func (result i32)))
(table $table (export "table") 2 2 funcref)
(func (export "run") (param) (result funcref)
(call 0 (ref.null func)))
(func (export "call_set_value") (param $fr funcref) (result i32)
(table.set $table (i32.const 0) (local.get $fr))
(call_indirect $table (type $ret_i32_ty) (i32.const 0)))
)"#;
let module = Module::new(&store, wat)?;
let imports = imports! {
"env" => {
"func_ref_identity" => Function::new(&store, FunctionType::new([Type::FuncRef], [Type::FuncRef]), |values| -> Result<Vec<_>, _> {
Ok(vec![values[0].clone()])
})
},
};
let instance = Instance::new(&module, &imports)?;
let f: &Function = instance.exports.get_function("run")?;
let results = f.call(&[]).unwrap();
if let Value::FuncRef(fr) = &results[0] {
assert!(fr.is_none());
} else {
panic!("funcref not found!");
}
#[derive(Clone, Debug, WasmerEnv)]
pub struct Env(Arc<AtomicBool>);
let env = Env(Arc::new(AtomicBool::new(false)));
let func_to_call = Function::new_native_with_env(&store, env.clone(), |env: &Env| -> i32 {
env.0.store(true, Ordering::SeqCst);
343
});
let call_set_value: &Function = instance.exports.get_function("call_set_value")?;
let results: Box<[Value]> = call_set_value.call(&[Value::FuncRef(Some(func_to_call))])?;
assert!(env.0.load(Ordering::SeqCst));
assert_eq!(&*results, &[Value::I32(343)]);
Ok(())
}
#[test]
fn func_ref_passed_and_called() -> Result<()> {
let store = Store::default();
let wat = r#"(module
(func $func_ref_call (import "env" "func_ref_call") (param funcref) (result i32))
(type $ret_i32_ty (func (result i32)))
(table $table (export "table") 2 2 funcref)
(func $product (param $x i32) (param $y i32) (result i32)
(i32.mul (local.get $x) (local.get $y)))
;; TODO: figure out exactly why this statement is needed
(elem declare func $product)
(func (export "call_set_value") (param $fr funcref) (result i32)
(table.set $table (i32.const 0) (local.get $fr))
(call_indirect $table (type $ret_i32_ty) (i32.const 0)))
(func (export "call_func") (param $fr funcref) (result i32)
(call $func_ref_call (local.get $fr)))
(func (export "call_host_func_with_wasm_func") (result i32)
(call $func_ref_call (ref.func $product)))
)"#;
let module = Module::new(&store, wat)?;
fn func_ref_call(values: &[Value]) -> Result<Vec<Value>, RuntimeError> {
// TODO: look into `Box<[Value]>` being returned breakage
let f = values[0].unwrap_funcref().as_ref().unwrap();
let f: NativeFunc<(i32, i32), i32> = f.native()?;
Ok(vec![Value::I32(f.call(7, 9)?)])
}
let imports = imports! {
"env" => {
"func_ref_call" => Function::new(
&store,
FunctionType::new([Type::FuncRef], [Type::I32]),
func_ref_call
),
// TODO(reftypes): this should work
/*
"func_ref_call_native" => Function::new_native(&store, |f: Function| -> Result<i32, RuntimeError> {
let f: NativeFunc::<(i32, i32), i32> = f.native()?;
f.call(7, 9)
})
*/
},
};
let instance = Instance::new(&module, &imports)?;
{
fn sum(a: i32, b: i32) -> i32 {
a + b
}
let sum_func = Function::new_native(&store, sum);
let call_func: &Function = instance.exports.get_function("call_func")?;
let result = call_func.call(&[Value::FuncRef(Some(sum_func))])?;
assert_eq!(result[0].unwrap_i32(), 16);
}
{
let f: NativeFunc<(), i32> = instance
.exports
.get_native_function("call_host_func_with_wasm_func")?;
let result = f.call()?;
assert_eq!(result, 63);
}
Ok(())
}
#[cfg(feature = "experimental-reference-types-extern-ref")]
#[test]
fn extern_ref_passed_and_returned() -> Result<()> {
let store = Store::default();
let wat = r#"(module
(func $extern_ref_identity (import "env" "extern_ref_identity") (param externref) (result externref))
(func $extern_ref_identity_native (import "env" "extern_ref_identity_native") (param externref) (result externref))
(func $get_new_extern_ref (import "env" "get_new_extern_ref") (result externref))
(func $get_new_extern_ref_native (import "env" "get_new_extern_ref_native") (result externref))
(func (export "run") (param) (result externref)
(call $extern_ref_identity (ref.null extern)))
(func (export "run_native") (param) (result externref)
(call $extern_ref_identity_native (ref.null extern)))
(func (export "get_hashmap") (param) (result externref)
(call $get_new_extern_ref))
(func (export "get_hashmap_native") (param) (result externref)
(call $get_new_extern_ref_native))
)"#;
let module = Module::new(&store, wat)?;
let imports = imports! {
"env" => {
"extern_ref_identity" => Function::new(&store, FunctionType::new([Type::ExternRef], [Type::ExternRef]), |values| -> Result<Vec<_>, _> {
Ok(vec![values[0].clone()])
}),
"extern_ref_identity_native" => Function::new_native(&store, |er: ExternRef| -> ExternRef {
er
}),
"get_new_extern_ref" => Function::new(&store, FunctionType::new([], [Type::ExternRef]), |_| -> Result<Vec<_>, _> {
let inner =
[("hello".to_string(), "world".to_string()),
("color".to_string(), "orange".to_string())]
.iter()
.cloned()
.collect::<HashMap<String, String>>();
let new_extern_ref = ExternRef::new(inner);
Ok(vec![Value::ExternRef(new_extern_ref)])
}),
"get_new_extern_ref_native" => Function::new_native(&store, || -> ExternRef {
let inner =
[("hello".to_string(), "world".to_string()),
("color".to_string(), "orange".to_string())]
.iter()
.cloned()
.collect::<HashMap<String, String>>();
ExternRef::new(inner)
})
},
};
let instance = Instance::new(&module, &imports)?;
for run in &["run", "run_native"] {
let f: &Function = instance.exports.get_function(run)?;
let results = f.call(&[]).unwrap();
if let Value::ExternRef(er) = &results[0] {
assert!(er.is_null());
} else {
panic!("result is not an extern ref!");
}
let f: NativeFunc<(), ExternRef> = instance.exports.get_native_function(run)?;
let result: ExternRef = f.call()?;
assert!(result.is_null());
}
for get_hashmap in &["get_hashmap", "get_hashmap_native"] {
let f: &Function = instance.exports.get_function(get_hashmap)?;
let results = f.call(&[]).unwrap();
if let Value::ExternRef(er) = &results[0] {
let inner: &HashMap<String, String> = er.downcast().unwrap();
assert_eq!(inner["hello"], "world");
assert_eq!(inner["color"], "orange");
} else {
panic!("result is not an extern ref!");
}
let f: NativeFunc<(), ExternRef> = instance.exports.get_native_function(get_hashmap)?;
let result: ExternRef = f.call()?;
let inner: &HashMap<String, String> = result.downcast().unwrap();
assert_eq!(inner["hello"], "world");
assert_eq!(inner["color"], "orange");
}
Ok(())
}
#[cfg(feature = "experimental-reference-types-extern-ref")]
#[test]
// TODO(reftypes): reenable this test
#[ignore]
fn extern_ref_ref_counting_basic() -> Result<()> {
let store = Store::default();
let wat = r#"(module
(func (export "drop") (param $er externref) (result)
(drop (local.get $er)))
)"#;
let module = Module::new(&store, wat)?;
let instance = Instance::new(&module, &imports! {})?;
let f: NativeFunc<ExternRef, ()> = instance.exports.get_native_function("drop")?;
let er = ExternRef::new(3u32);
f.call(er.clone())?;
assert_eq!(er.downcast::<u32>().unwrap(), &3);
assert_eq!(er.strong_count(), 1);
Ok(())
}
#[cfg(feature = "experimental-reference-types-extern-ref")]
#[test]
fn refs_in_globals() -> Result<()> {
let store = Store::default();
let wat = r#"(module
(global $er_global (export "er_global") (mut externref) (ref.null extern))
(global $fr_global (export "fr_global") (mut funcref) (ref.null func))
(global $fr_immutable_global (export "fr_immutable_global") funcref (ref.func $hello))
(func $hello (param) (result i32)
(i32.const 73))
)"#;
let module = Module::new(&store, wat)?;
let instance = Instance::new(&module, &imports! {})?;
{
let er_global: &Global = instance.exports.get_global("er_global")?;
if let Value::ExternRef(er) = er_global.get() {
assert!(er.is_null());
} else {
panic!("Did not find extern ref in the global");
}
er_global.set(Val::ExternRef(ExternRef::new(3u32)))?;
if let Value::ExternRef(er) = er_global.get() {
assert_eq!(er.downcast::<u32>().unwrap(), &3);
assert_eq!(er.strong_count(), 1);
} else {
panic!("Did not find extern ref in the global");
}
}
{
let fr_global: &Global = instance.exports.get_global("fr_immutable_global")?;
if let Value::FuncRef(Some(f)) = fr_global.get() {
let native_func: NativeFunc<(), u32> = f.native()?;
assert_eq!(native_func.call()?, 73);
} else {
panic!("Did not find non-null func ref in the global");
}
}
{
let fr_global: &Global = instance.exports.get_global("fr_global")?;
if let Value::FuncRef(None) = fr_global.get() {
} else {
panic!("Did not find a null func ref in the global");
}
let f = Function::new_native(&store, |arg1: i32, arg2: i32| -> i32 { arg1 + arg2 });
fr_global.set(Val::FuncRef(Some(f)))?;
if let Value::FuncRef(Some(f)) = fr_global.get() {
let native: NativeFunc<(i32, i32), i32> = f.native()?;
assert_eq!(native.call(5, 7)?, 12);
} else {
panic!("Did not find extern ref in the global");
}
}
Ok(())
}
#[cfg(feature = "experimental-reference-types-extern-ref")]
#[test]
fn extern_ref_ref_counting_table_basic() -> Result<()> {
let store = Store::default();
let wat = r#"(module
(global $global (export "global") (mut externref) (ref.null extern))
(table $table (export "table") 4 4 externref)
(func $insert (param $er externref) (param $idx i32)
(table.set $table (local.get $idx) (local.get $er)))
(func $intermediate (param $er externref) (param $idx i32)
(call $insert (local.get $er) (local.get $idx)))
(func $insert_into_table (export "insert_into_table") (param $er externref) (param $idx i32) (result externref)
(call $intermediate (local.get $er) (local.get $idx))
(local.get $er))
)"#;
let module = Module::new(&store, wat)?;
let instance = Instance::new(&module, &imports! {})?;
let f: NativeFunc<(ExternRef, i32), ExternRef> =
instance.exports.get_native_function("insert_into_table")?;
let er = ExternRef::new(3usize);
let er = f.call(er, 1)?;
assert_eq!(er.strong_count(), 2);
let table: &Table = instance.exports.get_table("table")?;
{
let er2 = table.get(1).unwrap().externref().unwrap();
assert_eq!(er2.strong_count(), 3);
}
assert_eq!(er.strong_count(), 2);
table.set(1, Val::ExternRef(ExternRef::null()))?;
assert_eq!(er.strong_count(), 1);
Ok(())
}
#[cfg(feature = "experimental-reference-types-extern-ref")]
#[test]
// TODO(reftypes): reenable this test
#[ignore]
fn extern_ref_ref_counting_global_basic() -> Result<()> {
let store = Store::default();
let wat = r#"(module
(global $global (export "global") (mut externref) (ref.null extern))
(func $get_from_global (export "get_from_global") (result externref)
(drop (global.get $global))
(global.get $global))
)"#;
let module = Module::new(&store, wat)?;
let instance = Instance::new(&module, &imports! {})?;
let global: &Global = instance.exports.get_global("global")?;
{
let er = ExternRef::new(3usize);
global.set(Val::ExternRef(er.clone()))?;
assert_eq!(er.strong_count(), 2);
}
let get_from_global: NativeFunc<(), ExternRef> =
instance.exports.get_native_function("get_from_global")?;
let er = get_from_global.call()?;
assert_eq!(er.strong_count(), 2);
global.set(Val::ExternRef(ExternRef::null()))?;
assert_eq!(er.strong_count(), 1);
Ok(())
}
#[cfg(feature = "experimental-reference-types-extern-ref")]
#[test]
// TODO(reftypes): reenable this test
#[ignore]
fn extern_ref_ref_counting_traps() -> Result<()> {
let store = Store::default();
let wat = r#"(module
(func $pass_er (export "pass_extern_ref") (param externref)
(local.get 0)
(unreachable))
)"#;
let module = Module::new(&store, wat)?;
let instance = Instance::new(&module, &imports! {})?;
let pass_extern_ref: NativeFunc<ExternRef, ()> =
instance.exports.get_native_function("pass_extern_ref")?;
let er = ExternRef::new(3usize);
assert_eq!(er.strong_count(), 1);
let result = pass_extern_ref.call(er.clone());
assert!(result.is_err());
assert_eq!(er.strong_count(), 1);
Ok(())
}
#[cfg(feature = "experimental-reference-types-extern-ref")]
#[test]
fn extern_ref_ref_counting_table_instructions() -> Result<()> {
let store = Store::default();
let wat = r#"(module
(table $table1 (export "table1") 2 12 externref)
(table $table2 (export "table2") 6 12 externref)
(func $grow_table_with_ref (export "grow_table_with_ref") (param $er externref) (param $size i32) (result i32)
(table.grow $table1 (local.get $er) (local.get $size)))
(func $fill_table_with_ref (export "fill_table_with_ref") (param $er externref) (param $start i32) (param $end i32)
(table.fill $table1 (local.get $start) (local.get $er) (local.get $end)))
(func $copy_into_table2 (export "copy_into_table2")
(table.copy $table2 $table1 (i32.const 0) (i32.const 0) (i32.const 4)))
)"#;
let module = Module::new(&store, wat)?;
let instance = Instance::new(&module, &imports! {})?;
let grow_table_with_ref: NativeFunc<(ExternRef, i32), i32> = instance
.exports
.get_native_function("grow_table_with_ref")?;
let fill_table_with_ref: NativeFunc<(ExternRef, i32, i32), ()> = instance
.exports
.get_native_function("fill_table_with_ref")?;
let copy_into_table2: NativeFunc<(), ()> =
instance.exports.get_native_function("copy_into_table2")?;
let table1: &Table = instance.exports.get_table("table1")?;
let table2: &Table = instance.exports.get_table("table2")?;
let er1 = ExternRef::new(3usize);
let er2 = ExternRef::new(5usize);
let er3 = ExternRef::new(7usize);
{
let result = grow_table_with_ref.call(er1.clone(), 0)?;
assert_eq!(result, 2);
assert_eq!(er1.strong_count(), 1);
let result = grow_table_with_ref.call(er1.clone(), 10_000)?;
assert_eq!(result, -1);
assert_eq!(er1.strong_count(), 1);
let result = grow_table_with_ref.call(er1.clone(), 8)?;
assert_eq!(result, 2);
assert_eq!(er1.strong_count(), 9);
for i in 2..10 {
let e = table1.get(i).unwrap().unwrap_externref();
assert_eq!(*e.downcast::<usize>().unwrap(), 3);
assert_eq!(&e, &er1);
}
assert_eq!(er1.strong_count(), 9);
}
{
fill_table_with_ref.call(er2.clone(), 0, 2)?;
assert_eq!(er2.strong_count(), 3);
}
{
table2.set(0, Val::ExternRef(er3.clone()))?;
table2.set(1, Val::ExternRef(er3.clone()))?;
table2.set(2, Val::ExternRef(er3.clone()))?;
table2.set(3, Val::ExternRef(er3.clone()))?;
table2.set(4, Val::ExternRef(er3.clone()))?;
assert_eq!(er3.strong_count(), 6);
}
{
copy_into_table2.call()?;
assert_eq!(er3.strong_count(), 2);
assert_eq!(er2.strong_count(), 5);
assert_eq!(er1.strong_count(), 11);
for i in 1..5 {
let e = table2.get(i).unwrap().unwrap_externref();
let value = e.downcast::<usize>().unwrap();
match i {
0 | 1 => assert_eq!(*value, 5),
4 => assert_eq!(*value, 7),
_ => assert_eq!(*value, 3),
}
}
}
{
for i in 0..table1.size() {
table1.set(i, Val::ExternRef(ExternRef::null()))?;
}
for i in 0..table2.size() {
table2.set(i, Val::ExternRef(ExternRef::null()))?;
}
}
assert_eq!(er1.strong_count(), 1);
assert_eq!(er2.strong_count(), 1);
assert_eq!(er3.strong_count(), 1);
Ok(())
}

339
lib/api/tests/sys/export.rs Normal file
View File

@@ -0,0 +1,339 @@
#[cfg(feature = "sys")]
mod sys {
use anyhow::Result;
use wasmer::*;
use wasmer_vm::WeakOrStrongInstanceRef;
const MEM_WAT: &str = "
(module
(func $host_fn (import \"env\" \"host_fn\") (param) (result))
(func (export \"call_host_fn\") (param) (result)
(call $host_fn))
(memory $mem 0)
(export \"memory\" (memory $mem))
)
";
const GLOBAL_WAT: &str = "
(module
(func $host_fn (import \"env\" \"host_fn\") (param) (result))
(func (export \"call_host_fn\") (param) (result)
(call $host_fn))
(global $global i32 (i32.const 11))
(export \"global\" (global $global))
)
";
const TABLE_WAT: &str = "
(module
(func $host_fn (import \"env\" \"host_fn\") (param) (result))
(func (export \"call_host_fn\") (param) (result)
(call $host_fn))
(table $table 4 4 funcref)
(export \"table\" (table $table))
)
";
const FUNCTION_WAT: &str = "
(module
(func $host_fn (import \"env\" \"host_fn\") (param) (result))
(func (export \"call_host_fn\") (param) (result)
(call $host_fn))
)
";
fn is_memory_instance_ref_strong(memory: &Memory) -> Option<bool> {
// This is safe because we're calling it from a test to test the internals
unsafe {
memory
.get_vm_memory()
.instance_ref
.as_ref()
.map(|v| matches!(v, WeakOrStrongInstanceRef::Strong(_)))
}
}
fn is_table_instance_ref_strong(table: &Table) -> Option<bool> {
// This is safe because we're calling it from a test to test the internals
unsafe {
table
.get_vm_table()
.instance_ref
.as_ref()
.map(|v| matches!(v, WeakOrStrongInstanceRef::Strong(_)))
}
}
fn is_global_instance_ref_strong(global: &Global) -> Option<bool> {
// This is safe because we're calling it from a test to test the internals
unsafe {
global
.get_vm_global()
.instance_ref
.as_ref()
.map(|v| matches!(v, WeakOrStrongInstanceRef::Strong(_)))
}
}
fn is_function_instance_ref_strong(f: &Function) -> Option<bool> {
// This is safe because we're calling it from a test to test the internals
unsafe {
f.get_vm_function()
.instance_ref
.as_ref()
.map(|v| matches!(v, WeakOrStrongInstanceRef::Strong(_)))
}
}
fn is_native_function_instance_ref_strong<Args, Rets>(
f: &NativeFunc<Args, Rets>,
) -> Option<bool>
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
// This is safe because we're calling it from a test to test the internals
unsafe {
f.get_vm_function()
.instance_ref
.as_ref()
.map(|v| matches!(v, WeakOrStrongInstanceRef::Strong(_)))
}
}
#[test]
fn strong_weak_behavior_works_memory() -> Result<()> {
#[derive(Clone, Debug, WasmerEnv, Default)]
struct MemEnv {
#[wasmer(export)]
memory: LazyInit<Memory>,
}
let host_fn = |env: &MemEnv| {
let mem = env.memory_ref().unwrap();
assert_eq!(is_memory_instance_ref_strong(&mem), Some(false));
let mem_clone = mem.clone();
assert_eq!(is_memory_instance_ref_strong(&mem_clone), Some(true));
assert_eq!(is_memory_instance_ref_strong(&mem), Some(false));
};
let f: NativeFunc<(), ()> = {
let store = Store::default();
let module = Module::new(&store, MEM_WAT)?;
let env = MemEnv::default();
let instance = Instance::new(
&module,
&imports! {
"env" => {
"host_fn" => Function::new_native_with_env(&store, env, host_fn)
}
},
)?;
{
let mem = instance.exports.get_memory("memory")?;
assert_eq!(is_memory_instance_ref_strong(&mem), Some(true));
}
let f: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_fn")?;
f.call()?;
f
};
f.call()?;
Ok(())
}
#[test]
fn strong_weak_behavior_works_global() -> Result<()> {
#[derive(Clone, Debug, WasmerEnv, Default)]
struct GlobalEnv {
#[wasmer(export)]
global: LazyInit<Global>,
}
let host_fn = |env: &GlobalEnv| {
let global = env.global_ref().unwrap();
assert_eq!(is_global_instance_ref_strong(&global), Some(false));
let global_clone = global.clone();
assert_eq!(is_global_instance_ref_strong(&global_clone), Some(true));
assert_eq!(is_global_instance_ref_strong(&global), Some(false));
};
let f: NativeFunc<(), ()> = {
let store = Store::default();
let module = Module::new(&store, GLOBAL_WAT)?;
let env = GlobalEnv::default();
let instance = Instance::new(
&module,
&imports! {
"env" => {
"host_fn" => Function::new_native_with_env(&store, env, host_fn)
}
},
)?;
{
let global = instance.exports.get_global("global")?;
assert_eq!(is_global_instance_ref_strong(&global), Some(true));
}
let f: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_fn")?;
f.call()?;
f
};
f.call()?;
Ok(())
}
#[test]
fn strong_weak_behavior_works_table() -> Result<()> {
#[derive(Clone, WasmerEnv, Default)]
struct TableEnv {
#[wasmer(export)]
table: LazyInit<Table>,
}
let host_fn = |env: &TableEnv| {
let table = env.table_ref().unwrap();
assert_eq!(is_table_instance_ref_strong(&table), Some(false));
let table_clone = table.clone();
assert_eq!(is_table_instance_ref_strong(&table_clone), Some(true));
assert_eq!(is_table_instance_ref_strong(&table), Some(false));
};
let f: NativeFunc<(), ()> = {
let store = Store::default();
let module = Module::new(&store, TABLE_WAT)?;
let env = TableEnv::default();
let instance = Instance::new(
&module,
&imports! {
"env" => {
"host_fn" => Function::new_native_with_env(&store, env, host_fn)
}
},
)?;
{
let table = instance.exports.get_table("table")?;
assert_eq!(is_table_instance_ref_strong(&table), Some(true));
}
let f: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_fn")?;
f.call()?;
f
};
f.call()?;
Ok(())
}
#[test]
fn strong_weak_behavior_works_function() -> Result<()> {
#[derive(Clone, WasmerEnv, Default)]
struct FunctionEnv {
#[wasmer(export)]
call_host_fn: LazyInit<Function>,
}
let host_fn = |env: &FunctionEnv| {
let function = env.call_host_fn_ref().unwrap();
assert_eq!(is_function_instance_ref_strong(&function), Some(false));
let function_clone = function.clone();
assert_eq!(is_function_instance_ref_strong(&function_clone), Some(true));
assert_eq!(is_function_instance_ref_strong(&function), Some(false));
};
let f: NativeFunc<(), ()> = {
let store = Store::default();
let module = Module::new(&store, FUNCTION_WAT)?;
let env = FunctionEnv::default();
let instance = Instance::new(
&module,
&imports! {
"env" => {
"host_fn" => Function::new_native_with_env(&store, env, host_fn)
}
},
)?;
{
let function = instance.exports.get_function("call_host_fn")?;
assert_eq!(is_function_instance_ref_strong(&function), Some(true));
}
let f: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_fn")?;
f.call()?;
f
};
f.call()?;
Ok(())
}
#[test]
fn strong_weak_behavior_works_native_function() -> Result<()> {
#[derive(Clone, WasmerEnv, Default)]
struct FunctionEnv {
#[wasmer(export)]
call_host_fn: LazyInit<NativeFunc<(), ()>>,
}
let host_fn = |env: &FunctionEnv| {
let function = env.call_host_fn_ref().unwrap();
assert_eq!(
is_native_function_instance_ref_strong(&function),
Some(false)
);
let function_clone = function.clone();
assert_eq!(
is_native_function_instance_ref_strong(&function_clone),
Some(true)
);
assert_eq!(
is_native_function_instance_ref_strong(&function),
Some(false)
);
};
let f: NativeFunc<(), ()> = {
let store = Store::default();
let module = Module::new(&store, FUNCTION_WAT)?;
let env = FunctionEnv::default();
let instance = Instance::new(
&module,
&imports! {
"env" => {
"host_fn" => Function::new_native_with_env(&store, env, host_fn)
}
},
)?;
{
let function: NativeFunc<(), ()> =
instance.exports.get_native_function("call_host_fn")?;
assert_eq!(
is_native_function_instance_ref_strong(&function),
Some(true)
);
}
let f: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_fn")?;
f.call()?;
f
};
f.call()?;
Ok(())
}
}

View File

@@ -0,0 +1,467 @@
#[cfg(feature = "sys")]
mod sys {
use anyhow::Result;
use wasmer::*;
#[test]
fn global_new() -> Result<()> {
let store = Store::default();
let global = Global::new(&store, Value::I32(10));
assert_eq!(
*global.ty(),
GlobalType {
ty: Type::I32,
mutability: Mutability::Const
}
);
let global_mut = Global::new_mut(&store, Value::I32(10));
assert_eq!(
*global_mut.ty(),
GlobalType {
ty: Type::I32,
mutability: Mutability::Var
}
);
Ok(())
}
#[test]
fn global_get() -> Result<()> {
let store = Store::default();
let global_i32 = Global::new(&store, Value::I32(10));
assert_eq!(global_i32.get(), Value::I32(10));
let global_i64 = Global::new(&store, Value::I64(20));
assert_eq!(global_i64.get(), Value::I64(20));
let global_f32 = Global::new(&store, Value::F32(10.0));
assert_eq!(global_f32.get(), Value::F32(10.0));
let global_f64 = Global::new(&store, Value::F64(20.0));
assert_eq!(global_f64.get(), Value::F64(20.0));
Ok(())
}
#[test]
fn global_set() -> Result<()> {
let store = Store::default();
let global_i32 = Global::new(&store, Value::I32(10));
// Set on a constant should error
assert!(global_i32.set(Value::I32(20)).is_err());
let global_i32_mut = Global::new_mut(&store, Value::I32(10));
// Set on different type should error
assert!(global_i32_mut.set(Value::I64(20)).is_err());
// Set on same type should succeed
global_i32_mut.set(Value::I32(20))?;
assert_eq!(global_i32_mut.get(), Value::I32(20));
Ok(())
}
#[test]
fn table_new() -> Result<()> {
let store = Store::default();
let table_type = TableType {
ty: Type::FuncRef,
minimum: 0,
maximum: None,
};
let f = Function::new_native(&store, || {});
let table = Table::new(&store, table_type, Value::FuncRef(Some(f)))?;
assert_eq!(*table.ty(), table_type);
// Anyrefs not yet supported
// let table_type = TableType {
// ty: Type::ExternRef,
// minimum: 0,
// maximum: None,
// };
// let table = Table::new(&store, table_type, Value::ExternRef(ExternRef::Null))?;
// assert_eq!(*table.ty(), table_type);
Ok(())
}
#[test]
#[ignore]
fn table_get() -> Result<()> {
let store = Store::default();
let table_type = TableType {
ty: Type::FuncRef,
minimum: 0,
maximum: Some(1),
};
let f = Function::new_native(&store, |num: i32| num + 1);
let table = Table::new(&store, table_type, Value::FuncRef(Some(f.clone())))?;
assert_eq!(*table.ty(), table_type);
let _elem = table.get(0).unwrap();
// assert_eq!(elem.funcref().unwrap(), f);
Ok(())
}
#[test]
#[ignore]
fn table_set() -> Result<()> {
// Table set not yet tested
Ok(())
}
#[test]
fn table_grow() -> Result<()> {
let store = Store::default();
let table_type = TableType {
ty: Type::FuncRef,
minimum: 0,
maximum: Some(10),
};
let f = Function::new_native(&store, |num: i32| num + 1);
let table = Table::new(&store, table_type, Value::FuncRef(Some(f.clone())))?;
// Growing to a bigger maximum should return None
let old_len = table.grow(12, Value::FuncRef(Some(f.clone())));
assert!(old_len.is_err());
// Growing to a bigger maximum should return None
let old_len = table.grow(5, Value::FuncRef(Some(f.clone())))?;
assert_eq!(old_len, 0);
Ok(())
}
#[test]
#[ignore]
fn table_copy() -> Result<()> {
// TODO: table copy test not yet implemented
Ok(())
}
#[test]
fn memory_new() -> Result<()> {
let store = Store::default();
let memory_type = MemoryType {
shared: false,
minimum: Pages(0),
maximum: Some(Pages(10)),
};
let memory = Memory::new(&store, memory_type)?;
assert_eq!(memory.size(), Pages(0));
assert_eq!(memory.ty(), memory_type);
Ok(())
}
#[test]
fn memory_grow() -> Result<()> {
let store = Store::default();
let desc = MemoryType::new(Pages(10), Some(Pages(16)), false);
let memory = Memory::new(&store, desc)?;
assert_eq!(memory.size(), Pages(10));
let result = memory.grow(Pages(2)).unwrap();
assert_eq!(result, Pages(10));
assert_eq!(memory.size(), Pages(12));
let result = memory.grow(Pages(10));
assert_eq!(
result,
Err(MemoryError::CouldNotGrow {
current: 12.into(),
attempted_delta: 10.into()
})
);
let bad_desc = MemoryType::new(Pages(15), Some(Pages(10)), false);
let bad_result = Memory::new(&store, bad_desc);
assert!(matches!(bad_result, Err(MemoryError::InvalidMemory { .. })));
Ok(())
}
#[test]
fn function_new() -> Result<()> {
let store = Store::default();
let function = Function::new_native(&store, || {});
assert_eq!(function.ty().clone(), FunctionType::new(vec![], vec![]));
let function = Function::new_native(&store, |_a: i32| {});
assert_eq!(
function.ty().clone(),
FunctionType::new(vec![Type::I32], vec![])
);
let function = Function::new_native(&store, |_a: i32, _b: i64, _c: f32, _d: f64| {});
assert_eq!(
function.ty().clone(),
FunctionType::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![])
);
let function = Function::new_native(&store, || -> i32 { 1 });
assert_eq!(
function.ty().clone(),
FunctionType::new(vec![], vec![Type::I32])
);
let function =
Function::new_native(&store, || -> (i32, i64, f32, f64) { (1, 2, 3.0, 4.0) });
assert_eq!(
function.ty().clone(),
FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64])
);
Ok(())
}
#[test]
fn function_new_env() -> Result<()> {
let store = Store::default();
#[derive(Clone, WasmerEnv)]
struct MyEnv {}
let my_env = MyEnv {};
let function = Function::new_native_with_env(&store, my_env.clone(), |_env: &MyEnv| {});
assert_eq!(function.ty().clone(), FunctionType::new(vec![], vec![]));
let function =
Function::new_native_with_env(&store, my_env.clone(), |_env: &MyEnv, _a: i32| {});
assert_eq!(
function.ty().clone(),
FunctionType::new(vec![Type::I32], vec![])
);
let function = Function::new_native_with_env(
&store,
my_env.clone(),
|_env: &MyEnv, _a: i32, _b: i64, _c: f32, _d: f64| {},
);
assert_eq!(
function.ty().clone(),
FunctionType::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![])
);
let function =
Function::new_native_with_env(&store, my_env.clone(), |_env: &MyEnv| -> i32 { 1 });
assert_eq!(
function.ty().clone(),
FunctionType::new(vec![], vec![Type::I32])
);
let function = Function::new_native_with_env(
&store,
my_env.clone(),
|_env: &MyEnv| -> (i32, i64, f32, f64) { (1, 2, 3.0, 4.0) },
);
assert_eq!(
function.ty().clone(),
FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64])
);
Ok(())
}
#[test]
fn function_new_dynamic() -> Result<()> {
let store = Store::default();
// Using &FunctionType signature
let function_type = FunctionType::new(vec![], vec![]);
let function = Function::new(&store, &function_type, |_values: &[Value]| unimplemented!());
assert_eq!(function.ty().clone(), function_type);
let function_type = FunctionType::new(vec![Type::I32], vec![]);
let function = Function::new(&store, &function_type, |_values: &[Value]| unimplemented!());
assert_eq!(function.ty().clone(), function_type);
let function_type =
FunctionType::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![]);
let function = Function::new(&store, &function_type, |_values: &[Value]| unimplemented!());
assert_eq!(function.ty().clone(), function_type);
let function_type = FunctionType::new(vec![], vec![Type::I32]);
let function = Function::new(&store, &function_type, |_values: &[Value]| unimplemented!());
assert_eq!(function.ty().clone(), function_type);
let function_type =
FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64]);
let function = Function::new(&store, &function_type, |_values: &[Value]| unimplemented!());
assert_eq!(function.ty().clone(), function_type);
// Using array signature
let function_type = ([Type::V128], [Type::I32, Type::F32, Type::F64]);
let function = Function::new(&store, function_type, |_values: &[Value]| unimplemented!());
assert_eq!(function.ty().params(), [Type::V128]);
assert_eq!(function.ty().results(), [Type::I32, Type::F32, Type::F64]);
Ok(())
}
#[test]
fn function_new_dynamic_env() -> Result<()> {
let store = Store::default();
#[derive(Clone, WasmerEnv)]
struct MyEnv {}
let my_env = MyEnv {};
// Using &FunctionType signature
let function_type = FunctionType::new(vec![], vec![]);
let function = Function::new_with_env(
&store,
&function_type,
my_env.clone(),
|_env: &MyEnv, _values: &[Value]| unimplemented!(),
);
assert_eq!(function.ty().clone(), function_type);
let function_type = FunctionType::new(vec![Type::I32], vec![]);
let function = Function::new_with_env(
&store,
&function_type,
my_env.clone(),
|_env: &MyEnv, _values: &[Value]| unimplemented!(),
);
assert_eq!(function.ty().clone(), function_type);
let function_type =
FunctionType::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![]);
let function = Function::new_with_env(
&store,
&function_type,
my_env.clone(),
|_env: &MyEnv, _values: &[Value]| unimplemented!(),
);
assert_eq!(function.ty().clone(), function_type);
let function_type = FunctionType::new(vec![], vec![Type::I32]);
let function = Function::new_with_env(
&store,
&function_type,
my_env.clone(),
|_env: &MyEnv, _values: &[Value]| unimplemented!(),
);
assert_eq!(function.ty().clone(), function_type);
let function_type =
FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64]);
let function = Function::new_with_env(
&store,
&function_type,
my_env.clone(),
|_env: &MyEnv, _values: &[Value]| unimplemented!(),
);
assert_eq!(function.ty().clone(), function_type);
// Using array signature
let function_type = ([Type::V128], [Type::I32, Type::F32, Type::F64]);
let function = Function::new_with_env(
&store,
function_type,
my_env.clone(),
|_env: &MyEnv, _values: &[Value]| unimplemented!(),
);
assert_eq!(function.ty().params(), [Type::V128]);
assert_eq!(function.ty().results(), [Type::I32, Type::F32, Type::F64]);
Ok(())
}
#[test]
fn native_function_works() -> Result<()> {
let store = Store::default();
let function = Function::new_native(&store, || {});
let native_function: NativeFunc<(), ()> = function.native().unwrap();
let result = native_function.call();
assert!(result.is_ok());
let function = Function::new_native(&store, |a: i32| -> i32 { a + 1 });
let native_function: NativeFunc<i32, i32> = function.native().unwrap();
assert_eq!(native_function.call(3).unwrap(), 4);
fn rust_abi(a: i32, b: i64, c: f32, d: f64) -> u64 {
(a as u64 * 1000) + (b as u64 * 100) + (c as u64 * 10) + (d as u64)
}
let function = Function::new_native(&store, rust_abi);
let native_function: NativeFunc<(i32, i64, f32, f64), u64> = function.native().unwrap();
assert_eq!(native_function.call(8, 4, 1.5, 5.).unwrap(), 8415);
let function = Function::new_native(&store, || -> i32 { 1 });
let native_function: NativeFunc<(), i32> = function.native().unwrap();
assert_eq!(native_function.call().unwrap(), 1);
let function = Function::new_native(&store, |_a: i32| {});
let native_function: NativeFunc<i32, ()> = function.native().unwrap();
assert!(native_function.call(4).is_ok());
let function =
Function::new_native(&store, || -> (i32, i64, f32, f64) { (1, 2, 3.0, 4.0) });
let native_function: NativeFunc<(), (i32, i64, f32, f64)> = function.native().unwrap();
assert_eq!(native_function.call().unwrap(), (1, 2, 3.0, 4.0));
Ok(())
}
#[test]
fn function_outlives_instance() -> Result<()> {
let store = Store::default();
let wat = r#"(module
(type $sum_t (func (param i32 i32) (result i32)))
(func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32)
local.get $x
local.get $y
i32.add)
(export "sum" (func $sum_f)))
"#;
let f = {
let module = Module::new(&store, wat)?;
let instance = Instance::new(&module, &imports! {})?;
let f: NativeFunc<(i32, i32), i32> = instance.exports.get_native_function("sum")?;
assert_eq!(f.call(4, 5)?, 9);
f
};
assert_eq!(f.call(4, 5)?, 9);
Ok(())
}
#[test]
fn weak_instance_ref_externs_after_instance() -> Result<()> {
let store = Store::default();
let wat = r#"(module
(memory (export "mem") 1)
(type $sum_t (func (param i32 i32) (result i32)))
(func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32)
local.get $x
local.get $y
i32.add)
(export "sum" (func $sum_f)))
"#;
let f = {
let module = Module::new(&store, wat)?;
let instance = Instance::new(&module, &imports! {})?;
let f: NativeFunc<(i32, i32), i32> = instance.exports.get_with_generics_weak("sum")?;
assert_eq!(f.call(4, 5)?, 9);
f
};
assert_eq!(f.call(4, 5)?, 9);
Ok(())
}
#[test]
fn manually_generate_wasmer_env() -> Result<()> {
let store = Store::default();
#[derive(WasmerEnv, Clone)]
struct MyEnv {
val: u32,
memory: LazyInit<Memory>,
}
fn host_function(env: &mut MyEnv, arg1: u32, arg2: u32) -> u32 {
env.val + arg1 + arg2
}
let mut env = MyEnv {
val: 5,
memory: LazyInit::new(),
};
let result = host_function(&mut env, 7, 9);
assert_eq!(result, 21);
let memory = Memory::new(&store, MemoryType::new(0, None, false))?;
env.memory.initialize(memory);
let result = host_function(&mut env, 1, 2);
assert_eq!(result, 8);
Ok(())
}
}

View File

@@ -0,0 +1,42 @@
#[cfg(feature = "sys")]
mod sys {
use anyhow::Result;
use wasmer::*;
#[test]
fn exports_work_after_multiple_instances_have_been_freed() -> Result<()> {
let store = Store::default();
let module = Module::new(
&store,
"
(module
(type $sum_t (func (param i32 i32) (result i32)))
(func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32)
local.get $x
local.get $y
i32.add)
(export \"sum\" (func $sum_f)))
",
)?;
let import_object = ImportObject::new();
let instance = Instance::new(&module, &import_object)?;
let instance2 = instance.clone();
let instance3 = instance.clone();
// The function is cloned to “break” the connection with `instance`.
let sum = instance.exports.get_function("sum")?.clone();
drop(instance);
drop(instance2);
drop(instance3);
// All instances have been dropped, but `sum` continues to work!
assert_eq!(
sum.call(&[Value::I32(1), Value::I32(2)])?.into_vec(),
vec![Value::I32(3)],
);
Ok(())
}
}

251
lib/api/tests/sys/module.rs Normal file
View File

@@ -0,0 +1,251 @@
#[cfg(feature = "sys")]
mod sys {
use anyhow::Result;
use wasmer::*;
#[test]
fn module_get_name() -> Result<()> {
let store = Store::default();
let wat = r#"(module)"#;
let module = Module::new(&store, wat)?;
assert_eq!(module.name(), None);
Ok(())
}
#[test]
fn module_set_name() -> Result<()> {
let store = Store::default();
let wat = r#"(module $name)"#;
let mut module = Module::new(&store, wat)?;
assert_eq!(module.name(), Some("name"));
module.set_name("new_name");
assert_eq!(module.name(), Some("new_name"));
Ok(())
}
#[test]
fn imports() -> Result<()> {
let store = Store::default();
let wat = r#"(module
(import "host" "func" (func))
(import "host" "memory" (memory 1))
(import "host" "table" (table 1 anyfunc))
(import "host" "global" (global i32))
)"#;
let module = Module::new(&store, wat)?;
assert_eq!(
module.imports().collect::<Vec<_>>(),
vec![
ImportType::new(
"host",
"func",
ExternType::Function(FunctionType::new(vec![], vec![]))
),
ImportType::new(
"host",
"memory",
ExternType::Memory(MemoryType::new(Pages(1), None, false))
),
ImportType::new(
"host",
"table",
ExternType::Table(TableType::new(Type::FuncRef, 1, None))
),
ImportType::new(
"host",
"global",
ExternType::Global(GlobalType::new(Type::I32, Mutability::Const))
)
]
);
// Now we test the iterators
assert_eq!(
module.imports().functions().collect::<Vec<_>>(),
vec![ImportType::new(
"host",
"func",
FunctionType::new(vec![], vec![])
),]
);
assert_eq!(
module.imports().memories().collect::<Vec<_>>(),
vec![ImportType::new(
"host",
"memory",
MemoryType::new(Pages(1), None, false)
),]
);
assert_eq!(
module.imports().tables().collect::<Vec<_>>(),
vec![ImportType::new(
"host",
"table",
TableType::new(Type::FuncRef, 1, None)
),]
);
assert_eq!(
module.imports().globals().collect::<Vec<_>>(),
vec![ImportType::new(
"host",
"global",
GlobalType::new(Type::I32, Mutability::Const)
),]
);
Ok(())
}
#[test]
fn exports() -> Result<()> {
let store = Store::default();
let wat = r#"(module
(func (export "func") nop)
(memory (export "memory") 1)
(table (export "table") 1 funcref)
(global (export "global") i32 (i32.const 0))
)"#;
let module = Module::new(&store, wat)?;
assert_eq!(
module.exports().collect::<Vec<_>>(),
vec![
ExportType::new(
"func",
ExternType::Function(FunctionType::new(vec![], vec![]))
),
ExportType::new(
"memory",
ExternType::Memory(MemoryType::new(Pages(1), None, false))
),
ExportType::new(
"table",
ExternType::Table(TableType::new(Type::FuncRef, 1, None))
),
ExportType::new(
"global",
ExternType::Global(GlobalType::new(Type::I32, Mutability::Const))
)
]
);
// Now we test the iterators
assert_eq!(
module.exports().functions().collect::<Vec<_>>(),
vec![ExportType::new("func", FunctionType::new(vec![], vec![])),]
);
assert_eq!(
module.exports().memories().collect::<Vec<_>>(),
vec![ExportType::new(
"memory",
MemoryType::new(Pages(1), None, false)
),]
);
assert_eq!(
module.exports().tables().collect::<Vec<_>>(),
vec![ExportType::new(
"table",
TableType::new(Type::FuncRef, 1, None)
),]
);
assert_eq!(
module.exports().globals().collect::<Vec<_>>(),
vec![ExportType::new(
"global",
GlobalType::new(Type::I32, Mutability::Const)
),]
);
Ok(())
}
#[test]
fn calling_host_functions_with_negative_values_works() -> Result<()> {
let store = Store::default();
let wat = r#"(module
(import "host" "host_func1" (func (param i64)))
(import "host" "host_func2" (func (param i32)))
(import "host" "host_func3" (func (param i64)))
(import "host" "host_func4" (func (param i32)))
(import "host" "host_func5" (func (param i32)))
(import "host" "host_func6" (func (param i32)))
(import "host" "host_func7" (func (param i32)))
(import "host" "host_func8" (func (param i32)))
(func (export "call_host_func1")
(call 0 (i64.const -1)))
(func (export "call_host_func2")
(call 1 (i32.const -1)))
(func (export "call_host_func3")
(call 2 (i64.const -1)))
(func (export "call_host_func4")
(call 3 (i32.const -1)))
(func (export "call_host_func5")
(call 4 (i32.const -1)))
(func (export "call_host_func6")
(call 5 (i32.const -1)))
(func (export "call_host_func7")
(call 6 (i32.const -1)))
(func (export "call_host_func8")
(call 7 (i32.const -1)))
)"#;
let module = Module::new(&store, wat)?;
let imports = imports! {
"host" => {
"host_func1" => Function::new_native(&store, |p: u64| {
println!("host_func1: Found number {}", p);
assert_eq!(p, u64::max_value());
}),
"host_func2" => Function::new_native(&store, |p: u32| {
println!("host_func2: Found number {}", p);
assert_eq!(p, u32::max_value());
}),
"host_func3" => Function::new_native(&store, |p: i64| {
println!("host_func3: Found number {}", p);
assert_eq!(p, -1);
}),
"host_func4" => Function::new_native(&store, |p: i32| {
println!("host_func4: Found number {}", p);
assert_eq!(p, -1);
}),
"host_func5" => Function::new_native(&store, |p: i16| {
println!("host_func5: Found number {}", p);
assert_eq!(p, -1);
}),
"host_func6" => Function::new_native(&store, |p: u16| {
println!("host_func6: Found number {}", p);
assert_eq!(p, u16::max_value());
}),
"host_func7" => Function::new_native(&store, |p: i8| {
println!("host_func7: Found number {}", p);
assert_eq!(p, -1);
}),
"host_func8" => Function::new_native(&store, |p: u8| {
println!("host_func8: Found number {}", p);
assert_eq!(p, u8::max_value());
}),
}
};
let instance = Instance::new(&module, &imports)?;
let f1: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_func1")?;
let f2: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_func2")?;
let f3: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_func3")?;
let f4: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_func4")?;
let f5: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_func5")?;
let f6: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_func6")?;
let f7: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_func7")?;
let f8: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_func8")?;
f1.call()?;
f2.call()?;
f3.call()?;
f4.call()?;
f5.call()?;
f6.call()?;
f7.call()?;
f8.call()?;
Ok(())
}
}

View File

@@ -0,0 +1,500 @@
#[cfg(feature = "sys")]
mod sys {
use anyhow::Result;
use std::collections::HashMap;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use wasmer::*;
#[test]
fn func_ref_passed_and_returned() -> Result<()> {
let store = Store::default();
let wat = r#"(module
(import "env" "func_ref_identity" (func (param funcref) (result funcref)))
(type $ret_i32_ty (func (result i32)))
(table $table (export "table") 2 2 funcref)
(func (export "run") (param) (result funcref)
(call 0 (ref.null func)))
(func (export "call_set_value") (param $fr funcref) (result i32)
(table.set $table (i32.const 0) (local.get $fr))
(call_indirect $table (type $ret_i32_ty) (i32.const 0)))
)"#;
let module = Module::new(&store, wat)?;
let imports = imports! {
"env" => {
"func_ref_identity" => Function::new(&store, FunctionType::new([Type::FuncRef], [Type::FuncRef]), |values| -> Result<Vec<_>, _> {
Ok(vec![values[0].clone()])
})
},
};
let instance = Instance::new(&module, &imports)?;
let f: &Function = instance.exports.get_function("run")?;
let results = f.call(&[]).unwrap();
if let Value::FuncRef(fr) = &results[0] {
assert!(fr.is_none());
} else {
panic!("funcref not found!");
}
#[derive(Clone, Debug, WasmerEnv)]
pub struct Env(Arc<AtomicBool>);
let env = Env(Arc::new(AtomicBool::new(false)));
let func_to_call = Function::new_native_with_env(&store, env.clone(), |env: &Env| -> i32 {
env.0.store(true, Ordering::SeqCst);
343
});
let call_set_value: &Function = instance.exports.get_function("call_set_value")?;
let results: Box<[Value]> = call_set_value.call(&[Value::FuncRef(Some(func_to_call))])?;
assert!(env.0.load(Ordering::SeqCst));
assert_eq!(&*results, &[Value::I32(343)]);
Ok(())
}
#[test]
fn func_ref_passed_and_called() -> Result<()> {
let store = Store::default();
let wat = r#"(module
(func $func_ref_call (import "env" "func_ref_call") (param funcref) (result i32))
(type $ret_i32_ty (func (result i32)))
(table $table (export "table") 2 2 funcref)
(func $product (param $x i32) (param $y i32) (result i32)
(i32.mul (local.get $x) (local.get $y)))
;; TODO: figure out exactly why this statement is needed
(elem declare func $product)
(func (export "call_set_value") (param $fr funcref) (result i32)
(table.set $table (i32.const 0) (local.get $fr))
(call_indirect $table (type $ret_i32_ty) (i32.const 0)))
(func (export "call_func") (param $fr funcref) (result i32)
(call $func_ref_call (local.get $fr)))
(func (export "call_host_func_with_wasm_func") (result i32)
(call $func_ref_call (ref.func $product)))
)"#;
let module = Module::new(&store, wat)?;
fn func_ref_call(values: &[Value]) -> Result<Vec<Value>, RuntimeError> {
// TODO: look into `Box<[Value]>` being returned breakage
let f = values[0].unwrap_funcref().as_ref().unwrap();
let f: NativeFunc<(i32, i32), i32> = f.native()?;
Ok(vec![Value::I32(f.call(7, 9)?)])
}
let imports = imports! {
"env" => {
"func_ref_call" => Function::new(
&store,
FunctionType::new([Type::FuncRef], [Type::I32]),
func_ref_call
),
// TODO(reftypes): this should work
/*
"func_ref_call_native" => Function::new_native(&store, |f: Function| -> Result<i32, RuntimeError> {
let f: NativeFunc::<(i32, i32), i32> = f.native()?;
f.call(7, 9)
})
*/
},
};
let instance = Instance::new(&module, &imports)?;
{
fn sum(a: i32, b: i32) -> i32 {
a + b
}
let sum_func = Function::new_native(&store, sum);
let call_func: &Function = instance.exports.get_function("call_func")?;
let result = call_func.call(&[Value::FuncRef(Some(sum_func))])?;
assert_eq!(result[0].unwrap_i32(), 16);
}
{
let f: NativeFunc<(), i32> = instance
.exports
.get_native_function("call_host_func_with_wasm_func")?;
let result = f.call()?;
assert_eq!(result, 63);
}
Ok(())
}
#[cfg(feature = "experimental-reference-types-extern-ref")]
#[test]
fn extern_ref_passed_and_returned() -> Result<()> {
let store = Store::default();
let wat = r#"(module
(func $extern_ref_identity (import "env" "extern_ref_identity") (param externref) (result externref))
(func $extern_ref_identity_native (import "env" "extern_ref_identity_native") (param externref) (result externref))
(func $get_new_extern_ref (import "env" "get_new_extern_ref") (result externref))
(func $get_new_extern_ref_native (import "env" "get_new_extern_ref_native") (result externref))
(func (export "run") (param) (result externref)
(call $extern_ref_identity (ref.null extern)))
(func (export "run_native") (param) (result externref)
(call $extern_ref_identity_native (ref.null extern)))
(func (export "get_hashmap") (param) (result externref)
(call $get_new_extern_ref))
(func (export "get_hashmap_native") (param) (result externref)
(call $get_new_extern_ref_native))
)"#;
let module = Module::new(&store, wat)?;
let imports = imports! {
"env" => {
"extern_ref_identity" => Function::new(&store, FunctionType::new([Type::ExternRef], [Type::ExternRef]), |values| -> Result<Vec<_>, _> {
Ok(vec![values[0].clone()])
}),
"extern_ref_identity_native" => Function::new_native(&store, |er: ExternRef| -> ExternRef {
er
}),
"get_new_extern_ref" => Function::new(&store, FunctionType::new([], [Type::ExternRef]), |_| -> Result<Vec<_>, _> {
let inner =
[("hello".to_string(), "world".to_string()),
("color".to_string(), "orange".to_string())]
.iter()
.cloned()
.collect::<HashMap<String, String>>();
let new_extern_ref = ExternRef::new(inner);
Ok(vec![Value::ExternRef(new_extern_ref)])
}),
"get_new_extern_ref_native" => Function::new_native(&store, || -> ExternRef {
let inner =
[("hello".to_string(), "world".to_string()),
("color".to_string(), "orange".to_string())]
.iter()
.cloned()
.collect::<HashMap<String, String>>();
ExternRef::new(inner)
})
},
};
let instance = Instance::new(&module, &imports)?;
for run in &["run", "run_native"] {
let f: &Function = instance.exports.get_function(run)?;
let results = f.call(&[]).unwrap();
if let Value::ExternRef(er) = &results[0] {
assert!(er.is_null());
} else {
panic!("result is not an extern ref!");
}
let f: NativeFunc<(), ExternRef> = instance.exports.get_native_function(run)?;
let result: ExternRef = f.call()?;
assert!(result.is_null());
}
for get_hashmap in &["get_hashmap", "get_hashmap_native"] {
let f: &Function = instance.exports.get_function(get_hashmap)?;
let results = f.call(&[]).unwrap();
if let Value::ExternRef(er) = &results[0] {
let inner: &HashMap<String, String> = er.downcast().unwrap();
assert_eq!(inner["hello"], "world");
assert_eq!(inner["color"], "orange");
} else {
panic!("result is not an extern ref!");
}
let f: NativeFunc<(), ExternRef> = instance.exports.get_native_function(get_hashmap)?;
let result: ExternRef = f.call()?;
let inner: &HashMap<String, String> = result.downcast().unwrap();
assert_eq!(inner["hello"], "world");
assert_eq!(inner["color"], "orange");
}
Ok(())
}
#[cfg(feature = "experimental-reference-types-extern-ref")]
#[test]
// TODO(reftypes): reenable this test
#[ignore]
fn extern_ref_ref_counting_basic() -> Result<()> {
let store = Store::default();
let wat = r#"(module
(func (export "drop") (param $er externref) (result)
(drop (local.get $er)))
)"#;
let module = Module::new(&store, wat)?;
let instance = Instance::new(&module, &imports! {})?;
let f: NativeFunc<ExternRef, ()> = instance.exports.get_native_function("drop")?;
let er = ExternRef::new(3u32);
f.call(er.clone())?;
assert_eq!(er.downcast::<u32>().unwrap(), &3);
assert_eq!(er.strong_count(), 1);
Ok(())
}
#[cfg(feature = "experimental-reference-types-extern-ref")]
#[test]
fn refs_in_globals() -> Result<()> {
let store = Store::default();
let wat = r#"(module
(global $er_global (export "er_global") (mut externref) (ref.null extern))
(global $fr_global (export "fr_global") (mut funcref) (ref.null func))
(global $fr_immutable_global (export "fr_immutable_global") funcref (ref.func $hello))
(func $hello (param) (result i32)
(i32.const 73))
)"#;
let module = Module::new(&store, wat)?;
let instance = Instance::new(&module, &imports! {})?;
{
let er_global: &Global = instance.exports.get_global("er_global")?;
if let Value::ExternRef(er) = er_global.get() {
assert!(er.is_null());
} else {
panic!("Did not find extern ref in the global");
}
er_global.set(Val::ExternRef(ExternRef::new(3u32)))?;
if let Value::ExternRef(er) = er_global.get() {
assert_eq!(er.downcast::<u32>().unwrap(), &3);
assert_eq!(er.strong_count(), 1);
} else {
panic!("Did not find extern ref in the global");
}
}
{
let fr_global: &Global = instance.exports.get_global("fr_immutable_global")?;
if let Value::FuncRef(Some(f)) = fr_global.get() {
let native_func: NativeFunc<(), u32> = f.native()?;
assert_eq!(native_func.call()?, 73);
} else {
panic!("Did not find non-null func ref in the global");
}
}
{
let fr_global: &Global = instance.exports.get_global("fr_global")?;
if let Value::FuncRef(None) = fr_global.get() {
} else {
panic!("Did not find a null func ref in the global");
}
let f = Function::new_native(&store, |arg1: i32, arg2: i32| -> i32 { arg1 + arg2 });
fr_global.set(Val::FuncRef(Some(f)))?;
if let Value::FuncRef(Some(f)) = fr_global.get() {
let native: NativeFunc<(i32, i32), i32> = f.native()?;
assert_eq!(native.call(5, 7)?, 12);
} else {
panic!("Did not find extern ref in the global");
}
}
Ok(())
}
#[cfg(feature = "experimental-reference-types-extern-ref")]
#[test]
fn extern_ref_ref_counting_table_basic() -> Result<()> {
let store = Store::default();
let wat = r#"(module
(global $global (export "global") (mut externref) (ref.null extern))
(table $table (export "table") 4 4 externref)
(func $insert (param $er externref) (param $idx i32)
(table.set $table (local.get $idx) (local.get $er)))
(func $intermediate (param $er externref) (param $idx i32)
(call $insert (local.get $er) (local.get $idx)))
(func $insert_into_table (export "insert_into_table") (param $er externref) (param $idx i32) (result externref)
(call $intermediate (local.get $er) (local.get $idx))
(local.get $er))
)"#;
let module = Module::new(&store, wat)?;
let instance = Instance::new(&module, &imports! {})?;
let f: NativeFunc<(ExternRef, i32), ExternRef> =
instance.exports.get_native_function("insert_into_table")?;
let er = ExternRef::new(3usize);
let er = f.call(er, 1)?;
assert_eq!(er.strong_count(), 2);
let table: &Table = instance.exports.get_table("table")?;
{
let er2 = table.get(1).unwrap().externref().unwrap();
assert_eq!(er2.strong_count(), 3);
}
assert_eq!(er.strong_count(), 2);
table.set(1, Val::ExternRef(ExternRef::null()))?;
assert_eq!(er.strong_count(), 1);
Ok(())
}
#[cfg(feature = "experimental-reference-types-extern-ref")]
#[test]
// TODO(reftypes): reenable this test
#[ignore]
fn extern_ref_ref_counting_global_basic() -> Result<()> {
let store = Store::default();
let wat = r#"(module
(global $global (export "global") (mut externref) (ref.null extern))
(func $get_from_global (export "get_from_global") (result externref)
(drop (global.get $global))
(global.get $global))
)"#;
let module = Module::new(&store, wat)?;
let instance = Instance::new(&module, &imports! {})?;
let global: &Global = instance.exports.get_global("global")?;
{
let er = ExternRef::new(3usize);
global.set(Val::ExternRef(er.clone()))?;
assert_eq!(er.strong_count(), 2);
}
let get_from_global: NativeFunc<(), ExternRef> =
instance.exports.get_native_function("get_from_global")?;
let er = get_from_global.call()?;
assert_eq!(er.strong_count(), 2);
global.set(Val::ExternRef(ExternRef::null()))?;
assert_eq!(er.strong_count(), 1);
Ok(())
}
#[cfg(feature = "experimental-reference-types-extern-ref")]
#[test]
// TODO(reftypes): reenable this test
#[ignore]
fn extern_ref_ref_counting_traps() -> Result<()> {
let store = Store::default();
let wat = r#"(module
(func $pass_er (export "pass_extern_ref") (param externref)
(local.get 0)
(unreachable))
)"#;
let module = Module::new(&store, wat)?;
let instance = Instance::new(&module, &imports! {})?;
let pass_extern_ref: NativeFunc<ExternRef, ()> =
instance.exports.get_native_function("pass_extern_ref")?;
let er = ExternRef::new(3usize);
assert_eq!(er.strong_count(), 1);
let result = pass_extern_ref.call(er.clone());
assert!(result.is_err());
assert_eq!(er.strong_count(), 1);
Ok(())
}
#[cfg(feature = "experimental-reference-types-extern-ref")]
#[test]
fn extern_ref_ref_counting_table_instructions() -> Result<()> {
let store = Store::default();
let wat = r#"(module
(table $table1 (export "table1") 2 12 externref)
(table $table2 (export "table2") 6 12 externref)
(func $grow_table_with_ref (export "grow_table_with_ref") (param $er externref) (param $size i32) (result i32)
(table.grow $table1 (local.get $er) (local.get $size)))
(func $fill_table_with_ref (export "fill_table_with_ref") (param $er externref) (param $start i32) (param $end i32)
(table.fill $table1 (local.get $start) (local.get $er) (local.get $end)))
(func $copy_into_table2 (export "copy_into_table2")
(table.copy $table2 $table1 (i32.const 0) (i32.const 0) (i32.const 4)))
)"#;
let module = Module::new(&store, wat)?;
let instance = Instance::new(&module, &imports! {})?;
let grow_table_with_ref: NativeFunc<(ExternRef, i32), i32> = instance
.exports
.get_native_function("grow_table_with_ref")?;
let fill_table_with_ref: NativeFunc<(ExternRef, i32, i32), ()> = instance
.exports
.get_native_function("fill_table_with_ref")?;
let copy_into_table2: NativeFunc<(), ()> =
instance.exports.get_native_function("copy_into_table2")?;
let table1: &Table = instance.exports.get_table("table1")?;
let table2: &Table = instance.exports.get_table("table2")?;
let er1 = ExternRef::new(3usize);
let er2 = ExternRef::new(5usize);
let er3 = ExternRef::new(7usize);
{
let result = grow_table_with_ref.call(er1.clone(), 0)?;
assert_eq!(result, 2);
assert_eq!(er1.strong_count(), 1);
let result = grow_table_with_ref.call(er1.clone(), 10_000)?;
assert_eq!(result, -1);
assert_eq!(er1.strong_count(), 1);
let result = grow_table_with_ref.call(er1.clone(), 8)?;
assert_eq!(result, 2);
assert_eq!(er1.strong_count(), 9);
for i in 2..10 {
let e = table1.get(i).unwrap().unwrap_externref();
assert_eq!(*e.downcast::<usize>().unwrap(), 3);
assert_eq!(&e, &er1);
}
assert_eq!(er1.strong_count(), 9);
}
{
fill_table_with_ref.call(er2.clone(), 0, 2)?;
assert_eq!(er2.strong_count(), 3);
}
{
table2.set(0, Val::ExternRef(er3.clone()))?;
table2.set(1, Val::ExternRef(er3.clone()))?;
table2.set(2, Val::ExternRef(er3.clone()))?;
table2.set(3, Val::ExternRef(er3.clone()))?;
table2.set(4, Val::ExternRef(er3.clone()))?;
assert_eq!(er3.strong_count(), 6);
}
{
copy_into_table2.call()?;
assert_eq!(er3.strong_count(), 2);
assert_eq!(er2.strong_count(), 5);
assert_eq!(er1.strong_count(), 11);
for i in 1..5 {
let e = table2.get(i).unwrap().unwrap_externref();
let value = e.downcast::<usize>().unwrap();
match i {
0 | 1 => assert_eq!(*value, 5),
4 => assert_eq!(*value, 7),
_ => assert_eq!(*value, 3),
}
}
}
{
for i in 0..table1.size() {
table1.set(i, Val::ExternRef(ExternRef::null()))?;
}
for i in 0..table2.size() {
table2.set(i, Val::ExternRef(ExternRef::null()))?;
}
}
assert_eq!(er1.strong_count(), 1);
assert_eq!(er2.strong_count(), 1);
assert_eq!(er3.strong_count(), 1);
Ok(())
}
}

View File

@@ -10,10 +10,6 @@ edition = "2018"
[lib] [lib]
proc-macro = true proc-macro = true
[features]
# It will make imports from `wasmer_js::` instead of `wasmer::`
js = []
[dependencies] [dependencies]
syn = { version = "1.0.72", features = ["full", "extra-traits"] } syn = { version = "1.0.72", features = ["full", "extra-traits"] }
quote = "1" quote = "1"
@@ -22,4 +18,4 @@ proc-macro-error = "1.0.0"
[dev-dependencies] [dev-dependencies]
wasmer = { path = "../api", version = "2.0.0" } wasmer = { path = "../api", version = "2.0.0" }
compiletest_rs = "0.6" compiletest_rs = "0.6"

View File

@@ -13,26 +13,21 @@ use crate::parse::WasmerAttr;
#[proc_macro_derive(WasmerEnv, attributes(wasmer))] #[proc_macro_derive(WasmerEnv, attributes(wasmer))]
pub fn derive_wasmer_env(input: proc_macro::TokenStream) -> proc_macro::TokenStream { pub fn derive_wasmer_env(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input: DeriveInput = syn::parse(input).unwrap(); let input: DeriveInput = syn::parse(input).unwrap();
#[cfg(feature = "js")] let gen = impl_wasmer_env(&input);
let root = Ident::new("wasmer_js", proc_macro2::Span::call_site());
#[cfg(not(feature = "js"))]
let root = Ident::new("wasmer", proc_macro2::Span::call_site());
let gen = impl_wasmer_env(&root, &input);
gen.into() gen.into()
} }
fn impl_wasmer_env_for_struct( fn impl_wasmer_env_for_struct(
root: &Ident,
name: &Ident, name: &Ident,
data: &DataStruct, data: &DataStruct,
generics: &Generics, generics: &Generics,
_attrs: &[Attribute], _attrs: &[Attribute],
) -> TokenStream { ) -> TokenStream {
let (trait_methods, helper_methods) = derive_struct_fields(root, data); let (trait_methods, helper_methods) = derive_struct_fields(data);
let lifetimes_and_generics = generics.params.clone(); let lifetimes_and_generics = generics.params.clone();
let where_clause = generics.where_clause.clone(); let where_clause = generics.where_clause.clone();
quote! { quote! {
impl < #lifetimes_and_generics > ::#root::WasmerEnv for #name < #lifetimes_and_generics > #where_clause{ impl < #lifetimes_and_generics > ::wasmer::WasmerEnv for #name < #lifetimes_and_generics > #where_clause{
#trait_methods #trait_methods
} }
@@ -43,12 +38,12 @@ fn impl_wasmer_env_for_struct(
} }
} }
fn impl_wasmer_env(root: &Ident, input: &DeriveInput) -> TokenStream { fn impl_wasmer_env(input: &DeriveInput) -> TokenStream {
let struct_name = &input.ident; let struct_name = &input.ident;
set_dummy(quote! { set_dummy(quote! {
impl ::#root::WasmerEnv for #struct_name { impl ::wasmer::WasmerEnv for #struct_name {
fn init_with_instance(&mut self, instance: &::#root::Instance) -> Result<(), ::#root::HostEnvInitError> { fn init_with_instance(&mut self, instance: &::wasmer::Instance) -> Result<(), ::wasmer::HostEnvInitError> {
Ok(()) Ok(())
} }
} }
@@ -56,7 +51,7 @@ fn impl_wasmer_env(root: &Ident, input: &DeriveInput) -> TokenStream {
match &input.data { match &input.data {
Data::Struct(ds) => { Data::Struct(ds) => {
impl_wasmer_env_for_struct(root, struct_name, ds, &input.generics, &input.attrs) impl_wasmer_env_for_struct(struct_name, ds, &input.generics, &input.attrs)
} }
_ => todo!(), _ => todo!(),
} }
@@ -70,7 +65,7 @@ fn impl_wasmer_env(root: &Ident, input: &DeriveInput) -> TokenStream {
}*/ }*/
} }
fn derive_struct_fields(root: &Ident, data: &DataStruct) -> (TokenStream, TokenStream) { fn derive_struct_fields(data: &DataStruct) -> (TokenStream, TokenStream) {
let mut finish = vec![]; let mut finish = vec![];
let mut helpers = vec![]; let mut helpers = vec![];
//let mut assign_tokens = vec![]; //let mut assign_tokens = vec![];
@@ -210,7 +205,7 @@ fn derive_struct_fields(root: &Ident, data: &DataStruct) -> (TokenStream, TokenS
} }
let trait_methods = quote! { let trait_methods = quote! {
fn init_with_instance(&mut self, instance: &::#root::Instance) -> Result<(), ::#root::HostEnvInitError> { fn init_with_instance(&mut self, instance: &::wasmer::Instance) -> Result<(), ::wasmer::HostEnvInitError> {
#(#finish)* #(#finish)*
Ok(()) Ok(())
} }

View File

@@ -1,47 +0,0 @@
[package]
name = "wasmer-js"
version = "2.0.0"
description = "A crate to compile Wasmer to WebAssembly and make it run in a JavaScript host"
categories = ["wasm"]
keywords = ["wasm", "webassembly", "runtime", "vm", "javascript"]
authors = ["Wasmer Engineering Team <engineering@wasmer.io>"]
repository = "https://github.com/wasmerio/wasmer"
license = "MIT"
readme = "README.md"
edition = "2018"
# `wasm-opt` is on by default in for the release profile, but it can be
# disabled by setting it to `false`
[package.metadata.wasm-pack.profile.release]
wasm-opt = false
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
wasm-bindgen = { version = "0.2.74" }
js-sys = { version = "0.3.51" }
wasmer-types = { path = "../types", version = "2.0.0", default-features = false, features = ["std"] }
indexmap = { version = "1.6", features = ["serde-1"] }
cfg-if = "1.0"
wat = { version = "1.0", optional = true }
thiserror = "1.0"
more-asserts = "0.2"
wasmer-derive = { path = "../derive", version = "2.0.0", features = ["js"] }
wasmparser = { version = "0.78", optional = true, default-features = false }
hashbrown = { version = "0.11", optional = true }
[dev-dependencies]
wat = "1.0"
anyhow = "1.0"
wasm-bindgen-test = "0.3.0"
# wasm-bindgen-test = { version= "0.3.0", path = "../../../wasm-bindgen/crates/test"}
[badges]
maintenance = { status = "actively-developed" }
[features]
default = ["std", "wasm-types-polyfill", "wat"]
wasm-types-polyfill = ["wasmparser"]
std = []
core = ["hashbrown"]

View File

@@ -1,78 +0,0 @@
# `wasmer-js` [![Build Status](https://github.com/wasmerio/wasmer/workflows/build/badge.svg?style=flat-square)](https://github.com/wasmerio/wasmer/actions?query=workflow%3Abuild) [![Join Wasmer Slack](https://img.shields.io/static/v1?label=Slack&message=join%20chat&color=brighgreen&style=flat-square)](https://slack.wasmer.io) [![MIT License](https://img.shields.io/github/license/wasmerio/wasmer.svg?style=flat-square)](https://github.com/wasmerio/wasmer/blob/master/LICENSE) [![crates.io](https://img.shields.io/crates/v/wasmer-js.svg)](https://crates.io/crates/wasmer-js)
[`Wasmer`](https://wasmer.io/) is the most popular
[WebAssembly](https://webassembly.org/) runtime for Rust. This crate mimics the same Rust
API than the `wasmer` crate, but when compiled to WebAssembly, it only targets
a JavaScript host. It means that it is possible to write a Rust program that uses Wasmer,
and compiles everything to WebAssembly to run in a browser, Node.js, Deno and so on.
This crate doesn't ship with any compilers or engines, as it leverages the Javascript VM to
compile and run WebAssembly.
## Usage
We recommend aliasing `wasmer_js` to `wasmer` at the top of your crate.
```rust
#[cfg(feature = "js")]
extern crate wasmer_js as wasmer;
```
And then:
```rust
use wasmer::{Store, Module, Instance, Value, imports};
#[wasm_bindgen]
pub extern fn do_add_one_in_wasmer() -> i32 {
let module_wat = r#"
(module
(type $t0 (func (param i32) (result i32)))
(func $add_one (export "add_one") (type $t0) (param $p0 i32) (result i32)
get_local $p0
i32.const 1
i32.add))
"#;
let store = Store::default();
let module = Module::new(&store, &module_wat).unwrap();
// The module doesn't import anything, so we create an empty import object.
let import_object = imports! {};
let instance = Instance::new(&module, &import_object).unwrap();
let add_one = instance.exports.get_function("add_one").unwrap();
let result = add_one.call(&[Value::I32(42)]).unwrap();
assert_eq!(result[0], Value::I32(43));
result[0].unwrap_i32()
}
```
## Feature flags
`wasmer-js` has the following feature flags:
* `wasm-types-polyfill` (enabled by default): it parses the Wasm file, allowing to do type reflection of the inner WebAssembly types.
__It adds 100Kb to the Wasm bundle__ (28Kb gzipped). You can disable it and use `Module::set_type_hints` manually instead if you want a lightweight alternative.
This is needed until the [Wasm JS introspection API proposal](https://github.com/WebAssembly/js-types/blob/master/proposals/js-types/Overview.md) is adopted by browsers
* `wat`: It allows to read WebAssembly files in their text format.
*This feature is normally used only in development environments, __it will add around 650Kb to the Wasm bundle__* (120Kb gzipped).
# Build
You can use [`wasm-pack`](https://github.com/rustwasm/wasm-pack/) to build `wasmer-js-api`:
```
wasm-pack build --release
```
> The provided `wasmer_js.wasm` file should weight around 60kB (27Kb gzipped) when optmized via `wasm-opt` and stripped via `wasm-strip`, so it's quite slim.
# Test
```
wasm-pack test --node
```
---
Made with ❤️ by the Wasmer team, for the community

View File

@@ -1,418 +0,0 @@
use wasm_bindgen_test::*;
// use anyhow::Result;
use wasmer_js::*;
#[wasm_bindgen_test]
fn global_new() {
let store = Store::default();
let global = Global::new(&store, Value::I32(10));
assert_eq!(
*global.ty(),
GlobalType {
ty: Type::I32,
mutability: Mutability::Const
}
);
let global_mut = Global::new_mut(&store, Value::I32(10));
assert_eq!(
*global_mut.ty(),
GlobalType {
ty: Type::I32,
mutability: Mutability::Var
}
);
}
#[wasm_bindgen_test]
fn global_get() {
let store = Store::default();
let global_i32 = Global::new(&store, Value::I32(10));
assert_eq!(global_i32.get(), Value::I32(10));
// 64-bit values are not yet fully supported in some versions of Node
// Commenting this tests for now:
// let global_i64 = Global::new(&store, Value::I64(20));
// assert_eq!(global_i64.get(), Value::I64(20));
let global_f32 = Global::new(&store, Value::F32(10.0));
assert_eq!(global_f32.get(), Value::F32(10.0));
// let global_f64 = Global::new(&store, Value::F64(20.0));
// assert_eq!(global_f64.get(), Value::F64(20.0));
}
#[wasm_bindgen_test]
fn global_set() {
let store = Store::default();
let global_i32 = Global::new(&store, Value::I32(10));
// Set on a constant should error
assert!(global_i32.set(Value::I32(20)).is_err());
let global_i32_mut = Global::new_mut(&store, Value::I32(10));
// Set on different type should error
assert!(global_i32_mut.set(Value::I64(20)).is_err());
// Set on same type should succeed
global_i32_mut.set(Value::I32(20)).unwrap();
assert_eq!(global_i32_mut.get(), Value::I32(20));
}
#[wasm_bindgen_test]
fn table_new() {
let store = Store::default();
let table_type = TableType {
ty: Type::FuncRef,
minimum: 0,
maximum: None,
};
let f = Function::new_native(&store, || {});
let table = Table::new(&store, table_type, Value::FuncRef(Some(f))).unwrap();
assert_eq!(*table.ty(), table_type);
// table.get()
// Anyrefs not yet supported
// let table_type = TableType {
// ty: Type::ExternRef,
// minimum: 0,
// maximum: None,
// };
// let table = Table::new(&store, table_type, Value::ExternRef(ExternRef::Null))?;
// assert_eq!(*table.ty(), table_type);
}
// Tables are not yet fully supported in Wasm
// Commenting this tests for now
// #[test]
// #[ignore]
// fn table_get() -> Result<()> {
// let store = Store::default();
// let table_type = TableType {
// ty: Type::FuncRef,
// minimum: 0,
// maximum: Some(1),
// };
// let f = Function::new_native(&store, |num: i32| num + 1);
// let table = Table::new(&store, table_type, Value::FuncRef(Some(f.clone())))?;
// assert_eq!(*table.ty(), table_type);
// let _elem = table.get(0).unwrap();
// // assert_eq!(elem.funcref().unwrap(), f);
// Ok(())
// }
// #[test]
// #[ignore]
// fn table_set() -> Result<()> {
// // Table set not yet tested
// Ok(())
// }
// #[test]
// fn table_grow() -> Result<()> {
// let store = Store::default();
// let table_type = TableType {
// ty: Type::FuncRef,
// minimum: 0,
// maximum: Some(10),
// };
// let f = Function::new_native(&store, |num: i32| num + 1);
// let table = Table::new(&store, table_type, Value::FuncRef(Some(f.clone())))?;
// // Growing to a bigger maximum should return None
// let old_len = table.grow(12, Value::FuncRef(Some(f.clone())));
// assert!(old_len.is_err());
// // Growing to a bigger maximum should return None
// let old_len = table.grow(5, Value::FuncRef(Some(f.clone())))?;
// assert_eq!(old_len, 0);
// Ok(())
// }
// #[test]
// #[ignore]
// fn table_copy() -> Result<()> {
// // TODO: table copy test not yet implemented
// Ok(())
// }
#[wasm_bindgen_test]
fn memory_new() {
let store = Store::default();
let memory_type = MemoryType {
shared: false,
minimum: Pages(0),
maximum: Some(Pages(10)),
};
let memory = Memory::new(&store, memory_type).unwrap();
assert_eq!(memory.size(), Pages(0));
assert_eq!(memory.ty(), memory_type);
}
#[wasm_bindgen_test]
fn memory_grow() {
let store = Store::default();
let desc = MemoryType::new(Pages(10), Some(Pages(16)), false);
let memory = Memory::new(&store, desc).unwrap();
assert_eq!(memory.size(), Pages(10));
let result = memory.grow(Pages(2)).unwrap();
assert_eq!(result, Pages(10));
assert_eq!(memory.size(), Pages(12));
let result = memory.grow(Pages(10));
assert!(result.is_err());
assert_eq!(
result,
Err(MemoryError::CouldNotGrow {
current: 12.into(),
attempted_delta: 10.into()
})
);
}
#[wasm_bindgen_test]
fn function_new() {
let store = Store::default();
let function = Function::new_native(&store, || {});
assert_eq!(function.ty().clone(), FunctionType::new(vec![], vec![]));
let function = Function::new_native(&store, |_a: i32| {});
assert_eq!(
function.ty().clone(),
FunctionType::new(vec![Type::I32], vec![])
);
let function = Function::new_native(&store, |_a: i32, _b: i64, _c: f32, _d: f64| {});
assert_eq!(
function.ty().clone(),
FunctionType::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![])
);
let function = Function::new_native(&store, || -> i32 { 1 });
assert_eq!(
function.ty().clone(),
FunctionType::new(vec![], vec![Type::I32])
);
let function = Function::new_native(&store, || -> (i32, i64, f32, f64) { (1, 2, 3.0, 4.0) });
assert_eq!(
function.ty().clone(),
FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64])
);
}
#[wasm_bindgen_test]
fn function_new_env() {
let store = Store::default();
#[derive(Clone, WasmerEnv)]
struct MyEnv {}
let my_env = MyEnv {};
let function = Function::new_native_with_env(&store, my_env.clone(), |_env: &MyEnv| {});
assert_eq!(function.ty().clone(), FunctionType::new(vec![], vec![]));
let function =
Function::new_native_with_env(&store, my_env.clone(), |_env: &MyEnv, _a: i32| {});
assert_eq!(
function.ty().clone(),
FunctionType::new(vec![Type::I32], vec![])
);
let function = Function::new_native_with_env(
&store,
my_env.clone(),
|_env: &MyEnv, _a: i32, _b: i64, _c: f32, _d: f64| {},
);
assert_eq!(
function.ty().clone(),
FunctionType::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![])
);
let function =
Function::new_native_with_env(&store, my_env.clone(), |_env: &MyEnv| -> i32 { 1 });
assert_eq!(
function.ty().clone(),
FunctionType::new(vec![], vec![Type::I32])
);
let function = Function::new_native_with_env(
&store,
my_env.clone(),
|_env: &MyEnv| -> (i32, i64, f32, f64) { (1, 2, 3.0, 4.0) },
);
assert_eq!(
function.ty().clone(),
FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64])
);
}
#[wasm_bindgen_test]
fn function_new_dynamic() {
let store = Store::default();
// Using &FunctionType signature
let function_type = FunctionType::new(vec![], vec![]);
let function = Function::new(&store, &function_type, |_values: &[Value]| unimplemented!());
assert_eq!(function.ty().clone(), function_type);
let function_type = FunctionType::new(vec![Type::I32], vec![]);
let function = Function::new(&store, &function_type, |_values: &[Value]| unimplemented!());
assert_eq!(function.ty().clone(), function_type);
let function_type = FunctionType::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![]);
let function = Function::new(&store, &function_type, |_values: &[Value]| unimplemented!());
assert_eq!(function.ty().clone(), function_type);
let function_type = FunctionType::new(vec![], vec![Type::I32]);
let function = Function::new(&store, &function_type, |_values: &[Value]| unimplemented!());
assert_eq!(function.ty().clone(), function_type);
let function_type = FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64]);
let function = Function::new(&store, &function_type, |_values: &[Value]| unimplemented!());
assert_eq!(function.ty().clone(), function_type);
// Using array signature
let function_type = ([Type::V128], [Type::I32, Type::F32, Type::F64]);
let function = Function::new(&store, function_type, |_values: &[Value]| unimplemented!());
assert_eq!(function.ty().params(), [Type::V128]);
assert_eq!(function.ty().results(), [Type::I32, Type::F32, Type::F64]);
}
#[wasm_bindgen_test]
fn function_new_dynamic_env() {
let store = Store::default();
#[derive(Clone, WasmerEnv)]
struct MyEnv {}
let my_env = MyEnv {};
// Using &FunctionType signature
let function_type = FunctionType::new(vec![], vec![]);
let function = Function::new_with_env(
&store,
&function_type,
my_env.clone(),
|_env: &MyEnv, _values: &[Value]| unimplemented!(),
);
assert_eq!(function.ty().clone(), function_type);
let function_type = FunctionType::new(vec![Type::I32], vec![]);
let function = Function::new_with_env(
&store,
&function_type,
my_env.clone(),
|_env: &MyEnv, _values: &[Value]| unimplemented!(),
);
assert_eq!(function.ty().clone(), function_type);
let function_type = FunctionType::new(vec![Type::I32, Type::I64, Type::F32, Type::F64], vec![]);
let function = Function::new_with_env(
&store,
&function_type,
my_env.clone(),
|_env: &MyEnv, _values: &[Value]| unimplemented!(),
);
assert_eq!(function.ty().clone(), function_type);
let function_type = FunctionType::new(vec![], vec![Type::I32]);
let function = Function::new_with_env(
&store,
&function_type,
my_env.clone(),
|_env: &MyEnv, _values: &[Value]| unimplemented!(),
);
assert_eq!(function.ty().clone(), function_type);
let function_type = FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64]);
let function = Function::new_with_env(
&store,
&function_type,
my_env.clone(),
|_env: &MyEnv, _values: &[Value]| unimplemented!(),
);
assert_eq!(function.ty().clone(), function_type);
// Using array signature
let function_type = ([Type::V128], [Type::I32, Type::F32, Type::F64]);
let function = Function::new_with_env(
&store,
function_type,
my_env.clone(),
|_env: &MyEnv, _values: &[Value]| unimplemented!(),
);
assert_eq!(function.ty().params(), [Type::V128]);
assert_eq!(function.ty().results(), [Type::I32, Type::F32, Type::F64]);
}
#[wasm_bindgen_test]
fn native_function_works() {
let store = Store::default();
let function = Function::new_native(&store, || {});
let native_function: NativeFunc<(), ()> = function.native().unwrap();
let result = native_function.call();
assert!(result.is_ok());
let function = Function::new_native(&store, |a: i32| -> i32 { a + 1 });
let native_function: NativeFunc<i32, i32> = function.native().unwrap();
assert_eq!(native_function.call(3).unwrap(), 4);
// fn rust_abi(a: i32, b: i64, c: f32, d: f64) -> u64 {
// (a as u64 * 1000) + (b as u64 * 100) + (c as u64 * 10) + (d as u64)
// }
// let function = Function::new_native(&store, rust_abi);
// let native_function: NativeFunc<(i32, i64, f32, f64), u64> = function.native().unwrap();
// assert_eq!(native_function.call(8, 4, 1.5, 5.).unwrap(), 8415);
let function = Function::new_native(&store, || -> i32 { 1 });
let native_function: NativeFunc<(), i32> = function.native().unwrap();
assert_eq!(native_function.call().unwrap(), 1);
let function = Function::new_native(&store, |_a: i32| {});
let native_function: NativeFunc<i32, ()> = function.native().unwrap();
assert!(native_function.call(4).is_ok());
// let function = Function::new_native(&store, || -> (i32, i64, f32, f64) { (1, 2, 3.0, 4.0) });
// let native_function: NativeFunc<(), (i32, i64, f32, f64)> = function.native().unwrap();
// assert_eq!(native_function.call().unwrap(), (1, 2, 3.0, 4.0));
}
#[wasm_bindgen_test]
fn function_outlives_instance() {
let store = Store::default();
let wat = r#"(module
(type $sum_t (func (param i32 i32) (result i32)))
(func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32)
local.get $x
local.get $y
i32.add)
(export "sum" (func $sum_f)))
"#;
let f = {
let module = Module::new(&store, wat).unwrap();
let instance = Instance::new(&module, &imports! {}).unwrap();
let f = instance.exports.get_function("sum").unwrap();
assert_eq!(
f.call(&[Val::I32(4), Val::I32(5)]).unwrap(),
vec![Val::I32(9)].into_boxed_slice()
);
f.clone()
};
assert_eq!(
f.call(&[Val::I32(4), Val::I32(5)]).unwrap(),
vec![Val::I32(9)].into_boxed_slice()
);
}
#[wasm_bindgen_test]
fn manually_generate_wasmer_env() {
let store = Store::default();
#[derive(WasmerEnv, Clone)]
struct MyEnv {
val: u32,
memory: LazyInit<Memory>,
}
fn host_function(env: &mut MyEnv, arg1: u32, arg2: u32) -> u32 {
env.val + arg1 + arg2
}
let mut env = MyEnv {
val: 5,
memory: LazyInit::new(),
};
let result = host_function(&mut env, 7, 9);
assert_eq!(result, 21);
let memory = Memory::new(&store, MemoryType::new(0, None, false)).unwrap();
env.memory.initialize(memory);
let result = host_function(&mut env, 1, 2);
assert_eq!(result, 8);
}

View File

@@ -1,731 +0,0 @@
use anyhow::Result;
use wasm_bindgen_test::*;
use wasmer_js::*;
#[wasm_bindgen_test]
fn test_exported_memory() {
let store = Store::default();
let mut module = Module::new(
&store,
br#"
(module
(memory (export "mem") 1)
)
"#,
)
.unwrap();
module
.set_type_hints(ModuleTypeHints {
imports: vec![],
exports: vec![ExternType::Memory(MemoryType::new(Pages(1), None, false))],
})
.unwrap();
let import_object = imports! {};
let instance = Instance::new(&module, &import_object).unwrap();
let memory = instance.exports.get_memory("mem").unwrap();
assert_eq!(memory.ty(), MemoryType::new(Pages(1), None, false));
assert_eq!(memory.size(), Pages(1));
assert_eq!(memory.data_size(), 65536);
memory.grow(Pages(1)).unwrap();
assert_eq!(memory.ty(), MemoryType::new(Pages(2), None, false));
assert_eq!(memory.size(), Pages(2));
assert_eq!(memory.data_size(), 65536 * 2);
}
#[wasm_bindgen_test]
fn test_exported_function() {
let store = Store::default();
let mut module = Module::new(
&store,
br#"
(module
(func (export "get_magic") (result i32)
(i32.const 42)
)
)
"#,
)
.unwrap();
module
.set_type_hints(ModuleTypeHints {
imports: vec![],
exports: vec![ExternType::Function(FunctionType::new(
vec![],
vec![Type::I32],
))],
})
.unwrap();
let import_object = imports! {};
let instance = Instance::new(&module, &import_object).unwrap();
let get_magic = instance.exports.get_function("get_magic").unwrap();
assert_eq!(
get_magic.ty().clone(),
FunctionType::new(vec![], vec![Type::I32])
);
let expected = vec![Val::I32(42)].into_boxed_slice();
assert_eq!(get_magic.call(&[]), Ok(expected));
}
#[wasm_bindgen_test]
fn test_imported_function_dynamic() {
let 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 imported_signature = FunctionType::new(vec![Type::I32], vec![Type::I32]);
let imported = Function::new(&store, &imported_signature, |args| {
println!("Calling `imported`...");
let result = args[0].unwrap_i32() * 2;
println!("Result of `imported`: {:?}", result);
Ok(vec![Value::I32(result)])
});
let import_object = imports! {
"env" => {
"imported" => imported,
}
};
let instance = Instance::new(&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(&[Val::I32(3)]), Ok(expected));
}
// We comment it for now because in old versions of Node, only single return values are supported
// #[wasm_bindgen_test]
// fn test_imported_function_dynamic_multivalue() {
// let store = Store::default();
// let mut module = Module::new(
// &store,
// br#"
// (module
// (func $multivalue (import "env" "multivalue") (param i32 i32) (result i32 i32))
// (func (export "multivalue") (param i32 i32) (result i32 i32)
// (call $multivalue (local.get 0) (local.get 1))
// )
// )
// "#,
// )
// .unwrap();
// module.set_type_hints(ModuleTypeHints {
// imports: vec![
// ExternType::Function(FunctionType::new(
// vec![Type::I32, Type::I32],
// vec![Type::I32, Type::I32],
// )),
// ],
// exports: vec![
// ExternType::Function(FunctionType::new(
// vec![Type::I32, Type::I32],
// vec![Type::I32, Type::I32],
// )),
// ],
// });
// let multivalue_signature =
// FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32, Type::I32]);
// let multivalue = Function::new(&store, &multivalue_signature, |args| {
// println!("Calling `imported`...");
// // let result = args[0].unwrap_i32() * ;
// // println!("Result of `imported`: {:?}", result);
// Ok(vec![args[1].clone(), args[0].clone()])
// });
// let import_object = imports! {
// "env" => {
// "multivalue" => multivalue,
// }
// };
// let instance = Instance::new(&module, &import_object).unwrap();
// let exported_multivalue = instance
// .exports
// .get_function("multivalue")
// .unwrap();
// let expected = vec![Val::I32(2), Val::I32(3)].into_boxed_slice();
// assert_eq!(
// exported_multivalue.call(&[Val::I32(3), Val::I32(2)]),
// Ok(expected)
// );
// }
#[wasm_bindgen_test]
fn test_imported_function_dynamic_with_env() {
let 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();
#[derive(WasmerEnv, Clone)]
struct Env {
multiplier: i32,
}
let imported_signature = FunctionType::new(vec![Type::I32], vec![Type::I32]);
let imported = Function::new_with_env(
&store,
&imported_signature,
Env { multiplier: 3 },
|env, args| {
println!("Calling `imported`...");
let result = args[0].unwrap_i32() * env.multiplier;
println!("Result of `imported`: {:?}", result);
Ok(vec![Value::I32(result)])
},
);
let import_object = imports! {
"env" => {
"imported" => imported,
}
};
let instance = Instance::new(&module, &import_object).unwrap();
let exported = instance.exports.get_function("exported").unwrap();
let expected = vec![Val::I32(9)].into_boxed_slice();
assert_eq!(exported.call(&[Val::I32(3)]), Ok(expected));
}
#[wasm_bindgen_test]
fn test_imported_function_native() {
let 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();
fn imported_fn(arg: u32) -> u32 {
return arg + 1;
}
let imported = Function::new_native(&store, imported_fn);
let import_object = imports! {
"env" => {
"imported" => imported,
}
};
let instance = Instance::new(&module, &import_object).unwrap();
let exported = instance.exports.get_function("exported").unwrap();
let expected = vec![Val::I32(5)].into_boxed_slice();
assert_eq!(exported.call(&[Val::I32(4)]), Ok(expected));
}
#[wasm_bindgen_test]
fn test_imported_function_native_with_env() {
let 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();
#[derive(WasmerEnv, Clone)]
struct Env {
multiplier: u32,
}
fn imported_fn(env: &Env, arg: u32) -> u32 {
return env.multiplier * arg;
}
let imported = Function::new_native_with_env(&store, Env { multiplier: 3 }, imported_fn);
let import_object = imports! {
"env" => {
"imported" => imported,
}
};
let instance = Instance::new(&module, &import_object).unwrap();
let exported = instance.exports.get_function("exported").unwrap();
let expected = vec![Val::I32(12)].into_boxed_slice();
assert_eq!(exported.call(&[Val::I32(4)]), Ok(expected));
}
#[wasm_bindgen_test]
fn test_imported_function_native_with_wasmer_env() {
let 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))
)
(memory (export "memory") 1)
)
"#,
)
.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])),
ExternType::Memory(MemoryType::new(Pages(1), None, false)),
],
})
.unwrap();
#[derive(WasmerEnv, Clone)]
struct Env {
multiplier: u32,
#[wasmer(export)]
memory: LazyInit<Memory>,
}
fn imported_fn(env: &Env, arg: u32) -> u32 {
let memory = env.memory_ref().unwrap();
let memory_val = memory.uint8view().get_index(0);
return (memory_val as u32) * env.multiplier * arg;
}
let imported = Function::new_native_with_env(
&store,
Env {
multiplier: 3,
memory: LazyInit::new(),
},
imported_fn,
);
let import_object = imports! {
"env" => {
"imported" => imported,
}
};
let instance = Instance::new(&module, &import_object).unwrap();
let memory = instance.exports.get_memory("memory").unwrap();
assert_eq!(memory.data_size(), 65536);
let memory_val = memory.uint8view().get_index(0);
assert_eq!(memory_val, 0);
memory.uint8view().set_index(0, 2);
let memory_val = memory.uint8view().get_index(0);
assert_eq!(memory_val, 2);
let exported = instance.exports.get_function("exported").unwrap();
// It works with the provided memory
let expected = vec![Val::I32(24)].into_boxed_slice();
assert_eq!(exported.call(&[Val::I32(4)]), Ok(expected));
// It works if we update the memory
memory.uint8view().set_index(0, 3);
let expected = vec![Val::I32(36)].into_boxed_slice();
assert_eq!(exported.call(&[Val::I32(4)]), Ok(expected));
}
#[wasm_bindgen_test]
fn test_imported_function_with_wasmer_env() {
let 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))
)
(memory (export "memory") 1)
)
"#,
)
.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])),
ExternType::Memory(MemoryType::new(Pages(1), None, false)),
],
})
.unwrap();
#[derive(WasmerEnv, Clone)]
struct Env {
multiplier: u32,
#[wasmer(export)]
memory: LazyInit<Memory>,
}
fn imported_fn(env: &Env, args: &[Val]) -> Result<Vec<Val>, RuntimeError> {
let memory = env.memory_ref().unwrap();
let memory_val = memory.uint8view().get_index(0);
let value = (memory_val as u32) * env.multiplier * args[0].unwrap_i32() as u32;
return Ok(vec![Val::I32(value as _)]);
}
let imported_signature = FunctionType::new(vec![Type::I32], vec![Type::I32]);
let imported = Function::new_with_env(
&store,
imported_signature,
Env {
multiplier: 3,
memory: LazyInit::new(),
},
imported_fn,
);
let import_object = imports! {
"env" => {
"imported" => imported,
}
};
let instance = Instance::new(&module, &import_object).unwrap();
let memory = instance.exports.get_memory("memory").unwrap();
assert_eq!(memory.data_size(), 65536);
let memory_val = memory.uint8view().get_index(0);
assert_eq!(memory_val, 0);
memory.uint8view().set_index(0, 2);
let memory_val = memory.uint8view().get_index(0);
assert_eq!(memory_val, 2);
let exported = instance.exports.get_function("exported").unwrap();
// It works with the provided memory
let expected = vec![Val::I32(24)].into_boxed_slice();
assert_eq!(exported.call(&[Val::I32(4)]), Ok(expected));
// It works if we update the memory
memory.uint8view().set_index(0, 3);
let expected = vec![Val::I32(36)].into_boxed_slice();
assert_eq!(exported.call(&[Val::I32(4)]), Ok(expected));
}
#[wasm_bindgen_test]
fn test_imported_exported_global() {
let store = Store::default();
let mut module = Module::new(
&store,
br#"
(module
(global $mut_i32_import (import "" "global") (mut i32))
(func (export "getGlobal") (result i32) (global.get $mut_i32_import))
(func (export "incGlobal") (global.set $mut_i32_import (
i32.add (i32.const 1) (global.get $mut_i32_import)
)))
)
"#,
)
.unwrap();
module
.set_type_hints(ModuleTypeHints {
imports: vec![ExternType::Global(GlobalType::new(
ValType::I32,
Mutability::Var,
))],
exports: vec![
ExternType::Function(FunctionType::new(vec![], vec![Type::I32])),
ExternType::Function(FunctionType::new(vec![], vec![])),
],
})
.unwrap();
let global = Global::new_mut(&store, Value::I32(0));
let import_object = imports! {
"" => {
"global" => global.clone()
}
};
let instance = Instance::new(&module, &import_object).unwrap();
let get_global = instance.exports.get_function("getGlobal").unwrap();
assert_eq!(
get_global.call(&[]),
Ok(vec![Val::I32(0)].into_boxed_slice())
);
global.set(Value::I32(42)).unwrap();
assert_eq!(
get_global.call(&[]),
Ok(vec![Val::I32(42)].into_boxed_slice())
);
let inc_global = instance.exports.get_function("incGlobal").unwrap();
inc_global.call(&[]).unwrap();
assert_eq!(
get_global.call(&[]),
Ok(vec![Val::I32(43)].into_boxed_slice())
);
assert_eq!(global.get(), Val::I32(43));
}
#[wasm_bindgen_test]
fn test_native_function() {
let store = Store::default();
let module = Module::new(
&store,
br#"(module
(func $add (import "env" "sum") (param i32 i32) (result i32))
(func (export "add_one") (param i32) (result i32)
(call $add (local.get 0) (i32.const 1))
)
)"#,
)
.unwrap();
fn sum(a: i32, b: i32) -> i32 {
a + b
}
let import_object = imports! {
"env" => {
"sum" => Function::new_native(&store, sum),
}
};
let instance = Instance::new(&module, &import_object).unwrap();
let add_one: NativeFunc<i32, i32> = instance.exports.get_native_function("add_one").unwrap();
assert_eq!(add_one.call(1), Ok(2));
}
#[wasm_bindgen_test]
fn test_panic() {
let store = Store::default();
let module = Module::new(
&store,
br#"
(module
(type $run_t (func (param i32 i32) (result i32)))
(type $early_exit_t (func (param) (result)))
(import "env" "early_exit" (func $early_exit (type $early_exit_t)))
(func $run (type $run_t) (param $x i32) (param $y i32) (result i32)
(call $early_exit)
(i32.add
local.get $x
local.get $y))
(export "run" (func $run)))
"#,
)
.unwrap();
fn early_exit() {
panic!("Do panic")
}
let import_object = imports! {
"env" => {
"early_exit" => Function::new_native(&store, early_exit),
}
};
let instance = Instance::new(&module, &import_object).unwrap();
let run_func: NativeFunc<(i32, i32), i32> =
instance.exports.get_native_function("run").unwrap();
assert!(run_func.call(1, 7).is_err(), "Expected early termination",);
let run_func = instance.exports.get_function("run").unwrap();
assert!(
run_func.call(&[Val::I32(1), Val::I32(7)]).is_err(),
"Expected early termination",
);
}
#[wasm_bindgen_test]
fn test_custom_error() {
let store = Store::default();
let module = Module::new(
&store,
br#"
(module
(type $run_t (func (param i32 i32) (result i32)))
(type $early_exit_t (func (param) (result)))
(import "env" "early_exit" (func $early_exit (type $early_exit_t)))
(func $run (type $run_t) (param $x i32) (param $y i32) (result i32)
(call $early_exit)
(i32.add
local.get $x
local.get $y))
(export "run" (func $run)))
"#,
)
.unwrap();
use std::fmt;
#[derive(Debug, Clone, Copy)]
struct ExitCode(u32);
impl fmt::Display for ExitCode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl std::error::Error for ExitCode {}
fn early_exit() {
RuntimeError::raise(Box::new(ExitCode(1)));
}
let import_object = imports! {
"env" => {
"early_exit" => Function::new_native(&store, early_exit),
}
};
let instance = Instance::new(&module, &import_object).unwrap();
fn test_result<T: core::fmt::Debug>(result: Result<T, RuntimeError>) {
match result {
Ok(result) => {
assert!(
false,
"Expected early termination with `ExitCode`, found: {:?}",
result
);
}
Err(e) => {
match e.downcast::<ExitCode>() {
// We found the exit code used to terminate execution.
Ok(exit_code) => {
assert_eq!(exit_code.0, 1);
}
Err(e) => {
assert!(false, "Unknown error `{:?}` found. expected `ErrorCode`", e);
}
}
}
}
}
let run_func: NativeFunc<(i32, i32), i32> =
instance.exports.get_native_function("run").unwrap();
test_result(run_func.call(1, 7));
let run_func = instance.exports.get_function("run").unwrap();
test_result(run_func.call(&[Val::I32(1), Val::I32(7)]));
}
#[wasm_bindgen_test]
fn test_start_function_fails() {
let store = Store::default();
let module = Module::new(
&store,
br#"
(module
(func $start_function
(i32.div_u
(i32.const 1)
(i32.const 0)
)
drop
)
(start $start_function)
)
"#,
)
.unwrap();
let import_object = imports! {};
let result = Instance::new(&module, &import_object);
let err = result.unwrap_err();
assert!(format!("{:?}", err).contains("zero"))
}

View File

@@ -1,291 +0,0 @@
use js_sys::{Uint8Array, WebAssembly};
use wasm_bindgen_test::*;
use wasmer_js::*;
#[wasm_bindgen_test]
fn module_get_name() {
let store = Store::default();
let wat = r#"(module)"#;
let module = Module::new(&store, wat).unwrap();
assert_eq!(module.name(), None);
}
#[wasm_bindgen_test]
fn module_set_name() {
let store = Store::default();
let wat = r#"(module $name)"#;
let mut module = Module::new(&store, wat).unwrap();
#[cfg(feature = "wasm-types-polyfill")]
assert_eq!(module.name(), Some("name"));
module.set_name("new_name");
assert_eq!(module.name(), Some("new_name"));
}
#[wasm_bindgen_test]
fn module_from_jsmodule() {
let wat = br#"(module $name)"#;
let binary = wat2wasm(wat).unwrap();
let js_bytes = unsafe { Uint8Array::view(&binary) };
let js_module = WebAssembly::Module::new(&js_bytes.into()).unwrap();
let module: Module = js_module.into();
assert_eq!(module.store(), &Store::default());
}
#[wasm_bindgen_test]
fn imports() {
let store = Store::default();
let wat = r#"(module
(import "host" "func" (func))
(import "host" "memory" (memory 1))
(import "host" "table" (table 1 anyfunc))
(import "host" "global" (global i32))
)"#;
let module = Module::new(&store, wat).unwrap();
assert_eq!(
module.imports().collect::<Vec<_>>(),
vec![
ImportType::new(
"host",
"func",
ExternType::Function(FunctionType::new(vec![], vec![]))
),
ImportType::new(
"host",
"memory",
ExternType::Memory(MemoryType::new(Pages(1), None, false))
),
ImportType::new(
"host",
"table",
ExternType::Table(TableType::new(Type::FuncRef, 1, None))
),
ImportType::new(
"host",
"global",
ExternType::Global(GlobalType::new(Type::I32, Mutability::Const))
)
]
);
// Now we test the iterators
assert_eq!(
module.imports().functions().collect::<Vec<_>>(),
vec![ImportType::new(
"host",
"func",
FunctionType::new(vec![], vec![])
),]
);
assert_eq!(
module.imports().memories().collect::<Vec<_>>(),
vec![ImportType::new(
"host",
"memory",
MemoryType::new(Pages(1), None, false)
),]
);
assert_eq!(
module.imports().tables().collect::<Vec<_>>(),
vec![ImportType::new(
"host",
"table",
TableType::new(Type::FuncRef, 1, None)
),]
);
assert_eq!(
module.imports().globals().collect::<Vec<_>>(),
vec![ImportType::new(
"host",
"global",
GlobalType::new(Type::I32, Mutability::Const)
),]
);
}
#[wasm_bindgen_test]
fn exports() {
let store = Store::default();
let wat = r#"(module
(func (export "func") nop)
(memory (export "memory") 2)
(table (export "table") 2 funcref)
(global (export "global") i32 (i32.const 0))
)"#;
let mut module = Module::new(&store, wat).unwrap();
module
.set_type_hints(ModuleTypeHints {
exports: vec![
ExternType::Function(FunctionType::new(vec![], vec![])),
ExternType::Memory(MemoryType::new(Pages(2), None, false)),
ExternType::Table(TableType::new(Type::FuncRef, 2, None)),
ExternType::Global(GlobalType::new(Type::I32, Mutability::Const)),
],
imports: vec![],
})
.unwrap();
assert_eq!(
module.exports().collect::<Vec<_>>(),
vec![
ExportType::new(
"func",
ExternType::Function(FunctionType::new(vec![], vec![]))
),
ExportType::new(
"memory",
ExternType::Memory(MemoryType::new(Pages(2), None, false))
),
ExportType::new(
"table",
ExternType::Table(TableType::new(Type::FuncRef, 2, None))
),
ExportType::new(
"global",
ExternType::Global(GlobalType::new(Type::I32, Mutability::Const))
)
]
);
// Now we test the iterators
assert_eq!(
module.exports().functions().collect::<Vec<_>>(),
vec![ExportType::new("func", FunctionType::new(vec![], vec![])),]
);
assert_eq!(
module.exports().memories().collect::<Vec<_>>(),
vec![ExportType::new(
"memory",
MemoryType::new(Pages(2), None, false)
),]
);
assert_eq!(
module.exports().tables().collect::<Vec<_>>(),
vec![ExportType::new(
"table",
TableType::new(Type::FuncRef, 2, None)
),]
);
assert_eq!(
module.exports().globals().collect::<Vec<_>>(),
vec![ExportType::new(
"global",
GlobalType::new(Type::I32, Mutability::Const)
),]
);
}
// Test commented because it doesn't work in old versions of Node
// which makes the CI to fail.
// #[wasm_bindgen_test]
// fn calling_host_functions_with_negative_values_works() {
// let store = Store::default();
// let wat = r#"(module
// (import "host" "host_func1" (func (param i64)))
// (import "host" "host_func2" (func (param i32)))
// (import "host" "host_func3" (func (param i64)))
// (import "host" "host_func4" (func (param i32)))
// (import "host" "host_func5" (func (param i32)))
// (import "host" "host_func6" (func (param i32)))
// (import "host" "host_func7" (func (param i32)))
// (import "host" "host_func8" (func (param i32)))
// (func (export "call_host_func1")
// (call 0 (i64.const -1)))
// (func (export "call_host_func2")
// (call 1 (i32.const -1)))
// (func (export "call_host_func3")
// (call 2 (i64.const -1)))
// (func (export "call_host_func4")
// (call 3 (i32.const -1)))
// (func (export "call_host_func5")
// (call 4 (i32.const -1)))
// (func (export "call_host_func6")
// (call 5 (i32.const -1)))
// (func (export "call_host_func7")
// (call 6 (i32.const -1)))
// (func (export "call_host_func8")
// (call 7 (i32.const -1)))
// )"#;
// let module = Module::new(&store, wat).unwrap();
// let imports = imports! {
// "host" => {
// "host_func1" => Function::new_native(&store, |p: u64| {
// println!("host_func1: Found number {}", p);
// assert_eq!(p, u64::max_value());
// }),
// "host_func2" => Function::new_native(&store, |p: u32| {
// println!("host_func2: Found number {}", p);
// assert_eq!(p, u32::max_value());
// }),
// "host_func3" => Function::new_native(&store, |p: i64| {
// println!("host_func3: Found number {}", p);
// assert_eq!(p, -1);
// }),
// "host_func4" => Function::new_native(&store, |p: i32| {
// println!("host_func4: Found number {}", p);
// assert_eq!(p, -1);
// }),
// "host_func5" => Function::new_native(&store, |p: i16| {
// println!("host_func5: Found number {}", p);
// assert_eq!(p, -1);
// }),
// "host_func6" => Function::new_native(&store, |p: u16| {
// println!("host_func6: Found number {}", p);
// assert_eq!(p, u16::max_value());
// }),
// "host_func7" => Function::new_native(&store, |p: i8| {
// println!("host_func7: Found number {}", p);
// assert_eq!(p, -1);
// }),
// "host_func8" => Function::new_native(&store, |p: u8| {
// println!("host_func8: Found number {}", p);
// assert_eq!(p, u8::max_value());
// }),
// }
// };
// let instance = Instance::new(&module, &imports).unwrap();
// let f1: NativeFunc<(), ()> = instance
// .exports
// .get_native_function("call_host_func1")
// .unwrap();
// let f2: NativeFunc<(), ()> = instance
// .exports
// .get_native_function("call_host_func2")
// .unwrap();
// let f3: NativeFunc<(), ()> = instance
// .exports
// .get_native_function("call_host_func3")
// .unwrap();
// let f4: NativeFunc<(), ()> = instance
// .exports
// .get_native_function("call_host_func4")
// .unwrap();
// let f5: NativeFunc<(), ()> = instance
// .exports
// .get_native_function("call_host_func5")
// .unwrap();
// let f6: NativeFunc<(), ()> = instance
// .exports
// .get_native_function("call_host_func6")
// .unwrap();
// let f7: NativeFunc<(), ()> = instance
// .exports
// .get_native_function("call_host_func7")
// .unwrap();
// let f8: NativeFunc<(), ()> = instance
// .exports
// .get_native_function("call_host_func8")
// .unwrap();
// f1.call().unwrap();
// f2.call().unwrap();
// f3.call().unwrap();
// f4.call().unwrap();
// f5.call().unwrap();
// f6.call().unwrap();
// f7.call().unwrap();
// f8.call().unwrap();
// }