Added basic example of Wasmer-js

This commit is contained in:
Syrus Akbary
2021-06-21 21:14:16 -07:00
parent 7ddf61a3db
commit 430113dbc2
30 changed files with 7558 additions and 0 deletions

68
Cargo.lock generated
View File

@@ -349,6 +349,16 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "console_error_panic_hook"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8d976903543e0c48546a91908f21588a680a8c8f984df9a5d69feccb2b2a211"
dependencies = [
"cfg-if 0.1.10",
"wasm-bindgen",
]
[[package]] [[package]]
name = "constant_time_eq" name = "constant_time_eq"
version = "0.1.5" version = "0.1.5"
@@ -1755,6 +1765,12 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "scoped-tls"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
[[package]] [[package]]
name = "scopeguard" name = "scopeguard"
version = "1.1.0" version = "1.1.0"
@@ -2300,6 +2316,18 @@ dependencies = [
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fba7978c679d53ce2d0ac80c8c175840feb849a161664365d1287b41f2e67f1"
dependencies = [
"cfg-if 1.0.0",
"js-sys",
"wasm-bindgen",
"web-sys",
]
[[package]] [[package]]
name = "wasm-bindgen-macro" name = "wasm-bindgen-macro"
version = "0.2.74" version = "0.2.74"
@@ -2329,6 +2357,30 @@ version = "0.2.74"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f" checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f"
[[package]]
name = "wasm-bindgen-test"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cab416a9b970464c2882ed92d55b0c33046b08e0bdc9d59b3b718acd4e1bae8"
dependencies = [
"console_error_panic_hook",
"js-sys",
"scoped-tls",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-bindgen-test-macro",
]
[[package]]
name = "wasm-bindgen-test-macro"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd4543fc6cf3541ef0d98bf720104cc6bd856d7eba449fd2aa365ef4fed0e782"
dependencies = [
"proc-macro2",
"quote",
]
[[package]] [[package]]
name = "wasm-encoder" name = "wasm-encoder"
version = "0.4.1" version = "0.4.1"
@@ -2673,6 +2725,22 @@ dependencies = [
"tempfile", "tempfile",
] ]
[[package]]
name = "wasmer-js"
version = "2.0.0"
dependencies = [
"anyhow",
"cfg-if 1.0.0",
"indexmap",
"js-sys",
"more-asserts",
"thiserror",
"wasm-bindgen",
"wasm-bindgen-test",
"wasmer-types",
"wat",
]
[[package]] [[package]]
name = "wasmer-middlewares" name = "wasmer-middlewares"
version = "2.0.0" version = "2.0.0"

View File

@@ -43,6 +43,7 @@ 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",

39
lib/js-api/Cargo.toml Normal file
View File

@@ -0,0 +1,39 @@
[package]
name = "wasmer-js"
version = "2.0.0"
description = "High-performant WebAssembly runtime (JS Bindings)"
categories = ["wasm"]
keywords = ["wasm", "webassembly", "runtime", "vm"]
authors = ["Wasmer Engineering Team <engineering@wasmer.io>"]
repository = "https://github.com/wasmerio/wasmer"
license = "MIT"
readme = "README.md"
edition = "2018"
[dependencies]
# executor = "0.8.0"
wasm-bindgen = { version = "0.2.74" }
js-sys = { version = "0.3.51" }
# wasm-bindgen = { version = "0.2.74", path = "../../../wasm-bindgen/"}
# js-sys = { version = "0.3.51", path = "../../../wasm-bindgen/crates/js-sys"}
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"
[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 = ["wat", "std"]
std = []
core = []

71
lib/js-api/README.md Normal file
View File

@@ -0,0 +1,71 @@
# `wasmer` [![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.svg)](https://crates.io/crates/wasmer)
[`Wasmer`](https://wasmer.io/) is the most popular
[WebAssembly](https://webassembly.org/) runtime for Rust (...and also
the fastest). It supports JIT (Just in Time) and AOT (Ahead of time)
compilation as well as pluggable compilers suited to your needs.
It's designed to be safe and secure, and runnable in any kind of environment.
## Usage
```rust
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(())
}
```
## Features
Wasmer is not only fast, but also designed to be *highly customizable*:
* **Pluggable Engines**: do you have a fancy `dlopen` implementation? This is for you!
* **Pluggable Compilers**: you want to emit code with DynASM or other compiler? We got you!
* **Headless mode**: that means that no compilers will be required
to run a `serialized` Module (via `Module::deserialize()`).
* **Cross-compilation**: You can pre-compile a module and serialize it
to then run it in other platform (via `Module::serialize()`).
## Config flags
Wasmer has the following configuration flags:
* `wat` (enabled by default): It allows to read WebAssembly files in their text format.
*This feature is normally used only in development environments*
* Compilers (mutually exclusive):
- `singlepass`: it will use `wasmer-compiler-singlepass` as the default
compiler (ideal for **blockchains**).
- `cranelift`: it will use `wasmer-compiler-cranelift` as the default
compiler (ideal for **development**).
- `llvm`: it will use `wasmer-compiler-llvm` as the default
compiler (ideal for **production**).
Wasmer ships by default with the `cranelift` compiler as its great for development proposes.
However, we strongly encourage to use the `llvm` backend in production as it performs
about 50% faster, achieving near-native speeds.
> Note: if you want to use multiple compilers at the same time, it's also possible!
> You will need to import them directly via each of the compiler crates.
---
Made with ❤️ by the Wasmer team, for the community

224
lib/js-api/src/env.rs Normal file
View File

@@ -0,0 +1,224 @@
use crate::{ExportError, Instance};
use thiserror::Error;
/// An error while initializing the user supplied host env with the `WasmerEnv` trait.
#[derive(Error, Debug)]
#[error("Host env initialization error: {0}")]
pub enum HostEnvInitError {
/// An error occurred when accessing an export
Export(ExportError),
}
impl From<ExportError> for HostEnvInitError {
fn from(other: ExportError) -> Self {
Self::Export(other)
}
}
/// Trait for initializing the environments passed to host functions after
/// instantiation but before execution.
///
/// This is useful for filling an environment with data that can only be accesed
/// after instantiation. For example, exported items such as memories and
/// functions which don't exist prior to instantiation can be accessed here so
/// that host functions can use them.
///
/// # Examples
///
/// This trait can be derived like so:
///
/// ```
/// use wasmer::{WasmerEnv, LazyInit, Memory, NativeFunc};
///
/// #[derive(WasmerEnv, Clone)]
/// pub struct MyEnvWithNoInstanceData {
/// non_instance_data: u8,
/// }
///
/// #[derive(WasmerEnv, Clone)]
/// pub struct MyEnvWithInstanceData {
/// non_instance_data: u8,
/// #[wasmer(export)]
/// memory: LazyInit<Memory>,
/// #[wasmer(export(name = "real_name"))]
/// func: LazyInit<NativeFunc<(i32, i32), i32>>,
/// #[wasmer(export(optional = true, alias = "memory2", alias = "_memory2"))]
/// optional_memory: LazyInit<Memory>,
/// }
///
/// ```
///
/// When deriving `WasmerEnv`, you must wrap your types to be initialized in
/// [`LazyInit`]. The derive macro will also generate helper methods of the form
/// `<field_name>_ref` and `<field_name>_ref_unchecked` for easy access to the
/// data.
///
/// The valid arguments to `export` are:
/// - `name = "string"`: specify the name of this item in the Wasm module. If this is not specified, it will default to the name of the field.
/// - `optional = true`: specify whether this export is optional. Defaults to
/// `false`. Being optional means that if the export can't be found, the
/// [`LazyInit`] will be left uninitialized.
/// - `alias = "string"`: specify additional names to look for in the Wasm module.
/// `alias` may be specified multiple times to search for multiple aliases.
/// -------
///
/// This trait may also be implemented manually:
/// ```
/// # use wasmer::{WasmerEnv, LazyInit, Memory, Instance, HostEnvInitError};
/// #[derive(Clone)]
/// pub struct MyEnv {
/// memory: LazyInit<Memory>,
/// }
///
/// impl WasmerEnv for MyEnv {
/// fn init_with_instance(&mut self, instance: &Instance) -> Result<(), HostEnvInitError> {
/// let memory: Memory = instance.exports.get_with_generics_weak("memory").unwrap();
/// self.memory.initialize(memory.clone());
/// Ok(())
/// }
/// }
/// ```
///
/// When implementing the trait manually, it's important to get a "weak" export to
/// prevent a cyclic reference leaking memory. You can access a "weak" export with
/// a method like `get_with_generics_weak`.
pub trait WasmerEnv: Clone + Send + Sync {
/// The function that Wasmer will call on your type to let it finish
/// setting up the environment with data from the `Instance`.
///
/// This function is called after `Instance` is created but before it is
/// returned to the user via `Instance::new`.
fn init_with_instance(&mut self, _instance: &Instance) -> Result<(), HostEnvInitError> {
Ok(())
}
}
impl WasmerEnv for u8 {}
impl WasmerEnv for i8 {}
impl WasmerEnv for u16 {}
impl WasmerEnv for i16 {}
impl WasmerEnv for u32 {}
impl WasmerEnv for i32 {}
impl WasmerEnv for u64 {}
impl WasmerEnv for i64 {}
impl WasmerEnv for u128 {}
impl WasmerEnv for i128 {}
impl WasmerEnv for f32 {}
impl WasmerEnv for f64 {}
impl WasmerEnv for usize {}
impl WasmerEnv for isize {}
impl WasmerEnv for char {}
impl WasmerEnv for bool {}
impl WasmerEnv for String {}
impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicBool {}
impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicI8 {}
impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicU8 {}
impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicI16 {}
impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicU16 {}
impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicI32 {}
impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicU32 {}
impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicI64 {}
impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicUsize {}
impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicIsize {}
impl<T: WasmerEnv> WasmerEnv for Box<T> {
fn init_with_instance(&mut self, instance: &Instance) -> Result<(), HostEnvInitError> {
(&mut **self).init_with_instance(instance)
}
}
impl<T: WasmerEnv> WasmerEnv for ::std::sync::Arc<::std::sync::Mutex<T>> {
fn init_with_instance(&mut self, instance: &Instance) -> Result<(), HostEnvInitError> {
let mut guard = self.lock().unwrap();
guard.init_with_instance(instance)
}
}
/// Lazily init an item
pub struct LazyInit<T: Sized> {
/// The data to be initialized
data: std::mem::MaybeUninit<T>,
/// Whether or not the data has been initialized
initialized: bool,
}
impl<T> LazyInit<T> {
/// Creates an unitialized value.
pub fn new() -> Self {
Self {
data: std::mem::MaybeUninit::uninit(),
initialized: false,
}
}
/// # Safety
/// - The data must be initialized first
pub unsafe fn get_unchecked(&self) -> &T {
&*self.data.as_ptr()
}
/// Get the inner data.
pub fn get_ref(&self) -> Option<&T> {
if !self.initialized {
None
} else {
Some(unsafe { self.get_unchecked() })
}
}
/// Sets a value and marks the data as initialized.
pub fn initialize(&mut self, value: T) -> bool {
if self.initialized {
return false;
}
unsafe {
self.data.as_mut_ptr().write(value);
}
self.initialized = true;
true
}
}
impl<T: std::fmt::Debug> std::fmt::Debug for LazyInit<T> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("LazyInit")
.field("data", &self.get_ref())
.finish()
}
}
impl<T: Clone> Clone for LazyInit<T> {
fn clone(&self) -> Self {
if let Some(inner) = self.get_ref() {
Self {
data: std::mem::MaybeUninit::new(inner.clone()),
initialized: true,
}
} else {
Self {
data: std::mem::MaybeUninit::uninit(),
initialized: false,
}
}
}
}
impl<T> Drop for LazyInit<T> {
fn drop(&mut self) {
if self.initialized {
unsafe {
let ptr = self.data.as_mut_ptr();
std::ptr::drop_in_place(ptr);
};
}
}
}
impl<T> Default for LazyInit<T> {
fn default() -> Self {
Self::new()
}
}
unsafe impl<T: Send> Send for LazyInit<T> {}
// I thought we could opt out of sync..., look into this
// unsafe impl<T> !Sync for InitWithInstance<T> {}

82
lib/js-api/src/error.rs Normal file
View File

@@ -0,0 +1,82 @@
use crate::lib::std::string::String;
#[cfg(feature = "std")]
use thiserror::Error;
// Compilation Errors
//
// If `std` feature is enable, we can't use `thiserror` until
// https://github.com/dtolnay/thiserror/pull/64 is merged.
/// The WebAssembly.CompileError object indicates an error during
/// WebAssembly decoding or validation.
///
/// This is based on the [Wasm Compile Error][compile-error] API.
///
/// [compiler-error]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/CompileError
#[derive(Debug)]
#[cfg_attr(feature = "std", derive(Error))]
pub enum CompileError {
/// A Wasm translation error occured.
#[cfg_attr(feature = "std", error("WebAssembly translation error: {0}"))]
Wasm(WasmError),
/// A compilation error occured.
#[cfg_attr(feature = "std", error("Compilation error: {0}"))]
Codegen(String),
/// The module did not pass validation.
#[cfg_attr(feature = "std", error("Validation error: {0}"))]
Validate(String),
/// The compiler doesn't support a Wasm feature
#[cfg_attr(feature = "std", error("Feature {0} is not yet supported"))]
UnsupportedFeature(String),
/// The compiler cannot compile for the given target.
/// This can refer to the OS, the chipset or any other aspect of the target system.
#[cfg_attr(feature = "std", error("The target {0} is not yet supported (see https://docs.wasmer.io/ecosystem/wasmer/wasmer-features)"))]
UnsupportedTarget(String),
/// Insufficient resources available for execution.
#[cfg_attr(feature = "std", error("Insufficient resources: {0}"))]
Resource(String),
}
impl From<WasmError> for CompileError {
fn from(original: WasmError) -> Self {
Self::Wasm(original)
}
}
/// A WebAssembly translation error.
///
/// When a WebAssembly function can't be translated, one of these error codes will be returned
/// to describe the failure.
#[derive(Debug)]
#[cfg_attr(feature = "std", derive(Error))]
pub enum WasmError {
/// The input WebAssembly code is invalid.
///
/// This error code is used by a WebAssembly translator when it encounters invalid WebAssembly
/// code. This should never happen for validated WebAssembly code.
#[cfg_attr(
feature = "std",
error("Invalid input WebAssembly code at offset {offset}: {message}")
)]
InvalidWebAssembly {
/// A string describing the validation error.
message: String,
/// The bytecode offset where the error occurred.
offset: usize,
},
/// A feature used by the WebAssembly code is not supported by the embedding environment.
///
/// Embedding environments may have their own limitations and feature restrictions.
#[cfg_attr(feature = "std", error("Unsupported feature: {0}"))]
Unsupported(String),
/// A generic error.
#[cfg_attr(feature = "std", error("{0}"))]
Generic(String),
}

