mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-09 14:18:20 +00:00
Added basic example of Wasmer-js
This commit is contained in:
68
Cargo.lock
generated
68
Cargo.lock
generated
@@ -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"
|
||||||
|
|||||||
@@ -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
39
lib/js-api/Cargo.toml
Normal 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
71
lib/js-api/README.md
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
# `wasmer` [](https://github.com/wasmerio/wasmer/actions?query=workflow%3Abuild) [](https://slack.wasmer.io) [](https://github.com/wasmerio/wasmer/blob/master/LICENSE) [](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
224
lib/js-api/src/env.rs
Normal 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
82
lib/js-api/src/error.rs
Normal 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
37
lib/js-api/src/export.rs
Normal 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
339
lib/js-api/src/exports.rs
Normal 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
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
249
lib/js-api/src/externals/global.rs
vendored
Normal 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
332
lib/js-api/src/externals/memory.rs
vendored
Normal 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
122
lib/js-api/src/externals/mod.rs
vendored
Normal 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
193
lib/js-api/src/externals/table.rs
vendored
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
411
lib/js-api/src/import_object.rs
Normal file
411
lib/js-api/src/import_object.rs
Normal 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
150
lib/js-api/src/instance.rs
Normal 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
114
lib/js-api/src/iterators.rs
Normal 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
333
lib/js-api/src/lib.rs
Normal 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
438
lib/js-api/src/module.rs
Normal 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
269
lib/js-api/src/native.rs
Normal 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(¶ms_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
426
lib/js-api/src/ptr.rs
Normal 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() });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
77
lib/js-api/src/resolver.rs
Normal file
77
lib/js-api/src/resolver.rs
Normal 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
60
lib/js-api/src/store.rs
Normal 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
207
lib/js-api/src/trap.rs
Normal 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
115
lib/js-api/src/types.rs
Normal 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
4
lib/js-api/src/utils.rs
Normal 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
334
lib/js-api/tests/export.rs
Normal 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(())
|
||||||
|
// }
|
||||||
458
lib/js-api/tests/externals.rs
Normal file
458
lib/js-api/tests/externals.rs
Normal 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(())
|
||||||
|
// }
|
||||||
87
lib/js-api/tests/instance.rs
Normal file
87
lib/js-api/tests/instance.rs
Normal 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
248
lib/js-api/tests/module.rs
Normal 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(())
|
||||||
|
// }
|
||||||
497
lib/js-api/tests/reference_types.rs
Normal file
497
lib/js-api/tests/reference_types.rs
Normal 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(())
|
||||||
|
// }
|
||||||
Reference in New Issue
Block a user