37
lib/js-api/src/export.rs Normal file
View File

@@ -0,0 +1,37 @@
use js_sys::WebAssembly::Memory;
use wasm_bindgen::JsCast;
use wasm_bindgen::JsValue;
pub type VMMemory = Memory;
/// The value of an export passed from one instance to another.
#[derive(Debug, Clone)]
pub enum Export {
// /// A function export value.
// Function(ExportFunction),
// /// A table export value.
// Table(VMTable),
/// A memory export value.
Memory(VMMemory),
// /// A global export value.
// Global(VMGlobal),
}
impl Export {
pub fn as_jsvalue(&self) -> &JsValue {
match self {
Export::Memory(js_wasm_memory) => js_wasm_memory.as_ref(),
_ => unimplemented!(),
}
}
}
impl From<JsValue> for Export {
fn from(val: JsValue) -> Export {
if val.is_instance_of::<VMMemory>() {
return Export::Memory(val.unchecked_into::<VMMemory>());
}
unimplemented!();
}
}

339
lib/js-api/src/exports.rs Normal file
View File

@@ -0,0 +1,339 @@
use crate::export::Export;
use crate::externals::{Extern, Memory /* Function, Global, Table */};
use crate::import_object::LikeNamespace;
// use crate::native::NativeFunc;
// use crate::WasmTypeList;
use indexmap::IndexMap;
use std::fmt;
use std::iter::{ExactSizeIterator, FromIterator};
use std::sync::Arc;
use thiserror::Error;
use wasm_bindgen::JsValue;
/// The `ExportError` can happen when trying to get a specific
/// export [`Extern`] from the [`Instance`] exports.
///
/// [`Instance`]: crate::Instance
///
/// # Examples
///
/// ## Incompatible export type
///
/// ```should_panic
/// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, Value, ExportError};
/// # let store = Store::default();
/// # let wasm_bytes = wat2wasm(r#"
/// # (module
/// # (global $one (export "glob") f32 (f32.const 1)))
/// # "#.as_bytes()).unwrap();
/// # let module = Module::new(&store, wasm_bytes).unwrap();
/// # let import_object = imports! {};
/// # let instance = Instance::new(&module, &import_object).unwrap();
/// #
/// // This results with an error: `ExportError::IncompatibleType`.
/// let export = instance.exports.get_function("glob").unwrap();
/// ```
///
/// ## Missing export
///
/// ```should_panic
/// # use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, Type, Value, ExportError};
/// # let store = Store::default();
/// # let wasm_bytes = wat2wasm("(module)".as_bytes()).unwrap();
/// # let module = Module::new(&store, wasm_bytes).unwrap();
/// # let import_object = imports! {};
/// # let instance = Instance::new(&module, &import_object).unwrap();
/// #
/// // This results with an error: `ExportError::Missing`.
/// let export = instance.exports.get_function("unknown").unwrap();
/// ```
#[derive(Error, Debug)]
pub enum ExportError {
/// An error than occurs when the exported type and the expected type
/// are incompatible.
#[error("Incompatible Export Type")]
IncompatibleType,
/// This error arises when an export is missing
#[error("Missing export {0}")]
Missing(String),
}
/// Exports is a special kind of map that allows easily unwrapping
/// the types of instances.
///
/// TODO: add examples of using exports
#[derive(Clone, Default)]
pub struct Exports {
map: Arc<IndexMap<String, Extern>>,
}
impl Exports {
/// Creates a new `Exports`.
pub fn new() -> Self {
Default::default()
}
/// Creates a new `Exports` with capacity `n`.
pub fn with_capacity(n: usize) -> Self {
Self {
map: Arc::new(IndexMap::with_capacity(n)),
}
}
/// Return the number of exports in the `Exports` map.
pub fn len(&self) -> usize {
self.map.len()
}
/// Return whether or not there are no exports
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Insert a new export into this `Exports` map.
pub fn insert<S, E>(&mut self, name: S, value: E)
where
S: Into<String>,
E: Into<Extern>,
{
Arc::get_mut(&mut self.map)
.unwrap()
.insert(name.into(), value.into());
}
/// Get an export given a `name`.
///
/// The `get` method is specifically made for usage inside of
/// Rust APIs, as we can detect what's the desired type easily.
///
/// If you want to get an export dynamically with type checking
/// please use the following functions: `get_func`, `get_memory`,
/// `get_table` or `get_global` instead.
///
/// If you want to get an export dynamically handling manually
/// type checking manually, please use `get_extern`.
pub fn get<'a, T: Exportable<'a>>(&'a self, name: &str) -> Result<&'a T, ExportError> {
match self.map.get(name) {
None => Err(ExportError::Missing(name.to_string())),
Some(extern_) => T::get_self_from_extern(extern_),
}
}
// /// Get an export as a `Global`.
// pub fn get_global(&self, name: &str) -> Result<&Global, ExportError> {
// self.get(name)
// }
/// Get an export as a `Memory`.
pub fn get_memory(&self, name: &str) -> Result<&Memory, ExportError> {
self.get(name)
}
// /// Get an export as a `Table`.
// pub fn get_table(&self, name: &str) -> Result<&Table, ExportError> {
// self.get(name)
// }
// /// Get an export as a `Func`.
// pub fn get_function(&self, name: &str) -> Result<&Function, ExportError> {
// self.get(name)
// }
// /// Get an export as a `NativeFunc`.
// pub fn get_native_function<Args, Rets>(
// &self,
// name: &str,
// ) -> Result<NativeFunc<Args, Rets>, ExportError>
// where
// Args: WasmTypeList,
// Rets: WasmTypeList,
// {
// self.get_function(name)?
// .native()
// .map_err(|_| ExportError::IncompatibleType)
// }
// /// Hack to get this working with nativefunc too
// pub fn get_with_generics<'a, T, Args, Rets>(&'a self, name: &str) -> Result<T, ExportError>
// where
// Args: WasmTypeList,
// Rets: WasmTypeList,
// T: ExportableWithGenerics<'a, Args, Rets>,
// {
// match self.map.get(name) {
// None => Err(ExportError::Missing(name.to_string())),
// Some(extern_) => T::get_self_from_extern_with_generics(extern_),
// }
// }
// /// Like `get_with_generics` but with a WeakReference to the `InstanceRef` internally.
// /// This is useful for passing data into `WasmerEnv`, for example.
// pub fn get_with_generics_weak<'a, T, Args, Rets>(&'a self, name: &str) -> Result<T, ExportError>
// where
// Args: WasmTypeList,
// Rets: WasmTypeList,
// T: ExportableWithGenerics<'a, Args, Rets>,
// {
// let mut out: T = self.get_with_generics(name)?;
// out.into_weak_instance_ref();
// Ok(out)
// }
/// Get an export as an `Extern`.
pub fn get_extern(&self, name: &str) -> Option<&Extern> {
self.map.get(name)
}
/// Returns true if the `Exports` contains the given export name.
pub fn contains<S>(&self, name: S) -> bool
where
S: Into<String>,
{
self.map.contains_key(&name.into())
}
/// Get an iterator over the exports.
pub fn iter(&self) -> ExportsIterator<impl Iterator<Item = (&String, &Extern)>> {
ExportsIterator {
iter: self.map.iter(),
}
}
}
impl fmt::Debug for Exports {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_set().entries(self.iter()).finish()
}
}
/// An iterator over exports.
pub struct ExportsIterator<'a, I>
where
I: Iterator<Item = (&'a String, &'a Extern)> + Sized,
{
iter: I,
}
impl<'a, I> Iterator for ExportsIterator<'a, I>
where
I: Iterator<Item = (&'a String, &'a Extern)> + Sized,
{
type Item = (&'a String, &'a Extern);
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
}
impl<'a, I> ExactSizeIterator for ExportsIterator<'a, I>
where
I: Iterator<Item = (&'a String, &'a Extern)> + ExactSizeIterator + Sized,
{
fn len(&self) -> usize {
self.iter.len()
}
}
impl<'a, I> ExportsIterator<'a, I>
where
I: Iterator<Item = (&'a String, &'a Extern)> + Sized,
{
// /// Get only the functions.
// pub fn functions(self) -> impl Iterator<Item = (&'a String, &'a Function)> + Sized {
// self.iter.filter_map(|(name, export)| match export {
// Extern::Function(function) => Some((name, function)),
// _ => None,
// })
// }
/// Get only the memories.
pub fn memories(self) -> impl Iterator<Item = (&'a String, &'a Memory)> + Sized {
self.iter.filter_map(|(name, export)| match export {
Extern::Memory(memory) => Some((name, memory)),
_ => None,
})
}
// /// Get only the globals.
// pub fn globals(self) -> impl Iterator<Item = (&'a String, &'a Global)> + Sized {
// self.iter.filter_map(|(name, export)| match export {
// Extern::Global(global) => Some((name, global)),
// _ => None,
// })
// }
// /// Get only the tables.
// pub fn tables(self) -> impl Iterator<Item = (&'a String, &'a Table)> + Sized {
// self.iter.filter_map(|(name, export)| match export {
// Extern::Table(table) => Some((name, table)),
// _ => None,
// })
// }
}
impl FromIterator<(String, Extern)> for Exports {
fn from_iter<I: IntoIterator<Item = (String, Extern)>>(iter: I) -> Self {
Self {
map: Arc::new(IndexMap::from_iter(iter)),
}
}
}
impl LikeNamespace for Exports {
fn get_namespace_export(&self, name: &str) -> Option<Export> {
self.map.get(name).map(|is_export| is_export.to_export())
}
fn get_namespace_exports(&self) -> Vec<(String, Export)> {
self.map
.iter()
.map(|(k, v)| (k.clone(), v.to_export()))
.collect()
}
}
/// This trait is used to mark types as gettable from an [`Instance`].
///
/// [`Instance`]: crate::Instance
pub trait Exportable<'a>: Sized {
/// This function is used when providedd the [`Extern`] as exportable, so it
/// can be used while instantiating the [`Module`].
///
/// [`Module`]: crate::Module
fn to_export(&self) -> Export;
/// Implementation of how to get the export corresponding to the implementing type
/// from an [`Instance`] by name.
///
/// [`Instance`]: crate::Instance
fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError>;
/// Convert the extern internally to hold a weak reference to the `InstanceRef`.
/// This is useful for preventing cycles, for example for data stored in a
/// type implementing `WasmerEnv`.
fn into_weak_instance_ref(&mut self);
}
// /// A trait for accessing exports (like [`Exportable`]) but it takes generic
// /// `Args` and `Rets` parameters so that `NativeFunc` can be accessed directly
// /// as well.
// pub trait ExportableWithGenerics<'a, Args: WasmTypeList, Rets: WasmTypeList>: Sized {
// /// Get an export with the given generics.
// fn get_self_from_extern_with_generics(_extern: &'a Extern) -> Result<Self, ExportError>;
// /// Convert the extern internally to hold a weak reference to the `InstanceRef`.
// /// This is useful for preventing cycles, for example for data stored in a
// /// type implementing `WasmerEnv`.
// fn into_weak_instance_ref(&mut self);
// }
// /// We implement it for all concrete [`Exportable`] types (that are `Clone`)
// /// with empty `Args` and `Rets`.
// impl<'a, T: Exportable<'a> + Clone + 'static> ExportableWithGenerics<'a, (), ()> for T {
// fn get_self_from_extern_with_generics(_extern: &'a Extern) -> Result<Self, ExportError> {
// T::get_self_from_extern(_extern).map(|i| i.clone())
// }
// fn into_weak_instance_ref(&mut self) {
// <Self as Exportable>::into_weak_instance_ref(self);
// }
// }

1573
lib/js-api/src/externals/function.rs vendored Normal file

File diff suppressed because it is too large Load Diff

249
lib/js-api/src/externals/global.rs vendored Normal file
View File

@@ -0,0 +1,249 @@
use crate::exports::{ExportError, Exportable};
use crate::externals::Extern;
use crate::store::{Store, StoreObject};
use crate::types::Val;
use crate::GlobalType;
use crate::Mutability;
use crate::RuntimeError;
use std::fmt;
use std::sync::Arc;
use wasmer_engine::Export;
use wasmer_vm::{Global as RuntimeGlobal, VMGlobal};
/// A WebAssembly `global` instance.
///
/// A global instance is the runtime representation of a global variable.
/// It consists of an individual value and a flag indicating whether it is mutable.
///
/// Spec: <https://webassembly.github.io/spec/core/exec/runtime.html#global-instances>
pub struct Global {
}
impl Global {
/// Create a new `Global` with the initial value [`Val`].
///
/// # Example
///
/// ```
/// # use wasmer::{Global, Mutability, Store, Value};
/// # let store = Store::default();
/// #
/// let g = Global::new(&store, Value::I32(1));
///
/// assert_eq!(g.get(), Value::I32(1));
/// assert_eq!(g.ty().mutability, Mutability::Const);
/// ```
pub fn new(store: &Store, val: Val) -> Self {
Self::from_value(store, val, Mutability::Const).unwrap()
}
/// Create a mutable `Global` with the initial value [`Val`].
///
/// # Example
///
/// ```
/// # use wasmer::{Global, Mutability, Store, Value};
/// # let store = Store::default();
/// #
/// let g = Global::new_mut(&store, Value::I32(1));
///
/// assert_eq!(g.get(), Value::I32(1));
/// assert_eq!(g.ty().mutability, Mutability::Var);
/// ```
pub fn new_mut(store: &Store, val: Val) -> Self {
Self::from_value(store, val, Mutability::Var).unwrap()
}
/// Create a `Global` with the initial value [`Val`] and the provided [`Mutability`].
fn from_value(store: &Store, val: Val, mutability: Mutability) -> Result<Self, RuntimeError> {
if !val.comes_from_same_store(store) {
return Err(RuntimeError::new("cross-`Store` globals are not supported"));
}
let global = RuntimeGlobal::new(GlobalType {
mutability,
ty: val.ty(),
});
unsafe {
global
.set_unchecked(val.clone())
.map_err(|e| RuntimeError::new(format!("create global for {:?}: {}", val, e)))?;
};
Ok(Self {
store: store.clone(),
vm_global: VMGlobal {
from: Arc::new(global),
instance_ref: None,
},
})
}
/// Returns the [`GlobalType`] of the `Global`.
///
/// # Example
///
/// ```
/// # use wasmer::{Global, Mutability, Store, Type, Value, GlobalType};
/// # let store = Store::default();
/// #
/// let c = Global::new(&store, Value::I32(1));
/// let v = Global::new_mut(&store, Value::I64(1));
///
/// assert_eq!(c.ty(), &GlobalType::new(Type::I32, Mutability::Const));
/// assert_eq!(v.ty(), &GlobalType::new(Type::I64, Mutability::Var));
/// ```
pub fn ty(&self) -> &GlobalType {
self.vm_global.from.ty()
}
/// Returns the [`Store`] where the `Global` belongs.
///
/// # Example
///
/// ```
/// # use wasmer::{Global, Store, Value};
/// # let store = Store::default();
/// #
/// let g = Global::new(&store, Value::I32(1));
///
/// assert_eq!(g.store(), &store);
/// ```
pub fn store(&self) -> &Store {
&self.store
}
/// Retrieves the current value [`Val`] that the Global has.
///
/// # Example
///
/// ```
/// # use wasmer::{Global, Store, Value};
/// # let store = Store::default();
/// #
/// let g = Global::new(&store, Value::I32(1));
///
/// assert_eq!(g.get(), Value::I32(1));
/// ```
pub fn get(&self) -> Val {
self.vm_global.from.get(&self.store)
}
/// Sets a custom value [`Val`] to the runtime Global.
///
/// # Example
///
/// ```
/// # use wasmer::{Global, Store, Value};
/// # let store = Store::default();
/// #
/// let g = Global::new_mut(&store, Value::I32(1));
///
/// assert_eq!(g.get(), Value::I32(1));
///
/// g.set(Value::I32(2));
///
/// assert_eq!(g.get(), Value::I32(2));
/// ```
///
/// # Errors
///
/// Trying to mutate a immutable global will raise an error:
///
/// ```should_panic
/// # use wasmer::{Global, Store, Value};
/// # let store = Store::default();
/// #
/// let g = Global::new(&store, Value::I32(1));
///
/// g.set(Value::I32(2)).unwrap();
/// ```
///
/// Trying to set a value of a incompatible type will raise an error:
///
/// ```should_panic
/// # use wasmer::{Global, Store, Value};
/// # let store = Store::default();
/// #
/// let g = Global::new(&store, Value::I32(1));
///
/// // This results in an error: `RuntimeError`.
/// g.set(Value::I64(2)).unwrap();
/// ```
pub fn set(&self, val: Val) -> Result<(), RuntimeError> {
if !val.comes_from_same_store(&self.store) {
return Err(RuntimeError::new("cross-`Store` values are not supported"));
}
unsafe {
self.vm_global
.from
.set(val)
.map_err(|e| RuntimeError::new(format!("{}", e)))?;
}
Ok(())
}
pub(crate) fn from_vm_export(store: &Store, vm_global: VMGlobal) -> Self {
Self {
store: store.clone(),
vm_global,
}
}
/// Returns whether or not these two globals refer to the same data.
///
/// # Example
///
/// ```
/// # use wasmer::{Global, Store, Value};
/// # let store = Store::default();
/// #
/// let g = Global::new(&store, Value::I32(1));
///
/// assert!(g.same(&g));
/// ```
pub fn same(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.vm_global.from, &other.vm_global.from)
}
}
impl Clone for Global {
fn clone(&self) -> Self {
let mut vm_global = self.vm_global.clone();
vm_global.upgrade_instance_ref().unwrap();
Self {
store: self.store.clone(),
vm_global,
}
}
}
impl fmt::Debug for Global {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter
.debug_struct("Global")
.field("ty", &self.ty())
.field("value", &self.get())
.finish()
}
}
impl<'a> Exportable<'a> for Global {
fn to_export(&self) -> Export {
self.vm_global.clone().into()
}
fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> {
match _extern {
Extern::Global(global) => Ok(global),
_ => Err(ExportError::IncompatibleType),
}
}
fn into_weak_instance_ref(&mut self) {
self.vm_global
.instance_ref
.as_mut()
.map(|v| *v = v.downgrade());
}
}

332
lib/js-api/src/externals/memory.rs vendored Normal file
View File

@@ -0,0 +1,332 @@
use crate::export::{Export, VMMemory};
use crate::exports::{ExportError, Exportable};
use crate::externals::Extern;
use crate::store::Store;
use crate::{MemoryType, MemoryView};
use std::convert::TryInto;
use std::slice;
use std::sync::Arc;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use wasmer_types::{Bytes, Pages, ValueType};
pub type MemoryError = ();
#[wasm_bindgen]
extern "C" {
/// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Memory)
#[wasm_bindgen(js_namespace = WebAssembly, extends = js_sys::Object, typescript_type = "WebAssembly.Memory")]
#[derive(Clone, Debug, PartialEq, Eq)]
pub type JSMemory;
/// The `grow()` protoype method of the `Memory` object increases the
/// size of the memory instance by a specified number of WebAssembly
/// pages.
///
/// Takes the number of pages to grow (64KiB in size) and returns the
/// previous size of memory, in pages.
///
/// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Memory/grow)
#[wasm_bindgen(catch, method, js_namespace = WebAssembly)]
pub fn grow(this: &JSMemory, pages: u32) -> Result<u32, JsValue>;
}
/// A WebAssembly `memory` instance.
///
/// A memory instance is the runtime representation of a linear memory.
/// It consists of a vector of bytes and an optional maximum size.
///
/// The length of the vector always is a multiple of the WebAssembly
/// page size, which is defined to be the constant 65536 abbreviated 64Ki.
/// Like in a memory type, the maximum size in a memory instance is
/// given in units of this page size.
///
/// A memory created by the host or in WebAssembly code will be accessible and
/// mutable from both host and WebAssembly.
///
/// Spec: <https://webassembly.github.io/spec/core/exec/runtime.html#memory-instances>
#[derive(Debug)]
pub struct Memory {
store: Store,
ty: MemoryType,
vm_memory: VMMemory,
}
impl Memory {
/// Creates a new host `Memory` from the provided [`MemoryType`].
///
/// This function will construct the `Memory` using the store
/// [`BaseTunables`][crate::tunables::BaseTunables].
///
/// # Example
///
/// ```
/// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value};
/// # let store = Store::default();
/// #
/// let m = Memory::new(&store, MemoryType::new(1, None, false)).unwrap();
/// ```
pub fn new(store: &Store, ty: MemoryType) -> Result<Self, MemoryError> {
let descriptor = js_sys::Object::new();
js_sys::Reflect::set(&descriptor, &"initial".into(), &ty.minimum.0.into());
if let Some(max) = ty.maximum {
js_sys::Reflect::set(&descriptor, &"maximum".into(), &max.0.into());
}
js_sys::Reflect::set(&descriptor, &"shared".into(), &ty.shared.into());
let memory = VMMemory::new(&descriptor).unwrap();
// unimplemented!();
Ok(Self {
store: store.clone(),
ty,
vm_memory: memory,
})
}
/// Returns the [`MemoryType`] of the `Memory`.
///
/// # Example
///
/// ```
/// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value};
/// # let store = Store::default();
/// #
/// let mt = MemoryType::new(1, None, false);
/// let m = Memory::new(&store, mt).unwrap();
///
/// assert_eq!(m.ty(), mt);
/// ```
pub fn ty(&self) -> MemoryType {
self.ty.clone()
// unimplemented!();
// self.vm_memory.from.ty()
}
/// Returns the [`Store`] where the `Memory` belongs.
///
/// # Example
///
/// ```
/// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value};
/// # let store = Store::default();
/// #
/// let m = Memory::new(&store, MemoryType::new(1, None, false)).unwrap();
///
/// assert_eq!(m.store(), &store);
/// ```
pub fn store(&self) -> &Store {
unimplemented!();
// &self.store
}
/// Retrieve a slice of the memory contents.
///
/// # Safety
///
/// Until the returned slice is dropped, it is undefined behaviour to
/// modify the memory contents in any way including by calling a wasm
/// function that writes to the memory or by resizing the memory.
pub unsafe fn data_unchecked(&self) -> &[u8] {
unimplemented!();
// self.data_unchecked_mut()
}
/// Retrieve a mutable slice of the memory contents.
///
/// # Safety
///
/// This method provides interior mutability without an UnsafeCell. Until
/// the returned value is dropped, it is undefined behaviour to read or
/// write to the pointed-to memory in any way except through this slice,
/// including by calling a wasm function that reads the memory contents or
/// by resizing this Memory.
#[allow(clippy::mut_from_ref)]
pub unsafe fn data_unchecked_mut(&self) -> &mut [u8] {
unimplemented!();
// let definition = self.vm_memory.from.vmmemory();
// let def = definition.as_ref();
// slice::from_raw_parts_mut(def.base, def.current_length.try_into().unwrap())
}
/// Returns the pointer to the raw bytes of the `Memory`.
pub fn data_ptr(&self) -> *mut u8 {
unimplemented!();
// let definition = self.vm_memory.from.vmmemory();
// let def = unsafe { definition.as_ref() };
// def.base
}
/// Returns the size (in bytes) of the `Memory`.
pub fn data_size(&self) -> u64 {
let bytes = js_sys::Reflect::get(&self.vm_memory.buffer(), &"byteLength".into())
.unwrap()
.as_f64()
.unwrap() as u64;
return bytes;
// let def = unsafe { definition.as_ref() };
// def.current_length.into()
}
/// Returns the size (in [`Pages`]) of the `Memory`.
///
/// # Example
///
/// ```
/// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value};
/// # let store = Store::default();
/// #
/// let m = Memory::new(&store, MemoryType::new(1, None, false)).unwrap();
///
/// assert_eq!(m.size(), Pages(1));
/// ```
pub fn size(&self) -> Pages {
let bytes = js_sys::Reflect::get(&self.vm_memory.buffer(), &"byteLength".into())
.unwrap()
.as_f64()
.unwrap() as u64;
Bytes(bytes as usize).try_into().unwrap()
// self.vm_memory.from.size()
}
/// Grow memory by the specified amount of WebAssembly [`Pages`] and return
/// the previous memory size.
///
/// # Example
///
/// ```
/// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value, WASM_MAX_PAGES};
/// # let store = Store::default();
/// #
/// let m = Memory::new(&store, MemoryType::new(1, Some(3), false)).unwrap();
/// let p = m.grow(2).unwrap();
///
/// assert_eq!(p, Pages(1));
/// assert_eq!(m.size(), Pages(3));
/// ```
///
/// # Errors
///
/// Returns an error if memory can't be grown by the specified amount
/// of pages.
///
/// ```should_panic
/// # use wasmer::{Memory, MemoryType, Pages, Store, Type, Value, WASM_MAX_PAGES};
/// # let store = Store::default();
/// #
/// let m = Memory::new(&store, MemoryType::new(1, Some(1), false)).unwrap();
///
/// // This results in an error: `MemoryError::CouldNotGrow`.
/// let s = m.grow(1).unwrap();
/// ```
pub fn grow<IntoPages>(&self, delta: IntoPages) -> Result<Pages, MemoryError>
where
IntoPages: Into<Pages>,
{
let pages = delta.into();
// let new_pages = js_memory_grow(&self.vm_memory.unchecked_into::<JSMemory>(), pages.0).unwrap();
// let new_pages = self.vm_memory.unchecked_ref::<JSMemory>().grow(pages.0);
let new_pages = self.vm_memory.grow(pages.0);
Ok(Pages(new_pages))
}
/// Return a "view" of the currently accessible memory. By
/// default, the view is unsynchronized, using regular memory
/// accesses. You can force a memory view to use atomic accesses
/// by calling the [`MemoryView::atomically`] method.
///
/// # Notes:
///
/// This method is safe (as in, it won't cause the host to crash or have UB),
/// but it doesn't obey rust's rules involving data races, especially concurrent ones.
/// Therefore, if this memory is shared between multiple threads, a single memory
/// location can be mutated concurrently without synchronization.
///
/// # Usage:
///
/// ```
/// # use wasmer::{Memory, MemoryView};
/// # use std::{cell::Cell, sync::atomic::Ordering};
/// # fn view_memory(memory: Memory) {
/// // Without synchronization.
/// let view: MemoryView<u8> = memory.view();
/// for byte in view[0x1000 .. 0x1010].iter().map(Cell::get) {
/// println!("byte: {}", byte);
/// }
///
/// // With synchronization.
/// let atomic_view = view.atomically();
/// for byte in atomic_view[0x1000 .. 0x1010].iter().map(|atom| atom.load(Ordering::SeqCst)) {
/// println!("byte: {}", byte);
/// }
/// # }
/// ```
pub fn view<T: ValueType>(&self) -> MemoryView<T> {
unimplemented!();
// let base = self.data_ptr();
// let length = self.size().bytes().0 / std::mem::size_of::<T>();
// unsafe { MemoryView::new(base as _, length as u32) }
}
pub(crate) fn from_vm_export(store: &Store, vm_memory: VMMemory) -> Self {
Self {
store: store.clone(),
ty: MemoryType::new(Pages(1), None, false),
vm_memory,
}
}
/// Returns whether or not these two memories refer to the same data.
///
/// # Example
///
/// ```
/// # use wasmer::{Memory, MemoryType, Store, Value};
/// # let store = Store::default();
/// #
/// let m = Memory::new(&store, MemoryType::new(1, None, false)).unwrap();
///
/// assert!(m.same(&m));
/// ```
pub fn same(&self, other: &Self) -> bool {
unimplemented!();
// Arc::ptr_eq(&self.vm_memory.from, &other.vm_memory.from)
}
}
impl Clone for Memory {
fn clone(&self) -> Self {
unimplemented!();
// let mut vm_memory = self.vm_memory.clone();
// vm_memory.upgrade_instance_ref().unwrap();
// Self {
// store: self.store.clone(),
// vm_memory,
// }
}
}
impl<'a> Exportable<'a> for Memory {
fn to_export(&self) -> Export {
unimplemented!();
// self.vm_memory.clone().into()
}
fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> {
match _extern {
Extern::Memory(memory) => Ok(memory),
_ => Err(ExportError::IncompatibleType),
}
}
fn into_weak_instance_ref(&mut self) {
unimplemented!();
// self.vm_memory
// .instance_ref
// .as_mut()
// .map(|v| *v = v.downgrade());
}
}

122
lib/js-api/src/externals/mod.rs vendored Normal file
View File

@@ -0,0 +1,122 @@
// pub(crate) mod function;
// mod global;
mod memory;
// mod table;
// pub use self::function::{
// FromToNativeWasmType, Function, HostFunction, WasmTypeList, WithEnv, WithoutEnv,
// };
// pub use self::global::Global;
pub use self::memory::Memory;
// pub use self::table::Table;
use crate::export::Export;
use crate::exports::{ExportError, Exportable};
use crate::store::{Store, StoreObject};
use crate::ExternType;
use std::fmt;
/// An `Extern` is the runtime representation of an entity that
/// can be imported or exported.
///
/// Spec: <https://webassembly.github.io/spec/core/exec/runtime.html#external-values>
#[derive(Clone)]
pub enum Extern {
// /// A external [`Function`].
// Function(Function),
// /// A external [`Global`].
// Global(Global),
/// A external [`Table`].
// Table(Table),
/// A external [`Memory`].
Memory(Memory),
}
impl Extern {
/// Return the underlying type of the inner `Extern`.
pub fn ty(&self) -> ExternType {
match self {
// Self::Function(ft) => ExternType::Function(ft.ty().clone()),
Self::Memory(ft) => ExternType::Memory(ft.ty()),
// Self::Table(tt) => ExternType::Table(*tt.ty()),
// Self::Global(gt) => ExternType::Global(*gt.ty()),
}
}
/// Create an `Extern` from an `wasmer_engine::Export`.
pub fn from_vm_export(store: &Store, export: Export) -> Self {
match export {
// Export::Function(f) => Self::Function(Function::from_vm_export(store, f)),
Export::Memory(m) => Self::Memory(Memory::from_vm_export(store, m)),
// Export::Global(g) => Self::Global(Global::from_vm_export(store, g)),
// Export::Table(t) => Self::Table(Table::from_vm_export(store, t)),
}
}
}
impl<'a> Exportable<'a> for Extern {
fn to_export(&self) -> Export {
match self {
// Self::Function(f) => f.to_export(),
// Self::Global(g) => g.to_export(),
Self::Memory(m) => m.to_export(),
// Self::Table(t) => t.to_export(),
}
}
fn get_self_from_extern(_extern: &'a Self) -> Result<&'a Self, ExportError> {
// Since this is already an extern, we can just return it.
Ok(_extern)
}
fn into_weak_instance_ref(&mut self) {
match self {
// Self::Function(f) => f.into_weak_instance_ref(),
// Self::Global(g) => g.into_weak_instance_ref(),
Self::Memory(m) => m.into_weak_instance_ref(),
// Self::Table(t) => t.into_weak_instance_ref(),
}
}
}
impl StoreObject for Extern {}
impl fmt::Debug for Extern {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match self {
// Self::Function(_) => "Function(...)",
// Self::Global(_) => "Global(...)",
Self::Memory(_) => "Memory(...)",
// Self::Table(_) => "Table(...)",
}
)
}
}
// impl From<Function> for Extern {
// fn from(r: Function) -> Self {
// Self::Function(r)
// }
// }
// impl From<Global> for Extern {
// fn from(r: Global) -> Self {
// Self::Global(r)
// }
// }
impl From<Memory> for Extern {
fn from(r: Memory) -> Self {
Self::Memory(r)
}
}
// impl From<Table> for Extern {
// fn from(r: Table) -> Self {
// Self::Table(r)
// }
// }

193
lib/js-api/src/externals/table.rs vendored Normal file
View File

@@ -0,0 +1,193 @@
use crate::exports::{ExportError, Exportable};
use crate::externals::Extern;
use crate::store::Store;
use crate::types::{Val, ValFuncRef};
use crate::RuntimeError;
use crate::TableType;
use loupe::MemoryUsage;
use std::sync::Arc;
use wasmer_engine::Export;
use wasmer_vm::{Table as RuntimeTable, TableElement, VMTable};
/// A WebAssembly `table` instance.
///
/// The `Table` struct is an array-like structure representing a WebAssembly Table,
/// which stores function references.
///
/// A table created by the host or in WebAssembly code will be accessible and
/// mutable from both host and WebAssembly.
///
/// Spec: <https://webassembly.github.io/spec/core/exec/runtime.html#table-instances>
#[derive(MemoryUsage)]
pub struct Table {
store: Store,
vm_table: VMTable,
}
fn set_table_item(
table: &dyn RuntimeTable,
item_index: u32,
item: TableElement,
) -> Result<(), RuntimeError> {
table.set(item_index, item).map_err(|e| e.into())
}
impl Table {
/// Creates a new `Table` with the provided [`TableType`] definition.
///
/// All the elements in the table will be set to the `init` value.
///
/// This function will construct the `Table` using the store
/// [`BaseTunables`][crate::tunables::BaseTunables].
pub fn new(store: &Store, ty: TableType, init: Val) -> Result<Self, RuntimeError> {
let item = init.into_table_reference(store)?;
let tunables = store.tunables();
let style = tunables.table_style(&ty);
let table = tunables
.create_host_table(&ty, &style)
.map_err(RuntimeError::new)?;
let num_elements = table.size();
for i in 0..num_elements {
set_table_item(table.as_ref(), i, item.clone())?;
}
Ok(Self {
store: store.clone(),
vm_table: VMTable {
from: table,
instance_ref: None,
},
})
}
/// Returns the [`TableType`] of the `Table`.
pub fn ty(&self) -> &TableType {
self.vm_table.from.ty()
}
/// Returns the [`Store`] where the `Table` belongs.
pub fn store(&self) -> &Store {
&self.store
}
/// Retrieves an element of the table at the provided `index`.
pub fn get(&self, index: u32) -> Option<Val> {
let item = self.vm_table.from.get(index)?;
Some(ValFuncRef::from_table_reference(item, &self.store))
}
/// Sets an element `val` in the Table at the provided `index`.
pub fn set(&self, index: u32, val: Val) -> Result<(), RuntimeError> {
let item = val.into_table_reference(&self.store)?;
set_table_item(self.vm_table.from.as_ref(), index, item)
}
/// Retrieves the size of the `Table` (in elements)
pub fn size(&self) -> u32 {
self.vm_table.from.size()
}
/// Grows the size of the `Table` by `delta`, initializating
/// the elements with the provided `init` value.
///
/// It returns the previous size of the `Table` in case is able
/// to grow the Table successfully.
///
/// # Errors
///
/// Returns an error if the `delta` is out of bounds for the table.
pub fn grow(&self, delta: u32, init: Val) -> Result<u32, RuntimeError> {
let item = init.into_table_reference(&self.store)?;
self.vm_table
.from
.grow(delta, item)
.ok_or_else(|| RuntimeError::new(format!("failed to grow table by `{}`", delta)))
}
/// Copies the `len` elements of `src_table` starting at `src_index`
/// to the destination table `dst_table` at index `dst_index`.
///
/// # Errors
///
/// Returns an error if the range is out of bounds of either the source or
/// destination tables.
pub fn copy(
dst_table: &Self,
dst_index: u32,
src_table: &Self,
src_index: u32,
len: u32,
) -> Result<(), RuntimeError> {
if !Store::same(&dst_table.store, &src_table.store) {
return Err(RuntimeError::new(
"cross-`Store` table copies are not supported",
));
}
RuntimeTable::copy(
dst_table.vm_table.from.as_ref(),
src_table.vm_table.from.as_ref(),
dst_index,
src_index,
len,
)
.map_err(RuntimeError::from_trap)?;
Ok(())
}
pub(crate) fn from_vm_export(store: &Store, vm_table: VMTable) -> Self {
Self {
store: store.clone(),
vm_table,
}
}
/// Returns whether or not these two tables refer to the same data.
pub fn same(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.vm_table.from, &other.vm_table.from)
}
/// Get access to the backing VM value for this extern. This function is for
/// tests it should not be called by users of the Wasmer API.
///
/// # Safety
/// This function is unsafe to call outside of tests for the wasmer crate
/// because there is no stability guarantee for the returned type and we may
/// make breaking changes to it at any time or remove this method.
#[doc(hidden)]
pub unsafe fn get_vm_table(&self) -> &VMTable {
&self.vm_table
}
}
impl Clone for Table {
fn clone(&self) -> Self {
let mut vm_table = self.vm_table.clone();
vm_table.upgrade_instance_ref().unwrap();
Self {
store: self.store.clone(),
vm_table,
}
}
}
impl<'a> Exportable<'a> for Table {
fn to_export(&self) -> Export {
self.vm_table.clone().into()
}
fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> {
match _extern {
Extern::Table(table) => Ok(table),
_ => Err(ExportError::IncompatibleType),
}
}
fn into_weak_instance_ref(&mut self) {
self.vm_table
.instance_ref
.as_mut()
.map(|v| *v = v.downgrade());
}
}

View File

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

150
lib/js-api/src/instance.rs Normal file
View File

@@ -0,0 +1,150 @@
use crate::exports::Exports;
use crate::externals::Extern;
use crate::module::Module;
use crate::store::Store;
// use crate::{HostEnvInitError, LinkError, RuntimeError};
use crate::resolver::{NamedResolver, Resolver};
use js_sys::{Object, Reflect, WebAssembly};
use std::fmt;
use thiserror::Error;
/// A WebAssembly Instance is a stateful, executable
/// instance of a WebAssembly [`Module`].
///
/// Instance objects contain all the exported WebAssembly
/// functions, memories, tables and globals that allow
/// interacting with WebAssembly.
///
/// Spec: <https://webassembly.github.io/spec/core/exec/runtime.html#module-instances>
#[derive(Clone)]
pub struct Instance {
instance: WebAssembly::Instance,
module: Module,
/// The exports for an instance.
pub exports: Exports,
}
// #[cfg(test)]
// mod send_test {
// use super::*;
// fn is_send<T: Send>() -> bool {
// true
// }
// #[test]
// fn instance_is_send() {
// assert!(is_send::<Instance>());
// }
// }
/// An error while instantiating a module.
///
/// This is not a common WebAssembly error, however
/// we need to differentiate from a `LinkError` (an error
/// that happens while linking, on instantiation), a
/// Trap that occurs when calling the WebAssembly module
/// start function, and an error when initializing the user's
/// host environments.
#[derive(Error, Debug)]
pub enum InstantiationError {
/// A linking ocurred during instantiation.
#[cfg_attr(feature = "std", error("Link error: {0}"))]
Link(String),
/// A runtime error occured while invoking the start function
#[cfg_attr(feature = "std", error("Start error: {0}"))]
Start(String),
// /// Error occurred when initializing the host environment.
// #[error(transparent)]
// HostEnvInitialization(HostEnvInitError),
}
// impl From<wasmer_engine::InstantiationError> for InstantiationError {
// fn from(other: wasmer_engine::InstantiationError) -> Self {
// match other {
// wasmer_engine::InstantiationError::Link(e) => Self::Link(e),
// wasmer_engine::InstantiationError::Start(e) => Self::Start(e),
// }
// }
// }
// impl From<HostEnvInitError> for InstantiationError {
// fn from(other: HostEnvInitError) -> Self {
// Self::HostEnvInitialization(other)
// }
// }
impl Instance {
/// Creates a new `Instance` from a WebAssembly [`Module`] and a
/// set of imports resolved by the [`Resolver`].
///
/// The resolver can be anything that implements the [`Resolver`] trait,
/// so you can plug custom resolution for the imports, if you wish not
/// to use [`ImportObject`].
///
/// The [`ImportObject`] is the easiest way to provide imports to the instance.
///
/// [`ImportObject`]: crate::ImportObject
///
/// ```
/// # use wasmer::{imports, Store, Module, Global, Value, Instance};
/// # fn main() -> anyhow::Result<()> {
/// let store = Store::default();
/// let module = Module::new(&store, "(module)")?;
/// let imports = imports!{
/// "host" => {
/// "var" => Global::new(&store, Value::I32(2))
/// }
/// };
/// let instance = Instance::new(&module, &imports)?;
/// # Ok(())
/// # }
/// ```
///
/// ## Errors
///
/// The function can return [`InstantiationError`]s.
///
/// Those are, as defined by the spec:
/// * Link errors that happen when plugging the imports into the instance
/// * Runtime errors that happen when running the module `start` function.
pub fn new(module: &Module, resolver: &dyn NamedResolver) -> Result<Self, InstantiationError> {
let store = module.store();
let instance = module.instantiate(resolver).unwrap();
let instance_exports = instance.exports();
let exports = module
.exports()
.map(|export_type| {
let name = export_type.name();
let export = js_sys::Reflect::get(&instance_exports, &name.into()).unwrap();
let extern_ = Extern::from_vm_export(store, export.into());
(name.to_string(), extern_)
})
.collect::<Exports>();
Ok(Self {
module: module.clone(),
instance: instance,
exports,
})
}
/// Gets the [`Module`] associated with this instance.
pub fn module(&self) -> &Module {
&self.module
}
/// Returns the [`Store`] where the `Instance` belongs.
pub fn store(&self) -> &Store {
self.module.store()
}
}
impl fmt::Debug for Instance {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Instance")
.field("exports", &self.exports)
.finish()
}
}

114
lib/js-api/src/iterators.rs Normal file
View File

@@ -0,0 +1,114 @@
use wasmer_types::{
ExportType, ExternType, FunctionType, GlobalType, ImportType, MemoryType, TableType,
};
// Code inspired from
// https://www.reddit.com/r/rust/comments/9vspv4/extending_iterators_ergonomically/
/// This iterator allows us to iterate over the exports
/// and offer nice API ergonomics over it.
pub struct ExportsIterator<I: Iterator<Item = ExportType> + Sized> {
pub(crate) iter: I,
pub(crate) size: usize,
}
impl<I: Iterator<Item = ExportType> + Sized> ExactSizeIterator for ExportsIterator<I> {
// We can easily calculate the remaining number of iterations.
fn len(&self) -> usize {
self.size
}
}
impl<I: Iterator<Item = ExportType> + Sized> ExportsIterator<I> {
/// Get only the functions
pub fn functions(self) -> impl Iterator<Item = ExportType<FunctionType>> + Sized {
self.iter.filter_map(|extern_| match extern_.ty() {
ExternType::Function(ty) => Some(ExportType::new(extern_.name(), ty.clone())),
_ => None,
})
}
/// Get only the memories
pub fn memories(self) -> impl Iterator<Item = ExportType<MemoryType>> + Sized {
self.iter.filter_map(|extern_| match extern_.ty() {
ExternType::Memory(ty) => Some(ExportType::new(extern_.name(), *ty)),
_ => None,
})
}
/// Get only the tables
pub fn tables(self) -> impl Iterator<Item = ExportType<TableType>> + Sized {
self.iter.filter_map(|extern_| match extern_.ty() {
ExternType::Table(ty) => Some(ExportType::new(extern_.name(), *ty)),
_ => None,
})
}
/// Get only the globals
pub fn globals(self) -> impl Iterator<Item = ExportType<GlobalType>> + Sized {
self.iter.filter_map(|extern_| match extern_.ty() {
ExternType::Global(ty) => Some(ExportType::new(extern_.name(), *ty)),
_ => None,
})
}
}
impl<I: Iterator<Item = ExportType> + Sized> Iterator for ExportsIterator<I> {
type Item = ExportType;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
}
/// This iterator allows us to iterate over the imports
/// and offer nice API ergonomics over it.
pub struct ImportsIterator<I: Iterator<Item = ImportType> + Sized> {
pub(crate) iter: I,
pub(crate) size: usize,
}
impl<I: Iterator<Item = ImportType> + Sized> ExactSizeIterator for ImportsIterator<I> {
// We can easily calculate the remaining number of iterations.
fn len(&self) -> usize {
self.size
}
}
impl<I: Iterator<Item = ImportType> + Sized> ImportsIterator<I> {
/// Get only the functions
pub fn functions(self) -> impl Iterator<Item = ImportType<FunctionType>> + Sized {
self.iter.filter_map(|extern_| match extern_.ty() {
ExternType::Function(ty) => Some(ImportType::new(
extern_.module(),
extern_.name(),
ty.clone(),
)),
_ => None,
})
}
/// Get only the memories
pub fn memories(self) -> impl Iterator<Item = ImportType<MemoryType>> + Sized {
self.iter.filter_map(|extern_| match extern_.ty() {
ExternType::Memory(ty) => Some(ImportType::new(extern_.module(), extern_.name(), *ty)),
_ => None,
})
}
/// Get only the tables
pub fn tables(self) -> impl Iterator<Item = ImportType<TableType>> + Sized {
self.iter.filter_map(|extern_| match extern_.ty() {
ExternType::Table(ty) => Some(ImportType::new(extern_.module(), extern_.name(), *ty)),
_ => None,
})
}
/// Get only the globals
pub fn globals(self) -> impl Iterator<Item = ImportType<GlobalType>> + Sized {
self.iter.filter_map(|extern_| match extern_.ty() {
ExternType::Global(ty) => Some(ImportType::new(extern_.module(), extern_.name(), *ty)),
_ => None,
})
}
}
impl<I: Iterator<Item = ImportType> + Sized> Iterator for ImportsIterator<I> {
type Item = ImportType;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
}

333
lib/js-api/src/lib.rs Normal file
View File

@@ -0,0 +1,333 @@
#![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_js::{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/
#[cfg(all(feature = "std", feature = "core"))]
compile_error!(
"The `std` and `core` features are both enabled, which is an error. Please enable only once."
);
#[cfg(all(not(feature = "std"), not(feature = "core")))]
compile_error!("Both the `std` and `core` features are disabled. Please enable one of them.");
#[cfg(feature = "core")]
extern crate alloc;
mod lib {
#[cfg(feature = "core")]
pub mod std {
pub use alloc::{borrow, boxed, str, string, sync, vec};
pub use core::fmt;
pub use hashbrown as collections;
}
#[cfg(feature = "std")]
pub mod std {
pub use std::{borrow, boxed, collections, fmt, str, string, sync, vec};
}
}
// mod env;
mod error;
mod export;
mod exports;
mod externals;
mod resolver;
// mod externals;
mod import_object;
mod instance;
mod iterators;
mod module;
// mod native;
// mod ptr;
mod store;
mod types;
mod utils;
// /// Implement [`WasmerEnv`] for your type with `#[derive(WasmerEnv)]`.
// ///
// /// See the [`WasmerEnv`] trait for more information.
// pub use wasmer_derive::WasmerEnv;
// pub use crate::env::{HostEnvInitError, LazyInit, WasmerEnv};
// // pub use crate::exports::{ExportError, Exportable, Exports, ExportsIterator};
pub use crate::externals::{
Extern,
Memory, /* FromToNativeWasmType, Function, Global, HostFunction, 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::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;
// #[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,
};
#[cfg(feature = "wat")]
pub use wat::parse_bytes as wat2wasm;
/// Version number of this crate.
pub const VERSION: &str = env!("CARGO_PKG_VERSION");

438
lib/js-api/src/module.rs Normal file
View File

@@ -0,0 +1,438 @@
use crate::iterators::{ExportsIterator, ImportsIterator};
use crate::resolver::{NamedResolver, Resolver};
use crate::store::Store;
use crate::types::{ExportType, ImportType};
// use crate::InstantiationError;
use crate::error::CompileError;
#[cfg(feature = "wat")]
use crate::error::WasmError;
use js_sys::{Function, Object, Reflect, Uint8Array, WebAssembly};
use std::fmt;
use std::io;
use std::path::Path;
use std::sync::Arc;
use thiserror::Error;
use wasmer_types::{
ExternType, FunctionType, GlobalType, MemoryType, Mutability, Pages, TableType, Type,
};
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
#[derive(Error, Debug)]
pub enum IoCompileError {
/// An IO error
#[error(transparent)]
Io(#[from] io::Error),
/// A compilation error
#[error(transparent)]
Compile(#[from] CompileError),
}
/// A WebAssembly Module contains stateless WebAssembly
/// code that has already been compiled and can be instantiated
/// multiple times.
///
/// ## Cloning a module
///
/// Cloning a module is cheap: it does a shallow copy of the compiled
/// contents rather than a deep copy.
#[derive(Clone)]
pub struct Module {
store: Store,
module: WebAssembly::Module,
name: Option<String>,
}
impl Module {
/// Creates a new WebAssembly Module given the configuration
/// in the store.
///
/// If the provided bytes are not WebAssembly-like (start with `b"\0asm"`),
/// and the "wat" feature is enabled for this crate, this function will try to
/// to convert the bytes assuming they correspond to the WebAssembly text
/// format.
///
/// ## Security
///
/// Before the code is compiled, it will be validated using the store
/// features.
///
/// ## Errors
///
/// Creating a WebAssembly module from bytecode can result in a
/// [`CompileError`] since this operation requires to transorm the Wasm
/// bytecode into code the machine can easily execute.
///
/// ## Example
///
/// Reading from a WAT file.
///
/// ```
/// use wasmer::*;
/// # fn main() -> anyhow::Result<()> {
/// # let store = Store::default();
/// let wat = "(module)";
/// let module = Module::new(&store, wat)?;
/// # Ok(())
/// # }
/// ```
///
/// Reading from bytes:
///
/// ```
/// use wasmer::*;
/// # fn main() -> anyhow::Result<()> {
/// # let store = Store::default();
/// // The following is the same as:
/// // (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 bytes: Vec<u8> = vec![
/// 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x06, 0x01, 0x60,
/// 0x01, 0x7f, 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x07, 0x0b, 0x01, 0x07,
/// 0x61, 0x64, 0x64, 0x5f, 0x6f, 0x6e, 0x65, 0x00, 0x00, 0x0a, 0x09, 0x01,
/// 0x07, 0x00, 0x20, 0x00, 0x41, 0x01, 0x6a, 0x0b, 0x00, 0x1a, 0x04, 0x6e,
/// 0x61, 0x6d, 0x65, 0x01, 0x0a, 0x01, 0x00, 0x07, 0x61, 0x64, 0x64, 0x5f,
/// 0x6f, 0x6e, 0x65, 0x02, 0x07, 0x01, 0x00, 0x01, 0x00, 0x02, 0x70, 0x30,
/// ];
/// let module = Module::new(&store, bytes)?;
/// # Ok(())
/// # }
/// ```
#[allow(unreachable_code)]
pub fn new(store: &Store, bytes: impl AsRef<[u8]>) -> Result<Self, CompileError> {
#[cfg(feature = "wat")]
let bytes = wat::parse_bytes(bytes.as_ref()).map_err(|e| {
CompileError::Wasm(WasmError::Generic(format!(
"Error when converting wat: {}",
e
)))
})?;
Self::from_binary(store, bytes.as_ref())
}
/// Creates a new WebAssembly module from a file path.
pub fn from_file(store: &Store, file: impl AsRef<Path>) -> Result<Self, IoCompileError> {
unimplemented!();
}
/// Creates a new WebAssembly module from a binary.
///
/// Opposed to [`Module::new`], this function is not compatible with
/// the WebAssembly text format (if the "wat" feature is enabled for
/// this crate).
pub fn from_binary(store: &Store, binary: &[u8]) -> Result<Self, CompileError> {
//
// Self::validate(store, binary)?;
unsafe { Self::from_binary_unchecked(store, binary) }
}
/// Creates a new WebAssembly module skipping any kind of validation.
///
/// # Safety
///
/// This is safe since the JS vm should be safe already.
/// We maintain the `unsafe` to preserve the same API as Wasmer
pub unsafe fn from_binary_unchecked(
store: &Store,
binary: &[u8],
) -> Result<Self, CompileError> {
let js_bytes = unsafe { Uint8Array::view(binary) };
let module = WebAssembly::Module::new(&js_bytes.into()).unwrap();
Ok(Self {
store: store.clone(),
module,
name: None,
})
}
/// Validates a new WebAssembly Module given the configuration
/// in the Store.
///
/// This validation is normally pretty fast and checks the enabled
/// WebAssembly features in the Store Engine to assure deterministic
/// validation of the Module.
pub fn validate(store: &Store, binary: &[u8]) -> Result<(), CompileError> {
let js_bytes = unsafe { Uint8Array::view(binary) };
match WebAssembly::validate(&js_bytes.into()) {
Ok(true) => Ok(()),
_ => Err(CompileError::Validate("Invalid Wasm file".to_owned())),
}
}
fn compile(store: &Store, binary: &[u8]) -> Result<Self, CompileError> {
unimplemented!();
}
// fn from_artifact(store: &Store, artifact: Arc<dyn Artifact>) -> Self {
// unimplemented!();
// }
pub(crate) fn instantiate(
&self,
resolver: &dyn NamedResolver,
) -> Result<WebAssembly::Instance, ()> {
let imports = js_sys::Object::new();
for import_type in self.imports() {
let resolved_import =
resolver.resolve_by_name(import_type.module(), import_type.name());
if let Some(import) = resolved_import {
match js_sys::Reflect::get(&imports, &import_type.module().into()) {
Ok(val) => {
js_sys::Reflect::set(&val, &import_type.name().into(), import.as_jsvalue());
}
Err(_) => {
let import_namespace = js_sys::Object::new();
js_sys::Reflect::set(
&import_namespace,
&import_type.name().into(),
import.as_jsvalue(),
);
js_sys::Reflect::set(
&imports,
&import_type.module().into(),
&import_namespace.into(),
);
}
};
}
}
WebAssembly::Instance::new(&self.module, &imports).map_err(|_| ())
}
/// Returns the name of the current module.
///
/// This name is normally set in the WebAssembly bytecode by some
/// compilers, but can be also overwritten using the [`Module::set_name`] method.
///
/// # Example
///
/// ```
/// # use wasmer::*;
/// # fn main() -> anyhow::Result<()> {
/// # let store = Store::default();
/// let wat = "(module $moduleName)";
/// let module = Module::new(&store, wat)?;
/// assert_eq!(module.name(), Some("moduleName"));
/// # Ok(())
/// # }
/// ```
pub fn name(&self) -> Option<&str> {
self.name.as_ref().map(|s| s.as_ref())
// self.artifact.module_ref().name.as_deref()
}
/// Sets the name of the current module.
/// This is normally useful for stacktraces and debugging.
///
/// It will return `true` if the module name was changed successfully,
/// and return `false` otherwise (in case the module is already
/// instantiated).
///
/// # Example
///
/// ```
/// # use wasmer::*;
/// # fn main() -> anyhow::Result<()> {
/// # let store = Store::default();
/// let wat = "(module)";
/// let mut module = Module::new(&store, wat)?;
/// assert_eq!(module.name(), None);
/// module.set_name("foo");
/// assert_eq!(module.name(), Some("foo"));
/// # Ok(())
/// # }
/// ```
pub fn set_name(&mut self, name: &str) -> bool {
self.name = Some(name.to_string());
true
// match Reflect::set(self.module.as_ref(), &"wasmer_name".into(), &name.into()) {
// Ok(_) => true,
// _ => false
// }
// Arc::get_mut(&mut self.artifact)
// .and_then(|artifact| artifact.module_mut())
// .map(|mut module_info| {
// module_info.name = Some(name.to_string());
// true
// })
// .unwrap_or(false)
}
/// Returns an iterator over the imported types in the Module.
///
/// The order of the imports is guaranteed to be the same as in the
/// WebAssembly bytecode.
///
/// # Example
///
/// ```
/// # use wasmer::*;
/// # fn main() -> anyhow::Result<()> {
/// # let store = Store::default();
/// let wat = r#"(module
/// (import "host" "func1" (func))
/// (import "host" "func2" (func))
/// )"#;
/// let module = Module::new(&store, wat)?;
/// for import in module.imports() {
/// assert_eq!(import.module(), "host");
/// assert!(import.name().contains("func"));
/// import.ty();
/// }
/// # Ok(())
/// # }
/// ```
pub fn imports<'a>(&'a self) -> ImportsIterator<impl Iterator<Item = ImportType> + 'a> {
let imports = WebAssembly::Module::imports(&self.module);
let iter = imports
.iter()
.map(move |val| {
let module = Reflect::get(val.as_ref(), &"module".into())
.unwrap()
.as_string()
.unwrap();
let field = Reflect::get(val.as_ref(), &"name".into())
.unwrap()
.as_string()
.unwrap();
let kind = Reflect::get(val.as_ref(), &"kind".into())
.unwrap()
.as_string()
.unwrap();
let extern_type = match kind.as_str() {
"function" => {
let func_type = FunctionType::new(vec![], vec![]);
ExternType::Function(func_type)
}
"global" => {
let global_type = GlobalType::new(Type::I32, Mutability::Const);
ExternType::Global(global_type)
}
"memory" => {
let memory_type = MemoryType::new(Pages(1), None, false);
ExternType::Memory(memory_type)
}
"table" => {
let table_type = TableType::new(Type::FuncRef, 1, None);
ExternType::Table(table_type)
}
_ => unimplemented!(),
};
ImportType::new(&module, &field, extern_type)
})
.collect::<Vec<_>>()
.into_iter();
ImportsIterator {
iter,
size: imports.length() as usize,
}
}
// /// Get the custom sections of the module given a `name`.
// pub fn custom_sections<'a>(&'a self, name: &'a str) -> impl Iterator<Item = Arc<[u8]>> + 'a {
// self.custom_sections
// .iter()
// .filter_map(move |(section_name, section_index)| {
// if name != section_name {
// return None;
// }
// Some(self.custom_sections_data[*section_index].clone())
// })
// }
/// Returns an iterator over the exported types in the Module.
///
/// The order of the exports is guaranteed to be the same as in the
/// WebAssembly bytecode.
///
/// # Example
///
/// ```
/// # use wasmer::*;
/// # fn main() -> anyhow::Result<()> {
/// # let store = Store::default();
/// let wat = r#"(module
/// (func (export "namedfunc"))
/// (memory (export "namedmemory") 1)
/// )"#;
/// let module = Module::new(&store, wat)?;
/// for export_ in module.exports() {
/// assert!(export_.name().contains("named"));
/// export_.ty();
/// }
/// # Ok(())
/// # }
/// ```
pub fn exports<'a>(&'a self) -> ExportsIterator<impl Iterator<Item = ExportType> + 'a> {
let exports = WebAssembly::Module::exports(&self.module);
let iter = exports
.iter()
.map(move |val| {
let field = Reflect::get(val.as_ref(), &"name".into())
.unwrap()
.as_string()
.unwrap();
let kind = Reflect::get(val.as_ref(), &"kind".into())
.unwrap()
.as_string()
.unwrap();
let extern_type = match kind.as_str() {
"function" => {
let func_type = FunctionType::new(vec![], vec![]);
ExternType::Function(func_type)
}
"global" => {
let global_type = GlobalType::new(Type::I32, Mutability::Const);
ExternType::Global(global_type)
}
"memory" => {
let memory_type = MemoryType::new(Pages(1), None, false);
ExternType::Memory(memory_type)
}
"table" => {
let table_type = TableType::new(Type::FuncRef, 1, None);
ExternType::Table(table_type)
}
_ => unimplemented!(),
};
ExportType::new(&field, extern_type)
})
.collect::<Vec<_>>()
.into_iter();
ExportsIterator {
iter,
size: exports.length() as usize,
}
}
// /// Get the custom sections of the module given a `name`.
// ///
// /// # Important
// ///
// /// Following the WebAssembly spec, one name can have multiple
// /// custom sections. That's why an iterator (rather than one element)
// /// is returned.
// pub fn custom_sections<'a>(&'a self, name: &'a str) -> impl Iterator<Item = Arc<[u8]>> + 'a {
// unimplemented!();
// // self.artifact.module_ref().custom_sections(name)
// }
/// Returns the [`Store`] where the `Instance` belongs.
pub fn store(&self) -> &Store {
// unimplemented!();
&self.store
}
}
impl fmt::Debug for Module {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Module")
.field("name", &self.name())
.finish()
}
}

269
lib/js-api/src/native.rs Normal file
View File

@@ -0,0 +1,269 @@
//! Native Functions.
//!
//! This module creates the helper `NativeFunc` that let us call WebAssembly
//! functions with the native ABI, that is:
//!
//! ```ignore
//! let add_one = instance.exports.get_function("function_name")?;
//! let add_one_native: NativeFunc<i32, i32> = add_one.native().unwrap();
//! ```
use std::marker::PhantomData;
use crate::externals::function::{DynamicFunction, VMDynamicFunction};
use crate::{FromToNativeWasmType, Function, RuntimeError, Store, WasmTypeList};
use std::panic::{catch_unwind, AssertUnwindSafe};
use wasmer_engine::ExportFunction;
use wasmer_types::NativeWasmType;
use wasmer_vm::{VMDynamicFunctionContext, VMFunctionBody, VMFunctionEnvironment, VMFunctionKind};
/// A WebAssembly function that can be called natively
/// (using the Native ABI).
pub struct NativeFunc<Args = (), Rets = ()> {
store: Store,
exported: ExportFunction,
_phantom: PhantomData<(Args, Rets)>,
}
unsafe impl<Args, Rets> Send for NativeFunc<Args, Rets> {}
impl<Args, Rets> NativeFunc<Args, Rets>
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
pub(crate) fn new(store: Store, exported: ExportFunction) -> Self {
Self {
store,
exported,
_phantom: PhantomData,
}
}
pub(crate) fn is_host(&self) -> bool {
self.exported.vm_function.instance_ref.is_none()
}
pub(crate) fn vmctx(&self) -> VMFunctionEnvironment {
self.exported.vm_function.vmctx
}
pub(crate) fn address(&self) -> *const VMFunctionBody {
self.exported.vm_function.address
}
pub(crate) fn arg_kind(&self) -> VMFunctionKind {
self.exported.vm_function.kind
}
/// Get access to the backing VM value for this extern. This function is for
/// tests it should not be called by users of the Wasmer API.
///
/// # Safety
/// This function is unsafe to call outside of tests for the wasmer crate
/// because there is no stability guarantee for the returned type and we may
/// make breaking changes to it at any time or remove this method.
#[doc(hidden)]
pub unsafe fn get_vm_function(&self) -> &wasmer_vm::VMFunction {
&self.exported.vm_function
}
}
/*
impl<Args, Rets> From<&NativeFunc<Args, Rets>> for VMFunction
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
fn from(other: &NativeFunc<Args, Rets>) -> Self {
let signature = FunctionType::new(Args::wasm_types(), Rets::wasm_types());
Self {
address: other.address,
vmctx: other.vmctx,
signature,
kind: other.arg_kind,
call_trampoline: None,
instance_ref: None,
}
}
}*/
impl<Args: WasmTypeList, Rets: WasmTypeList> Clone for NativeFunc<Args, Rets> {
fn clone(&self) -> Self {
let mut exported = self.exported.clone();
exported.vm_function.upgrade_instance_ref().unwrap();
Self {
store: self.store.clone(),
exported,
_phantom: PhantomData,
}
}
}
impl<Args, Rets> From<&NativeFunc<Args, Rets>> for ExportFunction
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
fn from(other: &NativeFunc<Args, Rets>) -> Self {
other.exported.clone()
}
}
impl<Args, Rets> From<NativeFunc<Args, Rets>> for Function
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
fn from(other: NativeFunc<Args, Rets>) -> Self {
Self {
store: other.store,
exported: other.exported,
}
}
}
macro_rules! impl_native_traits {
( $( $x:ident ),* ) => {
#[allow(unused_parens, non_snake_case)]
impl<$( $x , )* Rets> NativeFunc<( $( $x ),* ), Rets>
where
$( $x: FromToNativeWasmType, )*
Rets: WasmTypeList,
{
/// Call the typed func and return results.
pub fn call(&self, $( $x: $x, )* ) -> Result<Rets, RuntimeError> {
if !self.is_host() {
// We assume the trampoline is always going to be present for
// Wasm functions
let trampoline = self.exported.vm_function.call_trampoline.expect("Call trampoline not found in wasm function");
// TODO: when `const fn` related features mature more, we can declare a single array
// of the correct size here.
let mut params_list = [ $( $x.to_native().to_binary() ),* ];
let mut rets_list_array = Rets::empty_array();
let rets_list = rets_list_array.as_mut();
let using_rets_array;
let args_rets: &mut [i128] = if params_list.len() > rets_list.len() {
using_rets_array = false;
params_list.as_mut()
} else {
using_rets_array = true;
for (i, &arg) in params_list.iter().enumerate() {
rets_list[i] = arg;
}
rets_list.as_mut()
};
unsafe {
wasmer_vm::wasmer_call_trampoline(
&self.store,
self.vmctx(),
trampoline,
self.address(),
args_rets.as_mut_ptr() as *mut u8,
)
}?;
let num_rets = rets_list.len();
if !using_rets_array && num_rets > 0 {
let src_pointer = params_list.as_ptr();
let rets_list = &mut rets_list_array.as_mut()[0] as *mut i128;
unsafe {
// TODO: we can probably remove this copy by doing some clever `transmute`s.
// we know it's not overlapping because `using_rets_array` is false
std::ptr::copy_nonoverlapping(src_pointer,
rets_list,
num_rets);
}
}
Ok(Rets::from_array(rets_list_array))
// TODO: When the Host ABI and Wasm ABI are the same, we could do this instead:
// but we can't currently detect whether that's safe.
//
// let results = unsafe {
// wasmer_vm::catch_traps_with_result(self.vmctx, || {
// let f = std::mem::transmute::<_, unsafe extern "C" fn( *mut VMContext, $( $x, )*) -> Rets::CStruct>(self.address());
// // We always pass the vmctx
// f( self.vmctx, $( $x, )* )
// }).map_err(RuntimeError::from_trap)?
// };
// Ok(Rets::from_c_struct(results))
}
else {
match self.arg_kind() {
VMFunctionKind::Static => {
let results = catch_unwind(AssertUnwindSafe(|| unsafe {
let f = std::mem::transmute::<_, unsafe extern "C" fn( VMFunctionEnvironment, $( $x, )*) -> Rets::CStruct>(self.address());
// We always pass the vmctx
f( self.vmctx(), $( $x, )* )
})).map_err(|e| RuntimeError::new(format!("{:?}", e)))?;
Ok(Rets::from_c_struct(results))
},
VMFunctionKind::Dynamic => {
let params_list = [ $( $x.to_native().to_value() ),* ];
let results = {
type VMContextWithEnv = VMDynamicFunctionContext<DynamicFunction<std::ffi::c_void>>;
unsafe {
let ctx = self.vmctx().host_env as *mut VMContextWithEnv;
(*ctx).ctx.call(&params_list)?
}
};
let mut rets_list_array = Rets::empty_array();
let mut_rets = rets_list_array.as_mut() as *mut [i128] as *mut i128;
for (i, ret) in results.iter().enumerate() {
unsafe {
ret.write_value_to(mut_rets.add(i));
}
}
Ok(Rets::from_array(rets_list_array))
}
}
}
}
}
#[allow(unused_parens)]
impl<'a, $( $x, )* Rets> crate::exports::ExportableWithGenerics<'a, ($( $x ),*), Rets> for NativeFunc<( $( $x ),* ), Rets>
where
$( $x: FromToNativeWasmType, )*
Rets: WasmTypeList,
{
fn get_self_from_extern_with_generics(_extern: &crate::externals::Extern) -> Result<Self, crate::exports::ExportError> {
use crate::exports::Exportable;
crate::Function::get_self_from_extern(_extern)?.native().map_err(|_| crate::exports::ExportError::IncompatibleType)
}
fn into_weak_instance_ref(&mut self) {
self.exported.vm_function.instance_ref.as_mut().map(|v| *v = v.downgrade());
}
}
};
}
impl_native_traits!();
impl_native_traits!(A1);
impl_native_traits!(A1, A2);
impl_native_traits!(A1, A2, A3);
impl_native_traits!(A1, A2, A3, A4);
impl_native_traits!(A1, A2, A3, A4, A5);
impl_native_traits!(A1, A2, A3, A4, A5, A6);
impl_native_traits!(A1, A2, A3, A4, A5, A6, A7);
impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8);
impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9);
impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10);
impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11);
impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12);
impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13);
impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14);
impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15);
impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16);
impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17);
impl_native_traits!(
A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18
);
impl_native_traits!(
A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19
);
impl_native_traits!(
A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20
);

426
lib/js-api/src/ptr.rs Normal file
View File

@@ -0,0 +1,426 @@
//! Types for a reusable pointer abstraction for accessing Wasm linear memory.
//!
//! This abstraction is safe: it ensures the memory is in bounds and that the pointer
//! is aligned (avoiding undefined behavior).
//!
//! Therefore, you should use this abstraction whenever possible to avoid memory
//! related bugs when implementing an ABI.
use crate::{externals::Memory, FromToNativeWasmType};
use std::{cell::Cell, fmt, marker::PhantomData, mem};
use wasmer_types::ValueType;
/// The `Array` marker type. This type can be used like `WasmPtr<T, Array>`
/// to get access to methods
pub struct Array;
/// The `Item` marker type. This is the default and does not usually need to be
/// specified.
pub struct Item;
/// A zero-cost type that represents a pointer to something in Wasm linear
/// memory.
///
/// This type can be used directly in the host function arguments:
/// ```
/// # use wasmer::Memory;
/// # use wasmer::WasmPtr;
/// pub fn host_import(memory: Memory, ptr: WasmPtr<u32>) {
/// let derefed_ptr = ptr.deref(&memory).expect("pointer in bounds");
/// let inner_val: u32 = derefed_ptr.get();
/// println!("Got {} from Wasm memory address 0x{:X}", inner_val, ptr.offset());
/// // update the value being pointed to
/// derefed_ptr.set(inner_val + 1);
/// }
/// ```
///
/// This type can also be used with primitive-filled structs, but be careful of
/// guarantees required by `ValueType`.
/// ```
/// # use wasmer::Memory;
/// # use wasmer::WasmPtr;
/// # use wasmer::ValueType;
///
/// #[derive(Copy, Clone, Debug)]
/// #[repr(C)]
/// struct V3 {
/// x: f32,
/// y: f32,
/// z: f32
/// }
/// // This is safe as the 12 bytes represented by this struct
/// // are valid for all bit combinations.
/// unsafe impl ValueType for V3 {
/// }
///
/// fn update_vector_3(memory: Memory, ptr: WasmPtr<V3>) {
/// let derefed_ptr = ptr.deref(&memory).expect("pointer in bounds");
/// let mut inner_val: V3 = derefed_ptr.get();
/// println!("Got {:?} from Wasm memory address 0x{:X}", inner_val, ptr.offset());
/// // update the value being pointed to
/// inner_val.x = 10.4;
/// derefed_ptr.set(inner_val);
/// }
/// ```
#[repr(transparent)]
pub struct WasmPtr<T: Copy, Ty = Item> {
offset: u32,
_phantom: PhantomData<(T, Ty)>,
}
/// Methods relevant to all types of `WasmPtr`.
impl<T: Copy, Ty> WasmPtr<T, Ty> {
/// Create a new `WasmPtr` at the given offset.
#[inline]
pub fn new(offset: u32) -> Self {
Self {
offset,
_phantom: PhantomData,
}
}
/// Get the offset into Wasm linear memory for this `WasmPtr`.
#[inline]
pub fn offset(self) -> u32 {
self.offset
}
}
#[inline(always)]
fn align_pointer(ptr: usize, align: usize) -> usize {
// clears bits below aligment amount (assumes power of 2) to align pointer
debug_assert!(align.count_ones() == 1);
ptr & !(align - 1)
}
/// Methods for `WasmPtr`s to data that can be dereferenced, namely to types
/// that implement [`ValueType`], meaning that they're valid for all possible
/// bit patterns.
impl<T: Copy + ValueType> WasmPtr<T, Item> {
/// Dereference the `WasmPtr` getting access to a `&Cell<T>` allowing for
/// reading and mutating of the inner value.
///
/// This method is unsound if used with unsynchronized shared memory.
/// If you're unsure what that means, it likely does not apply to you.
/// This invariant will be enforced in the future.
#[inline]
pub fn deref<'a>(self, memory: &'a Memory) -> Option<&'a Cell<T>> {
if (self.offset as usize) + mem::size_of::<T>() > memory.size().bytes().0
|| mem::size_of::<T>() == 0
{
return None;
}
unsafe {
let cell_ptr = align_pointer(
memory.view::<u8>().as_ptr().add(self.offset as usize) as usize,
mem::align_of::<T>(),
) as *const Cell<T>;
Some(&*cell_ptr)
}
}
/// Mutably dereference this `WasmPtr` getting a `&mut Cell<T>` allowing for
/// direct access to a `&mut T`.
///
/// # Safety
/// - This method does not do any aliasing checks: it's possible to create
/// `&mut T` that point to the same memory. You should ensure that you have
/// exclusive access to Wasm linear memory before calling this method.
#[inline]
pub unsafe fn deref_mut<'a>(self, memory: &'a Memory) -> Option<&'a mut Cell<T>> {
if (self.offset as usize) + mem::size_of::<T>() > memory.size().bytes().0
|| mem::size_of::<T>() == 0
{
return None;
}
let cell_ptr = align_pointer(
memory.view::<u8>().as_ptr().add(self.offset as usize) as usize,
mem::align_of::<T>(),
) as *mut Cell<T>;
Some(&mut *cell_ptr)
}
}
/// Methods for `WasmPtr`s to arrays of data that can be dereferenced, namely to
/// types that implement [`ValueType`], meaning that they're valid for all
/// possible bit patterns.
impl<T: Copy + ValueType> WasmPtr<T, Array> {
/// Dereference the `WasmPtr` getting access to a `&[Cell<T>]` allowing for
/// reading and mutating of the inner values.
///
/// This method is unsound if used with unsynchronized shared memory.
/// If you're unsure what that means, it likely does not apply to you.
/// This invariant will be enforced in the future.
#[inline]
pub fn deref(self, memory: &Memory, index: u32, length: u32) -> Option<&[Cell<T>]> {
// gets the size of the item in the array with padding added such that
// for any index, we will always result an aligned memory access
let item_size = mem::size_of::<T>();
let slice_full_len = index as usize + length as usize;
let memory_size = memory.size().bytes().0;
if (self.offset as usize) + (item_size * slice_full_len) > memory_size
|| self.offset as usize >= memory_size
|| mem::size_of::<T>() == 0
{
return None;
}
unsafe {
let cell_ptr = align_pointer(
memory.view::<u8>().as_ptr().add(self.offset as usize) as usize,
mem::align_of::<T>(),
) as *const Cell<T>;
let cell_ptrs = &std::slice::from_raw_parts(cell_ptr, slice_full_len)
[index as usize..slice_full_len];
Some(cell_ptrs)
}
}
/// Mutably dereference this `WasmPtr` getting a `&mut [Cell<T>]` allowing for
/// direct access to a `&mut [T]`.
///
/// # Safety
/// - This method does not do any aliasing checks: it's possible to create
/// `&mut T` that point to the same memory. You should ensure that you have
/// exclusive access to Wasm linear memory before calling this method.
#[inline]
pub unsafe fn deref_mut(
self,
memory: &Memory,
index: u32,
length: u32,
) -> Option<&mut [Cell<T>]> {
// gets the size of the item in the array with padding added such that
// for any index, we will always result an aligned memory access
let item_size = mem::size_of::<T>();
let slice_full_len = index as usize + length as usize;
let memory_size = memory.size().bytes().0;
if (self.offset as usize) + (item_size * slice_full_len) > memory.size().bytes().0
|| self.offset as usize >= memory_size
|| mem::size_of::<T>() == 0
{
return None;
}
let cell_ptr = align_pointer(
memory.view::<u8>().as_ptr().add(self.offset as usize) as usize,
mem::align_of::<T>(),
) as *mut Cell<T>;
let cell_ptrs = &mut std::slice::from_raw_parts_mut(cell_ptr, slice_full_len)
[index as usize..slice_full_len];
Some(cell_ptrs)
}
/// Get a UTF-8 string from the `WasmPtr` with the given length.
///
/// Note that . The
/// underlying data can be mutated if the Wasm is allowed to execute or
/// an aliasing `WasmPtr` is used to mutate memory.
///
/// # Safety
/// This method returns a reference to Wasm linear memory. The underlying
/// data can be mutated if the Wasm is allowed to execute or an aliasing
/// `WasmPtr` is used to mutate memory.
///
/// `str` has invariants that must not be broken by mutating Wasm memory.
/// Thus the caller must ensure that the backing memory is not modified
/// while the reference is held.
///
/// Additionally, if `memory` is dynamic, the caller must also ensure that `memory`
/// is not grown while the reference is held.
pub unsafe fn get_utf8_str<'a>(self, memory: &'a Memory, str_len: u32) -> Option<&'a str> {
let memory_size = memory.size().bytes().0;
if self.offset as usize + str_len as usize > memory.size().bytes().0
|| self.offset as usize >= memory_size
{
return None;
}
let ptr = memory.view::<u8>().as_ptr().add(self.offset as usize) as *const u8;
let slice: &[u8] = std::slice::from_raw_parts(ptr, str_len as usize);
std::str::from_utf8(slice).ok()
}
/// Get a UTF-8 `String` from the `WasmPtr` with the given length.
///
/// an aliasing `WasmPtr` is used to mutate memory.
pub fn get_utf8_string(self, memory: &Memory, str_len: u32) -> Option<String> {
let memory_size = memory.size().bytes().0;
if self.offset as usize + str_len as usize > memory.size().bytes().0
|| self.offset as usize >= memory_size
{
return None;
}
// TODO: benchmark the internals of this function: there is likely room for
// micro-optimization here and this may be a fairly common function in user code.
let view = memory.view::<u8>();
let mut vec: Vec<u8> = Vec::with_capacity(str_len as usize);
let base = self.offset as usize;
for i in 0..(str_len as usize) {
let byte = view[base + i].get();
vec.push(byte);
}
String::from_utf8(vec).ok()
}
/// Get a UTF-8 string from the `WasmPtr`, where the string is nul-terminated.
///
/// Note that this does not account for UTF-8 strings that _contain_ nul themselves,
/// [`WasmPtr::get_utf8_str`] has to be used for those.
///
/// # Safety
/// This method behaves similarly to [`WasmPtr::get_utf8_str`], all safety invariants on
/// that method must also be upheld here.
pub unsafe fn get_utf8_str_with_nul<'a>(self, memory: &'a Memory) -> Option<&'a str> {
memory.view::<u8>()[(self.offset as usize)..]
.iter()
.map(|cell| cell.get())
.position(|byte| byte == 0)
.and_then(|length| self.get_utf8_str(memory, length as u32))
}
/// Get a UTF-8 `String` from the `WasmPtr`, where the string is nul-terminated.
///
/// Note that this does not account for UTF-8 strings that _contain_ nul themselves,
/// [`WasmPtr::get_utf8_string`] has to be used for those.
pub fn get_utf8_string_with_nul(self, memory: &Memory) -> Option<String> {
unsafe { self.get_utf8_str_with_nul(memory) }.map(|s| s.to_owned())
}
}
unsafe impl<T: Copy, Ty> FromToNativeWasmType for WasmPtr<T, Ty> {
type Native = i32;
fn to_native(self) -> Self::Native {
self.offset as i32
}
fn from_native(n: Self::Native) -> Self {
Self {
offset: n as u32,
_phantom: PhantomData,
}
}
}
unsafe impl<T: Copy, Ty> ValueType for WasmPtr<T, Ty> {}
impl<T: Copy, Ty> Clone for WasmPtr<T, Ty> {
fn clone(&self) -> Self {
Self {
offset: self.offset,
_phantom: PhantomData,
}
}
}
impl<T: Copy, Ty> Copy for WasmPtr<T, Ty> {}
impl<T: Copy, Ty> PartialEq for WasmPtr<T, Ty> {
fn eq(&self, other: &Self) -> bool {
self.offset == other.offset
}
}
impl<T: Copy, Ty> Eq for WasmPtr<T, Ty> {}
impl<T: Copy, Ty> fmt::Debug for WasmPtr<T, Ty> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "WasmPtr({:#x})", self.offset)
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::{Memory, MemoryType, Store};
/// Ensure that memory accesses work on the edges of memory and that out of
/// bounds errors are caught with both `deref` and `deref_mut`.
#[test]
fn wasm_ptr_memory_bounds_checks_hold() {
// create a memory
let store = Store::default();
let memory_descriptor = MemoryType::new(1, Some(1), false);
let memory = Memory::new(&store, memory_descriptor).unwrap();
// test that basic access works and that len = 0 works, but oob does not
let start_wasm_ptr: WasmPtr<u8> = WasmPtr::new(0);
let start_wasm_ptr_array: WasmPtr<u8, Array> = WasmPtr::new(0);
assert!(start_wasm_ptr.deref(&memory).is_some());
assert!(unsafe { start_wasm_ptr.deref_mut(&memory).is_some() });
assert!(start_wasm_ptr_array.deref(&memory, 0, 0).is_some());
assert!(unsafe { start_wasm_ptr_array.get_utf8_str(&memory, 0).is_some() });
assert!(start_wasm_ptr_array.get_utf8_string(&memory, 0).is_some());
assert!(unsafe { start_wasm_ptr_array.deref_mut(&memory, 0, 0).is_some() });
assert!(start_wasm_ptr_array.deref(&memory, 0, 1).is_some());
assert!(unsafe { start_wasm_ptr_array.deref_mut(&memory, 0, 1).is_some() });
// test that accessing the last valid memory address works correctly and OOB is caught
let last_valid_address_for_u8 = (memory.size().bytes().0 - 1) as u32;
let end_wasm_ptr: WasmPtr<u8> = WasmPtr::new(last_valid_address_for_u8);
assert!(end_wasm_ptr.deref(&memory).is_some());
assert!(unsafe { end_wasm_ptr.deref_mut(&memory).is_some() });
let end_wasm_ptr_array: WasmPtr<u8, Array> = WasmPtr::new(last_valid_address_for_u8);
assert!(end_wasm_ptr_array.deref(&memory, 0, 1).is_some());
assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, 0, 1).is_some() });
let invalid_idx_len_combos: [(u32, u32); 3] =
[(last_valid_address_for_u8 + 1, 0), (0, 2), (1, 1)];
for &(idx, len) in invalid_idx_len_combos.iter() {
assert!(end_wasm_ptr_array.deref(&memory, idx, len).is_none());
assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, idx, len).is_none() });
}
assert!(unsafe { end_wasm_ptr_array.get_utf8_str(&memory, 2).is_none() });
assert!(end_wasm_ptr_array.get_utf8_string(&memory, 2).is_none());
// test that accesing the last valid memory address for a u32 is valid
// (same as above test but with more edge cases to assert on)
let last_valid_address_for_u32 = (memory.size().bytes().0 - 4) as u32;
let end_wasm_ptr: WasmPtr<u32> = WasmPtr::new(last_valid_address_for_u32);
assert!(end_wasm_ptr.deref(&memory).is_some());
assert!(unsafe { end_wasm_ptr.deref_mut(&memory).is_some() });
assert!(end_wasm_ptr.deref(&memory).is_some());
assert!(unsafe { end_wasm_ptr.deref_mut(&memory).is_some() });
let end_wasm_ptr_oob_array: [WasmPtr<u32>; 4] = [
WasmPtr::new(last_valid_address_for_u32 + 1),
WasmPtr::new(last_valid_address_for_u32 + 2),
WasmPtr::new(last_valid_address_for_u32 + 3),
WasmPtr::new(last_valid_address_for_u32 + 4),
];
for oob_end_ptr in end_wasm_ptr_oob_array.iter() {
assert!(oob_end_ptr.deref(&memory).is_none());
assert!(unsafe { oob_end_ptr.deref_mut(&memory).is_none() });
}
let end_wasm_ptr_array: WasmPtr<u32, Array> = WasmPtr::new(last_valid_address_for_u32);
assert!(end_wasm_ptr_array.deref(&memory, 0, 1).is_some());
assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, 0, 1).is_some() });
let invalid_idx_len_combos: [(u32, u32); 3] =
[(last_valid_address_for_u32 + 1, 0), (0, 2), (1, 1)];
for &(idx, len) in invalid_idx_len_combos.iter() {
assert!(end_wasm_ptr_array.deref(&memory, idx, len).is_none());
assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, idx, len).is_none() });
}
let end_wasm_ptr_array_oob_array: [WasmPtr<u32, Array>; 4] = [
WasmPtr::new(last_valid_address_for_u32 + 1),
WasmPtr::new(last_valid_address_for_u32 + 2),
WasmPtr::new(last_valid_address_for_u32 + 3),
WasmPtr::new(last_valid_address_for_u32 + 4),
];
for oob_end_array_ptr in end_wasm_ptr_array_oob_array.iter() {
assert!(oob_end_array_ptr.deref(&memory, 0, 1).is_none());
assert!(unsafe { oob_end_array_ptr.deref_mut(&memory, 0, 1).is_none() });
assert!(oob_end_array_ptr.deref(&memory, 1, 0).is_none());
assert!(unsafe { oob_end_array_ptr.deref_mut(&memory, 1, 0).is_none() });
}
}
}

View File

@@ -0,0 +1,77 @@
use crate::export::Export;
use wasmer_types::entity::{BoxedSlice, EntityRef, PrimaryMap};
use wasmer_types::{ExternType, FunctionIndex, ImportIndex, MemoryIndex, TableIndex};
/// Import resolver connects imports with available exported values.
pub trait Resolver: Sized {
/// Resolves an import a WebAssembly module to an export it's hooked up to.
///
/// The `index` provided is the index of the import in the wasm module
/// that's being resolved. For example 1 means that it's the second import
/// listed in the wasm module.
///
/// The `module` and `field` arguments provided are the module/field names
/// listed on the import itself.
///
/// # Notes:
///
/// The index is useful because some WebAssembly modules may rely on that
/// for resolving ambiguity in their imports. Such as:
/// ```ignore
/// (module
/// (import "" "" (func))
/// (import "" "" (func (param i32) (result i32)))
/// )
/// ```
fn resolve(&self, _index: u32, module: &str, field: &str) -> Option<Export>;
}
/// Import resolver connects imports with available exported values.
///
/// This is a specific subtrait for [`Resolver`] for those users who don't
/// care about the `index`, but only about the `module` and `field` for
/// the resolution.
pub trait NamedResolver {
/// Resolves an import a WebAssembly module to an export it's hooked up to.
///
/// It receives the `module` and `field` names and return the [`Export`] in
/// case it's found.
fn resolve_by_name(&self, module: &str, field: &str) -> Option<Export>;
}
// All NamedResolvers should extend `Resolver`.
impl<T: NamedResolver> Resolver for T {
/// By default this method will be calling [`NamedResolver::resolve_by_name`],
/// dismissing the provided `index`.
fn resolve(&self, _index: u32, module: &str, field: &str) -> Option<Export> {
self.resolve_by_name(module, field)
}
}
impl<T: NamedResolver> NamedResolver for &T {
fn resolve_by_name(&self, module: &str, field: &str) -> Option<Export> {
(**self).resolve_by_name(module, field)
}
}
impl NamedResolver for Box<dyn NamedResolver> {
fn resolve_by_name(&self, module: &str, field: &str) -> Option<Export> {
(**self).resolve_by_name(module, field)
}
}
impl NamedResolver for () {
/// Always returns `None`.
fn resolve_by_name(&self, _module: &str, _field: &str) -> Option<Export> {
None
}
}
/// `Resolver` implementation that always resolves to `None`. Equivalent to `()`.
pub struct NullResolver {}
impl Resolver for NullResolver {
fn resolve(&self, _idx: u32, _module: &str, _field: &str) -> Option<Export> {
None
}
}

60
lib/js-api/src/store.rs Normal file
View File

@@ -0,0 +1,60 @@
use std::any::Any;
use std::fmt;
/// The store represents all global state that can be manipulated by
/// WebAssembly programs. It consists of the runtime representation
/// of all instances of functions, tables, memories, and globals that
/// have been allocated during the lifetime of the abstract machine.
///
/// The `Store` holds the engine (that is —amongst many things— used to compile
/// the Wasm bytes into a valid module artifact), in addition to the
/// [`Tunables`] (that are used to create the memories, tables and globals).
///
/// Spec: <https://webassembly.github.io/spec/core/exec/runtime.html#store>
#[derive(Clone)]
pub struct Store {}
impl Store {
/// Creates a new `Store`.
pub fn new() -> Self {
Self {}
}
/// Checks whether two stores are identical. A store is considered
/// equal to another store if both have the same engine. The
/// tunables are excluded from the logic.
pub fn same(a: &Self, b: &Self) -> bool {
true
}
}
impl PartialEq for Store {
fn eq(&self, other: &Self) -> bool {
Self::same(self, other)
}
}
// This is required to be able to set the trap_handler in the
// Store.
unsafe impl Send for Store {}
unsafe impl Sync for Store {}
impl Default for Store {
fn default() -> Self {
Self::new()
}
}
impl fmt::Debug for Store {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Store").finish()
}
}
/// A trait represinting any object that lives in the `Store`.
pub trait StoreObject {
/// Return true if the object `Store` is the same as the provided `Store`.
fn comes_from_same_store(&self, store: &Store) -> bool {
true
}
}

207
lib/js-api/src/trap.rs Normal file
View File

@@ -0,0 +1,207 @@
use super::frame_info::{FrameInfo, GlobalFrameInfo, FRAME_INFO};
use backtrace::Backtrace;
use std::error::Error;
use std::fmt;
use std::sync::Arc;
use wasm_bindgen::Exception;
/// A struct representing an aborted instruction execution, with a message
/// indicating the cause.
#[derive(Clone)]
pub struct RuntimeError {
inner: Arc<RuntimeErrorInner>,
}
/// The source of the `RuntimeError`.
#[derive(Debug)]
enum RuntimeErrorSource {
Generic(String),
User(Box<dyn Error + Send + Sync>),
}
impl fmt::Display for RuntimeErrorSource {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Generic(s) => write!(f, "{}", s),
Self::User(s) => write!(f, "{}", s),
Self::OOM => write!(f, "Wasmer VM out of memory"),
Self::Trap(s) => write!(f, "{}", s.message()),
}
}
}
struct RuntimeErrorInner {
/// The source error (this can be a custom user `Error` or a [`TrapCode`])
source: RuntimeErrorSource,
}
fn _assert_trap_is_sync_and_send(t: &Trap) -> (&dyn Sync, &dyn Send) {
(t, t)
}
impl RuntimeError {
/// Creates a new generic `RuntimeError` with the given `message`.
///
/// # Example
/// ```
/// let trap = wasmer_engine::RuntimeError::new("unexpected error");
/// assert_eq!("unexpected error", trap.message());
/// ```
pub fn new<I: Into<String>>(message: I) -> Self {
let info = FRAME_INFO.read().unwrap();
let msg = message.into();
Self::new_with_trace(
&info,
None,
RuntimeErrorSource::Generic(msg),
Backtrace::new_unresolved(),
)
}
/// Raises a custom user Error
pub fn raise(error: Box<dyn Error + Send + Sync>) -> ! {
wasm_bindgen::throw_val()
}
fn new_with_trace(
info: &GlobalFrameInfo,
trap_pc: Option<usize>,
source: RuntimeErrorSource,
native_trace: Backtrace,
) -> Self {
let frames: Vec<usize> = native_trace
.frames()
.iter()
.filter_map(|frame| {
let pc = frame.ip() as usize;
if pc == 0 {
None
} else {
// Note that we need to be careful about the pc we pass in here to
// lookup frame information. This program counter is used to
// translate back to an original source location in the origin wasm
// module. If this pc is the exact pc that the trap happened at,
// then we look up that pc precisely. Otherwise backtrace
// information typically points at the pc *after* the call
// instruction (because otherwise it's likely a call instruction on
// the stack). In that case we want to lookup information for the
// previous instruction (the call instruction) so we subtract one as
// the lookup.
let pc_to_lookup = if Some(pc) == trap_pc { pc } else { pc - 1 };
Some(pc_to_lookup)
}
})
.collect();
// Let's construct the trace
let wasm_trace = frames
.into_iter()
.filter_map(|pc| info.lookup_frame_info(pc))
.collect::<Vec<_>>();
Self {
inner: Arc::new(RuntimeErrorInner {
source,
wasm_trace,
native_trace,
}),
}
}
/// Returns a reference the `message` stored in `Trap`.
pub fn message(&self) -> String {
format!("{}", self.inner.source)
}
/// Returns a list of function frames in WebAssembly code that led to this
/// trap happening.
pub fn trace(&self) -> &[FrameInfo] {
&self.inner.wasm_trace
}
/// Attempts to downcast the `RuntimeError` to a concrete type.
pub fn downcast<T: Error + 'static>(self) -> Result<T, Self> {
match self.inner.source {
// We only try to downcast user errors
RuntimeErrorSource::User(err)) if err.is::<T>() => Ok(*err.downcast::<T>().unwrap()),
Ok(inner) => Err(Self {
inner: Arc::new(inner),
}),
Err(inner) => Err(Self { inner }),
}
}
/// Returns trap code, if it's a Trap
pub fn to_trap(self) -> Option<TrapCode> {
if let RuntimeErrorSource::Trap(trap_code) = self.inner.source {
Some(trap_code)
} else {
None
}
}
/// Returns true if the `RuntimeError` is the same as T
pub fn is<T: Error + 'static>(&self) -> bool {
match &self.inner.source {
RuntimeErrorSource::User(err) => err.is::<T>(),
_ => false,
}
}
}
impl fmt::Debug for RuntimeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RuntimeError")
.field("source", &self.inner.source)
.field("wasm_trace", &self.inner.wasm_trace)
.field("native_trace", &self.inner.native_trace)
.finish()
}
}
impl fmt::Display for RuntimeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "RuntimeError: {}", self.message())?;
let trace = self.trace();
if trace.is_empty() {
return Ok(());
}
for frame in self.trace().iter() {
let name = frame.module_name();
let func_index = frame.func_index();
writeln!(f)?;
write!(f, " at ")?;
match frame.function_name() {
Some(name) => match rustc_demangle::try_demangle(name) {
Ok(name) => write!(f, "{}", name)?,
Err(_) => write!(f, "{}", name)?,
},
None => write!(f, "<unnamed>")?,
}
write!(
f,
" ({}[{}]:0x{:x})",
name,
func_index,
frame.module_offset()
)?;
}
Ok(())
}
}
impl std::error::Error for RuntimeError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match &self.inner.source {
RuntimeErrorSource::User(err) => Some(&**err),
RuntimeErrorSource::Trap(err) => Some(err),
_ => None,
}
}
}
impl From<Trap> for RuntimeError {
fn from(trap: Trap) -> Self {
Self::from_trap(trap)
}
}

115
lib/js-api/src/types.rs Normal file
View File

@@ -0,0 +1,115 @@
// use crate::externals::Function;
// use crate::store::{Store, StoreObject};
// use crate::RuntimeError;
// use wasmer_types::Value;
pub use wasmer_types::{
ExportType, ExternType, FunctionType, GlobalType, ImportType, MemoryType, Mutability,
TableType, Type as ValType,
};
// use wasmer_vm::VMFuncRef;
/// WebAssembly computations manipulate values of basic value types:
/// * Integers (32 or 64 bit width)
/// * Floating-point (32 or 64 bit width)
/// * Vectors (128 bits, with 32 or 64 bit lanes)
///
/// Spec: <https://webassembly.github.io/spec/core/exec/runtime.html#values>
pub type Val = ();
// pub type Val = Value<Function>;
// impl StoreObject for Val {
// fn comes_from_same_store(&self, store: &Store) -> bool {
// match self {
// Self::FuncRef(None) => true,
// Self::FuncRef(Some(f)) => Store::same(store, f.store()),
// // `ExternRef`s are not tied to specific stores
// Self::ExternRef(_) => true,
// Self::I32(_) | Self::I64(_) | Self::F32(_) | Self::F64(_) | Self::V128(_) => true,
// }
// }
// }
// impl From<Function> for Val {
// fn from(val: Function) -> Self {
// Self::FuncRef(Some(val))
// }
// }
// /// It provides useful functions for converting back and forth
// /// from [`Val`] into `FuncRef`.
// pub trait ValFuncRef {
// fn into_vm_funcref(&self, store: &Store) -> Result<VMFuncRef, RuntimeError>;
// fn from_vm_funcref(item: VMFuncRef, store: &Store) -> Self;
// fn into_table_reference(&self, store: &Store) -> Result<wasmer_vm::TableElement, RuntimeError>;
// fn from_table_reference(item: wasmer_vm::TableElement, store: &Store) -> Self;
// }
// impl ValFuncRef for Val {
// fn into_vm_funcref(&self, store: &Store) -> Result<VMFuncRef, RuntimeError> {
// if !self.comes_from_same_store(store) {
// return Err(RuntimeError::new("cross-`Store` values are not supported"));
// }
// Ok(match self {
// Self::FuncRef(None) => VMFuncRef::null(),
// Self::FuncRef(Some(f)) => f.vm_funcref(),
// _ => return Err(RuntimeError::new("val is not func ref")),
// })
// }
// fn from_vm_funcref(func_ref: VMFuncRef, store: &Store) -> Self {
// if func_ref.is_null() {
// return Self::FuncRef(None);
// }
// let item: &wasmer_vm::VMCallerCheckedAnyfunc = unsafe {
// let anyfunc: *const wasmer_vm::VMCallerCheckedAnyfunc = *func_ref;
// &*anyfunc
// };
// let signature = store
// .engine()
// .lookup_signature(item.type_index)
// .expect("Signature not found in store");
// let export = wasmer_engine::ExportFunction {
// // TODO:
// // figure out if we ever need a value here: need testing with complicated import patterns
// metadata: None,
// vm_function: wasmer_vm::VMFunction {
// address: item.func_ptr,
// signature,
// // TODO: review this comment (unclear if it's still correct):
// // All functions in tables are already Static (as dynamic functions
// // are converted to use the trampolines with static signatures).
// kind: wasmer_vm::VMFunctionKind::Static,
// vmctx: item.vmctx,
// call_trampoline: None,
// instance_ref: None,
// },
// };
// let f = Function::from_vm_export(store, export);
// Self::FuncRef(Some(f))
// }
// fn into_table_reference(&self, store: &Store) -> Result<wasmer_vm::TableElement, RuntimeError> {
// if !self.comes_from_same_store(store) {
// return Err(RuntimeError::new("cross-`Store` values are not supported"));
// }
// Ok(match self {
// // TODO(reftypes): review this clone
// Self::ExternRef(extern_ref) => {
// wasmer_vm::TableElement::ExternRef(extern_ref.clone().into())
// }
// Self::FuncRef(None) => wasmer_vm::TableElement::FuncRef(VMFuncRef::null()),
// Self::FuncRef(Some(f)) => wasmer_vm::TableElement::FuncRef(f.vm_funcref()),
// _ => return Err(RuntimeError::new("val is not reference")),
// })
// }
// fn from_table_reference(item: wasmer_vm::TableElement, store: &Store) -> Self {
// match item {
// wasmer_vm::TableElement::FuncRef(f) => Self::from_vm_funcref(f, store),
// wasmer_vm::TableElement::ExternRef(extern_ref) => Self::ExternRef(extern_ref.into()),
// }
// }
// }

4
lib/js-api/src/utils.rs Normal file
View File

@@ -0,0 +1,4 @@
/// Check if the provided bytes are wasm-like
pub fn is_wasm(bytes: impl AsRef<[u8]>) -> bool {
bytes.as_ref().starts_with(b"\0asm")
}

334
lib/js-api/tests/export.rs Normal file
View File

@@ -0,0 +1,334 @@
// 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,458 @@
use wasm_bindgen_test::*;
// use anyhow::Result;
use wasmer_js::*;
// #[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(())
// }
#[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()
// })
// );
// 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 { .. })));
// assert!(bad_result.is_err());
}
// #[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,87 @@
use anyhow::Result;
use wasm_bindgen_test::*;
use wasmer_js::*;
// #[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(())
// }
#[wasm_bindgen_test]
fn test_exported_memory() {
// Let's declare the Wasm module with the text representation.
let wasm_bytes = wat2wasm(
br#"
(module
(memory (export "mem") 1)
)
"#,
)
.unwrap();
// Create a Store.
// Note that we don't need to specify the engine/compiler if we want to use
// the default provided by Wasmer.
// You can use `Store::default()` for that.
let store = Store::default();
println!("Compiling module...");
// Let's compile the Wasm module.
let module = Module::new(&store, wasm_bytes).unwrap();
// Create an empty import object.
let import_object = imports! {};
println!("Instantiating module...");
// Let's instantiate the Wasm module.
let instance = Instance::new(&module, &import_object).unwrap();
// let load = instance
// .exports
// .get_native_function::<(), (WasmPtr<u8, Array>, i32)>("load")?;
// Here we go.
//
// The Wasm module exports a memory under "mem". Let's get it.
let memory = instance.exports.get_memory("mem").unwrap();
// Now that we have the exported memory, let's get some
// information about it.
//
// The first thing we might be intersted in is the size of the memory.
// Let's get it!
println!("Memory size (pages) {:?}", memory.size());
println!("Memory size (bytes) {:?}", memory.data_size());
}

248
lib/js-api/tests/module.rs Normal file
View File

@@ -0,0 +1,248 @@
use anyhow::Result;
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();
// We explicitly don't test the wasm name from the name section
// because it adds unnecessary overhead for really little gain.
// assert_eq!(module.name(), Some("name"));
module.set_name("new_name");
assert_eq!(module.name(), Some("new_name"));
}
#[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)
),]
);
// Ok(())
}
#[wasm_bindgen_test]
fn exports() {
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).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(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,497 @@
// 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(())
// }