Merge branch 'master' into doc-readmes

This commit is contained in:
Ivan Enderlin
2020-07-20 10:25:15 +02:00
113 changed files with 6735 additions and 68 deletions

View File

@@ -15,7 +15,7 @@ jobs:
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
profile: minimal profile: minimal
toolchain: nightly-2019-12-19 toolchain: 1.45.0
override: true override: true
components: rustfmt, clippy components: rustfmt, clippy
- run: make lint - run: make lint
@@ -29,7 +29,7 @@ jobs:
# uses: actions-rs/toolchain@v1 # uses: actions-rs/toolchain@v1
# with: # with:
# profile: minimal # profile: minimal
# toolchain: nightly-2019-12-19 # toolchain: 1.45.0
# override: true # override: true
# components: rustfmt, clippy # components: rustfmt, clippy
# - run: make doc-local # - run: make doc-local
@@ -44,13 +44,13 @@ jobs:
include: include:
- build: linux - build: linux
os: ubuntu-latest os: ubuntu-latest
rust: nightly-2019-12-19 rust: 1.45.0
- build: macos - build: macos
os: macos-latest os: macos-latest
rust: nightly-2019-12-19 rust: 1.45.0
- build: windows - build: windows
os: windows-latest os: windows-latest
rust: nightly-2019-12-19 rust: 1.45.0
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Install Rust ${{ matrix.rust }} - name: Install Rust ${{ matrix.rust }}

View File

@@ -14,7 +14,7 @@ We recommend trying the following commands before sending a pull request to ensu
- `cargo fmt --all` Ensures all code is correctly formatted. - `cargo fmt --all` Ensures all code is correctly formatted.
- Run `cargo test` in the crates that you are modifying. - Run `cargo test` in the crates that you are modifying.
- Run `cargo build --all` (nightly) or `cargo build --all --exclude wasmer-singlepass-backend` - Run `cargo build --all`.
A comprehensive CI test suite will be run by a Wasmer team member after the PR has been created. A comprehensive CI test suite will be run by a Wasmer team member after the PR has been created.
@@ -25,9 +25,3 @@ A comprehensive CI test suite will be run by a Wasmer team member after the PR h
`Didn't find usable system-wide LLVM` `Didn't find usable system-wide LLVM`
Building Wasmer with the LLVM backend requires LLVM to be installed Building Wasmer with the LLVM backend requires LLVM to be installed
#### Singlepass Nightly Only
`error[E0554]: #![feature] may not be used on the stable release channel`
Building Wasmer with the singlepass backend requires the nightly version of Rust

20
Cargo.lock generated
View File

@@ -563,14 +563,14 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
[[package]] [[package]]
name = "dynasm" name = "dynasm"
version = "0.6.0" version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48be6e89d4c88d2945d5c57d50a2d7c46325a3ae358a11cdf263c545eace97c6" checksum = "6a93688d3993e998336f7e31d1272bafc078f7665c8b883bd08f140e73d2e9cd"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"byteorder", "byteorder",
"lazy_static", "lazy_static",
"owning_ref", "proc-macro-error",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn",
@@ -578,11 +578,12 @@ dependencies = [
[[package]] [[package]]
name = "dynasmrt" name = "dynasmrt"
version = "0.6.0" version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dce4800e1140e94ad07e35e789df569516fd795223600a96f3b01887a42e0da" checksum = "acae550ea0de09db4dcb18d4cd49f9f7963edd34783a4571429ba2c4e13e1e09"
dependencies = [ dependencies = [
"byteorder", "byteorder",
"dynasm",
"memmap", "memmap",
] ]
@@ -1165,15 +1166,6 @@ dependencies = [
"sdl2", "sdl2",
] ]
[[package]]
name = "owning_ref"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce"
dependencies = [
"stable_deref_trait",
]
[[package]] [[package]]
name = "parking_lot" name = "parking_lot"
version = "0.10.2" version = "0.10.2"

View File

@@ -53,6 +53,9 @@ cfg-if = "0.1"
members = [ members = [
"lib/c-api", "lib/c-api",
] ]
exclude = [
"lib/deprecated",
]
[build-dependencies] [build-dependencies]
test-generator = { path = "tests/lib/test-generator" } test-generator = { path = "tests/lib/test-generator" }

View File

@@ -10,21 +10,13 @@ endif
compilers := compilers :=
# Singlepass is enabled
RUST_VERSION := $(shell rustc -V)
ifneq (, $(findstring nightly,$(RUST_VERSION)))
# Singlepass doesn't work yet on Windows
ifneq ($(OS), Windows_NT)
compilers += singlepass
endif
endif
ifeq ($(ARCH), x86_64) ifeq ($(ARCH), x86_64)
# In X64, Cranelift is enabled # In X64, Cranelift is enabled
compilers += cranelift compilers += cranelift
# LLVM could be enabled if not in Windows # LLVM could be enabled if not in Windows
ifneq ($(OS), Windows_NT) ifneq ($(OS), Windows_NT)
# Singlepass doesn't work yet on Windows
compilers += singlepass
# Autodetect LLVM from llvm-config # Autodetect LLVM from llvm-config
ifneq (, $(shell which llvm-config)) ifneq (, $(shell which llvm-config))
LLVM_VERSION := $(shell llvm-config --version) LLVM_VERSION := $(shell llvm-config --version)
@@ -96,7 +88,7 @@ build-capi-llvm:
# Testing # # Testing #
########### ###########
test: $(foreach compiler,$(compilers),test-$(compiler)) test-packages test-examples test: $(foreach compiler,$(compilers),test-$(compiler)) test-packages test-examples test-deprecated
test-singlepass: test-singlepass:
cargo test --release $(compiler_features) --features "test-singlepass" cargo test --release $(compiler_features) --features "test-singlepass"
@@ -135,6 +127,11 @@ test-wasi-unit:
test-examples: test-examples:
cargo test --release $(compiler_features) --features wasi --examples cargo test --release $(compiler_features) --features wasi --examples
test-deprecated:
cargo test --manifest-path lib/deprecated/runtime-core/Cargo.toml -p wasmer-runtime-core --release
cargo test --manifest-path lib/deprecated/runtime/Cargo.toml -p wasmer-runtime --release
cargo test --manifest-path lib/deprecated/runtime/Cargo.toml -p wasmer-runtime --release --examples
############# #############
# Packaging # # Packaging #
############# #############

4
fuzz/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
target
corpus
artifacts

49
fuzz/Cargo.toml Normal file
View File

@@ -0,0 +1,49 @@
[package]
name = "wasmer-bin-fuzz"
version = "0.0.0"
authors = ["Automatically generated"]
publish = false
edition = "2018"
[package.metadata]
cargo-fuzz = true
[dependencies.wasmer]
path = "../lib/api"
[dependencies.wasmer-compiler-cranelift]
path = "../lib/compiler-cranelift"
[dependencies.wasmer-compiler-llvm]
path = "../lib/compiler-llvm"
[dependencies.wasmer-compiler-singlepass]
path = "../lib/compiler-singlepass"
[dependencies.wasmer-engine-jit]
path = "../lib/engine-jit"
[dependencies.wasmer-engine-native]
path = "../lib/engine-native"
[dependencies.libfuzzer-sys]
git = "https://github.com/rust-fuzz/libfuzzer-sys.git"
# Prevent this from interfering with workspaces
[workspace]
members = ["."]
[[bin]]
name = "validate"
path = "fuzz_targets/validate.rs"
[[bin]]
name = "jit_cranelift"
path = "fuzz_targets/jit_cranelift.rs"
[[bin]]
name = "jit_llvm"
path = "fuzz_targets/jit_llvm.rs"
[[bin]]
name = "jit_singlepass"
path = "fuzz_targets/jit_singlepass.rs"
[[bin]]
name = "native_cranelift"
path = "fuzz_targets/native_cranelift.rs"

52
fuzz/README.md Normal file
View File

@@ -0,0 +1,52 @@
This directory contains the fuzz tests for wasmer. To fuzz, we use the `cargo-fuzz` package.
## Installation
You may need to install the `cargo-fuzz` package to get the `cargo fuzz` subcommand. Use
```sh
$ cargo install cargo-fuzz
```
`cargo-fuzz` is documented in the [Rust Fuzz Book](https://rust-fuzz.github.io/book/cargo-fuzz.html).
## Running a fuzzer (validate, jit_llvm, native_cranelift, ...)
Once `cargo-fuzz` is installed, you can run the `validate` fuzzer with
```sh
cargo fuzz run validate
```
or the `jit_cranelift` fuzzer
```sh
cargo fuzz run jit_cranelift
```
See the [fuzz/fuzz_targets](https://github.com/wasmerio/wasmer-reborn/tree/fuzz/fuzz_targets/) directory for the full list of targets.
You should see output that looks something like this:
```
#1408022 NEW cov: 115073 ft: 503843 corp: 4659/1807Kb lim: 4096 exec/s: 889 rss: 857Mb L: 2588/4096 MS: 1 ChangeASCIIInt-
#1408273 NEW cov: 115073 ft: 503844 corp: 4660/1808Kb lim: 4096 exec/s: 888 rss: 857Mb L: 1197/4096 MS: 1 ShuffleBytes-
#1408534 NEW cov: 115073 ft: 503866 corp: 4661/1809Kb lim: 4096 exec/s: 886 rss: 857Mb L: 977/4096 MS: 1 ShuffleBytes-
#1408540 NEW cov: 115073 ft: 503869 corp: 4662/1811Kb lim: 4096 exec/s: 886 rss: 857Mb L: 2067/4096 MS: 1 ChangeBit-
#1408831 NEW cov: 115073 ft: 503945 corp: 4663/1811Kb lim: 4096 exec/s: 885 rss: 857Mb L: 460/4096 MS: 1 CMP- DE: "\x16\x00\x00\x00\x00\x00\x00\x00"-
#1408977 NEW cov: 115073 ft: 503946 corp: 4664/1813Kb lim: 4096 exec/s: 885 rss: 857Mb L: 1972/4096 MS: 1 ShuffleBytes-
#1408999 NEW cov: 115073 ft: 503949 corp: 4665/1814Kb lim: 4096 exec/s: 884 rss: 857Mb L: 964/4096 MS: 2 ChangeBit-ShuffleBytes-
#1409040 NEW cov: 115073 ft: 503950 corp: 4666/1814Kb lim: 4096 exec/s: 884 rss: 857Mb L: 90/4096 MS: 1 ChangeBit-
#1409042 NEW cov: 115073 ft: 503951 corp: 4667/1814Kb lim: 4096 exec/s: 884 rss: 857Mb L: 174/4096 MS: 2 ChangeByte-ChangeASCIIInt-
```
It will continue to generate random inputs forever, until it finds a bug or is terminated. The testcases for bugs it finds go into `fuzz/artifacts/jit_cranelift` and you can rerun the fuzzer on a single input by passing it on the command line `cargo fuzz run jit_cranelift my_testcase.wasm`.
## Seeding the corpus, optional
The fuzzer works best when it has examples of small Wasm files to start with. Using `wast2json` from [wabt](https://github.com/WebAssembly/wabt), we can easily produce `.wasm` files out of the WebAssembly spec tests.
```sh
mkdir spec-test-corpus
for i in `find tests/ -name "*.wast"`; do wast2json --enable-all $i -o spec-test-corpus/$(basename $i).json; done
mv spec-test-corpus/*.wasm fuzz/corpus/validate/
rm -r spec-test-corpus
```
The corpus directory is created on the first run of the fuzzer. If it doesn't exist, run it first and then seed the corpus. The fuzzer will pick up new files added to the corpus while it is running.

View File

@@ -0,0 +1,25 @@
#![no_main]
#[macro_use]
extern crate libfuzzer_sys;
use wasmer::{imports, Instance, Module, Store};
use wasmer_compiler_cranelift::Cranelift;
use wasmer_engine_native::Native;
fuzz_target!(|wasm_bytes: &[u8]| {
let serialized = {
let mut compiler = Cranelift::default();
let store = Store::new(&Native::new(&mut compiler).engine());
match Module::validate(&store, wasm_bytes) {
Err(_) => return,
Ok(_) => {}
};
let module = Module::new(&store, wasm_bytes).unwrap();
module.serialize().unwrap();
};
let engine = Native::headless().engine();
let store = Store::new(&engine);
let module = unsafe { Module::deserialize(&store, serialized) };
let _instance = Instance::new(&module, &imports! {});
});

View File

@@ -0,0 +1,21 @@
#![no_main]
#[macro_use]
extern crate libfuzzer_sys;
use wasmer::{imports, CompilerConfig, Instance, Module, Store};
use wasmer_compiler_cranelift::Cranelift;
use wasmer_engine_jit::JIT;
fuzz_target!(|wasm_bytes: &[u8]| {
let mut compiler = Cranelift::default();
compiler.canonicalize_nans(true);
compiler.enable_verifier();
let store = Store::new(&JIT::new(&compiler).engine());
match Module::validate(&store, wasm_bytes) {
Ok(_) => {
let module = Module::new(&store, wasm_bytes).unwrap();
let _instance = Instance::new(&module, &imports! {});
}
Err(_) => {}
};
});

View File

@@ -0,0 +1,21 @@
#![no_main]
#[macro_use]
extern crate libfuzzer_sys;
use wasmer::{imports, CompilerConfig, Instance, Module, Store};
use wasmer_compiler_llvm::LLVM;
use wasmer_engine_jit::JIT;
fuzz_target!(|wasm_bytes: &[u8]| {
let mut compiler = LLVM::default();
compiler.canonicalize_nans(true);
compiler.enable_verifier();
let store = Store::new(&JIT::new(&compiler).engine());
match Module::validate(&store, wasm_bytes) {
Ok(_) => {
let module = Module::new(&store, wasm_bytes).unwrap();
let _instance = Instance::new(&module, &imports! {});
}
Err(_) => {}
};
});

View File

@@ -0,0 +1,19 @@
#![no_main]
#[macro_use]
extern crate libfuzzer_sys;
use wasmer::{imports, Instance, Module, Store};
use wasmer_compiler_singlepass::Singlepass;
use wasmer_engine_jit::JIT;
fuzz_target!(|wasm_bytes: &[u8]| {
let compiler = Singlepass::default();
let store = Store::new(&JIT::new(&compiler).engine());
match Module::validate(&store, wasm_bytes) {
Ok(_) => {
let module = Module::new(&store, wasm_bytes).unwrap();
let _instance = Instance::new(&module, &imports! {});
}
Err(_) => {}
};
});

View File

@@ -0,0 +1,25 @@
#![no_main]
#[macro_use]
extern crate libfuzzer_sys;
use wasmer::{imports, Instance, Module, Store};
use wasmer_compiler_cranelift::Cranelift;
use wasmer_engine_native::Native;
fuzz_target!(|wasm_bytes: &[u8]| {
let serialized = {
let mut compiler = Cranelift::default();
let store = Store::new(&Native::new(&mut compiler).engine());
match Module::validate(&store, wasm_bytes) {
Err(_) => return,
Ok(_) => {}
};
let module = Module::new(&store, wasm_bytes).unwrap();
module.serialize().unwrap()
};
let engine = Native::headless().engine();
let store = Store::new(&engine);
let module = unsafe { Module::deserialize(&store, serialized.as_slice()) }.unwrap();
let _instance = Instance::new(&module, &imports! {});
});

View File

@@ -0,0 +1,13 @@
#![no_main]
#[macro_use]
extern crate libfuzzer_sys;
use wasmer::{imports, Instance, Module, Store};
use wasmer_compiler_cranelift::Cranelift;
use wasmer_engine_jit::JIT;
fuzz_target!(|wasm_bytes: &[u8]| {
let compiler = Cranelift::default();
let store = Store::new(&JIT::new(&compiler).engine());
Module::validate(&store, wasm_bytes);
});

View File

@@ -42,7 +42,8 @@ impl Extern {
} }
} }
pub(crate) fn from_export(store: &Store, export: Export) -> Extern { /// Create an `Extern` from an `Export`.
pub fn from_export(store: &Store, export: Export) -> Extern {
match export { match export {
Export::Function(f) => Extern::Function(Function::from_export(store, f)), Export::Function(f) => Extern::Function(Function::from_export(store, f)),
Export::Memory(m) => Extern::Memory(Memory::from_export(store, m)), Export::Memory(m) => Extern::Memory(Memory::from_export(store, m)),

View File

@@ -6,7 +6,10 @@
intra_doc_link_resolution_failure intra_doc_link_resolution_failure
)] )]
#![warn(unused_import_braces)] #![warn(unused_import_braces)]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))] #![cfg_attr(
feature = "cargo-clippy",
allow(clippy::new_without_default, vtable_address_comparisons)
)]
#![cfg_attr( #![cfg_attr(
feature = "cargo-clippy", feature = "cargo-clippy",
warn( warn(

View File

@@ -172,17 +172,7 @@ fn memory_grow() -> Result<()> {
let bad_desc = MemoryType::new(Pages(15), Some(Pages(10)), false); let bad_desc = MemoryType::new(Pages(15), Some(Pages(10)), false);
let bad_result = Memory::new(&store, bad_desc); let bad_result = Memory::new(&store, bad_desc);
// due to stack overflow with a modern nightly, we can't update CI to use a version of nightly which will make this work assert!(matches!(bad_result, Err(MemoryError::InvalidMemory { .. })));
/*assert!(matches!(
bad_result,
Err(MemoryError::InvalidMemory { .. })
));*/
assert!(if let Err(MemoryError::InvalidMemory { .. }) = bad_result {
true
} else {
false
});
Ok(()) Ok(())
} }

View File

@@ -19,8 +19,8 @@ rayon = "1.3"
hashbrown = { version = "0.8", optional = true } hashbrown = { version = "0.8", optional = true }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
more-asserts = "0.2" more-asserts = "0.2"
dynasm = "0.6" dynasm = "0.7"
dynasmrt = "0.6" dynasmrt = "0.7"
lazy_static = "1.4" lazy_static = "1.4"
byteorder = "1.3" byteorder = "1.3"
smallvec = "1" smallvec = "1"

View File

@@ -37,13 +37,7 @@ The fact that singlepass is not prone to JIT bombs and offers a very
predictable compilation speed makes it ideal for **blockchains** and other predictable compilation speed makes it ideal for **blockchains** and other
systems where fast and consistent compilation times are very critical. systems where fast and consistent compilation times are very critical.
## Requirements
At the moment, this crate depends on Rust nightly to be compiled, as it uses
[`dynasm-rs`] which can only be compiled in Nightly.
[example]: https://github.com/wasmerio/wasmer-reborn/blob/master/examples/compiler_singlepass.rs [example]: https://github.com/wasmerio/wasmer-reborn/blob/master/examples/compiler_singlepass.rs
[`wasmer-compiler-cranelift`]: https://github.com/wasmerio/wasmer-reborn/tree/master/lib/compiler-cranelift [`wasmer-compiler-cranelift`]: https://github.com/wasmerio/wasmer-reborn/tree/master/lib/compiler-cranelift
[`wasmer-compiler-llvm`]: https://github.com/wasmerio/wasmer-reborn/tree/master/lib/compiler-llvm [`wasmer-compiler-llvm`]: https://github.com/wasmerio/wasmer-reborn/tree/master/lib/compiler-llvm
[`dynasm-rs`]: https://github.com/CensoredUsername/dynasm-rs

View File

@@ -7,10 +7,6 @@
//! //!
//! Compared to Cranelift and LLVM, Singlepass compiles much faster but has worse //! Compared to Cranelift and LLVM, Singlepass compiles much faster but has worse
//! runtime performance. //! runtime performance.
//!
//! > Note: Singlepass currently depends on Rust nightly features.
#![feature(proc_macro_hygiene)]
mod codegen_x64; mod codegen_x64;
mod common_decl; mod common_decl;

View File

@@ -1,5 +1,30 @@
# Wasmer deprecated packages # Wasmer deprecated packages
We create "wrapping" crates to ease the adodption of new ## Important Note; Please Read
versions of Wasmer, with minimal or no breaking changes.
Thanks to users' feedbacks, collected experience and various usecases,
Wasmer has decided to entirely changed its API to offer the best user
experience and the best features to as many users as possible, just
before the 1.0 release. This new version of Wasmer includes many
improvements in terms of performance or the memory consumption, in
addition to a ton of new features and much better flexibility!
In order to help our existing users to enjoy the performance boost and
memory improvements without updating their program that much, we have
created a new version of the `wasmer-runtime-core` crate, which is now
*a port* of the new API but with the old API, as much as
possible. Indeed, it was not always possible to provide the exact same
API, but changes are subtle.
We have carefully documented most of the differences in [the
`runtime-core/CHANGES.md` document](./runtime-core/CHANGES.md).
It is important to understand the public of this port. We do not
recommend to advanced users of Wasmer to use this port. Advanced API,
like `ModuleInfo` or the `vm` module (incl. `vm::Ctx`) have not been
fully ported because it was very internals to Wasmer. For advanced
users, we highly recommend to migrate to the new version of Wasmer,
which is awesome by the way (completely neutral opinion). The public
for this port is beginners or regular users that do not necesarily
have time to update their code immediately but that want to enjoy a
performance boost and memory improvements.

View File

@@ -0,0 +1,698 @@
# Changes between this port and the old `wasmer-runtime-core` API
This document lists *only the differences* between the old and the new
API (the port), i.e. API that didn't change are absent.
## Overall
* Host function must always take a `vm::Ctx` as first parameter
## All changes
By type name in alphabetic order.
### `Artifact`
Before:
```rust
impl Artifact {
fn deserialize(bytes: &[u8]) -> Result<Self, Error>;
}
```
After:
```rust
impl Artifact {
unsafe fn deserialize(bytes: &[u8]) -> Result<Self, Error>;
fn module(self) -> Module;
}
```
The `deserialize` method is now marked as `unsafe` since it is not
checked that `bytes` represent a valid artifact.
The new `module` method is introduced to fetch the `Module` inside the
artifact.
### `DynamicFunc`
Before:
```rust
impl DynamicFunc {
fn new<F>(signature: Arc<FuncSig>, func: F) -> Self;
}
```
After:
```rust
impl DynamicFunc {
fn new<F>(signature: &FuncSig, func: F) -> Self;
fn signature(&self) -> &FuncSig;
fn params(&self) -> &[Type];
fn returns(&self) -> &[Type];
fn call(&self, params: &[Value] -> Result<Box<[Value]>, RuntimeError>;
}
```
The constructor `new` no longer takes an `Arc` but a reference to
`FuncSig`.
`signature`, `params`, `returns` and `call` are new
methods. Previously, it was required to convert `DynamicFunc` into
`Func` with `Into<Func<…, …>>`. Now there is no conversion possible
between the two, and `DynamicFunc` gains its own methods.
### `DynFunc`
Before:
```rust
impl DynFunc {
fn raw(&self) -> *const Func;
}
```
After:
```rust
impl DynFunc {
}
```
The `raw` method has been removed. It was present for internal
purposes only, not sure it will impact you.
### `ExportDescriptor`
Before:
```rust
struct ExportDescriptor<'a> {
name: &'a str,
ty: ExternDescriptor,
}
```
After:
```rust
struct ExportDescriptor {
name: String,
ty: ExternDescriptor,
}
```
The lifetime on `ExportDescriptor` is no longer necessary: The `name`
field now holds a `String` instead of a `&str`.
### `Export`
Before:
```rust
impl Exports {
fn get<'a, T: Exportable<'a>>(&'a self, name: &str) -> Result<T, ResolveError>;
fn into_iter(&self) -> ExportIter;
}
```
After:
```rust
impl Exports {
fn get<'a, T: Exportable<'a> + Clone + 'a>(&'a self, name: &str) -> Result<T, ExportError>;
fn iter(&self) -> ExportsIterator<impl Iterator<Item = (&String, &Export)>>;
}
```
The `get` method is different. It returns a `T` where `T` implements
`Exportable<'a>` _and_ `Clone` (that the addition). The returned error
is also `ExportError` instead of `ResolveError`.
The method `into_iter` is now `iter` and returns references to the
export name and export value.
### `Func`
Before:
```rust
impl<Args, Rets> Func<Args, Rets> {
fn params(&self) -> &'static [Type];
fn returns(&self) -> &'static [Type];
fn get_vm_func(&self) -> NonNull<Func>;
}
```
After:
```rust
impl<Args, Rets> Func<Args, Rets> {
fn params(&self) -> &[Type];
fn returns(&self) -> &[Type];
fn dyn_call(&self, params: &[Value]) -> Result<Box<[Value]>, RuntimeError>;
fn signature(&self) -> &FuncSig;
}
```
The `params` and `returns` return a slice of `[Type]` which has not
the `'static` lifetime.
The `get_vm_func` method has been removed. It was present for internal
purposes, it unlikely it will have an impact on your project.
In addition to the `call` method, there is now a `dyn_call` method,
that calls the function with arguments packed in a slice of `Value`.
Finally, there is a new `signature` method.
### `FuncSig`
Before:
```rust
impl FuncSig {
fn returns(&self) -> &[Type];
fn check_param_value_types(&self, params: &[Value]) -> bool;
}
```
After:
```rust
impl FuncSig {
fn results(&self) -> &[Type];
}
```
The `returns` method has been renamed `results`.
The `check_param_value_types` method has been removed, since it is now
possible to compare two `Vec<Type>`.
### `GlobalInit`
`GlobalInit` has totally changed. It was a `struct`, now it's an
`enum`:
```rust
enum GlobalInit {
I32Const(i32),
I64Const(i64),
F32Const(f32),
F64Const(f64),
V128Const(V128),
GetGlobal(GlobalIndex),
RefNullConst,
RefFunc(FunctionIndex),
}
impl GlobalInit {
fn from_value<T>(value: Value<T>) -> Self;
fn to_value<T>(&self) -> Value<T>;
}
```
### `HostFunction`
Before:
```rust
trait HostFunction<Kind, Args, Rets> {
fn to_raw(self) -> (NonNull<Func>, Option<NonNull<FuncEnv>>);
}
```
After:
```rust
trait HostFunction<Args, Rets, Kind, T> {
fn function_body_ptr(self) -> *const VMFunctionBody;
}
```
The generic parameters of the `HostFunction` trait has been re-ordered
and a new one has been introduced: `T`. In the new API, it represents
the type of the environment; in this port, it should not be used (or
at worst, use `vm::Ctx`).
The `to_raw` method, aimed at internal using, has been replaced by
`function_body_ptr`. It is subject to another change, so please don't
use it.
### `ImportDescriptor`
Before:
```rust
struct ImportDescriptor {
namespace: String,
}
```
After:
```rust
struct ImportDescriptor {
module: String,
}
```
The `namespace` field has been renamed `module`.
### `ImportObject`
Before:
```rust
impl ImportObject {
fn with_namespace<Func, InnerRet>(&self, namespace: &str, f: Func) -> Option<InnerRet>;
fn maybe_with_namespace<Func, InnerRet>(&self, namespace: &str, f: Func) -> Option<InnerRet>;
}
```
After:
```rust
impl ImportObject {
fn call_state_creator(&self) -> Option<(*mut c_void, fn(*mut c_void))>;
fn get_export(&self, module: &str, name: &str) -> Option<Export>;
fn clone_ref(&self) -> Self;
}
```
The `with_namespace` and `maybe_with_namespace` methods have been
removed.
The `call_state_creator` method is new, along with `get_export` and
`clone_ref`.
### `Instance`
Before:
```rust
impl Instance {
fn load<T: Loader>(&self, loader: T) -> Result<T::Instance, T::Error>;
fn fun<Args, Rets>(&self, name: &str) -> ResolveResult<Args, Rets, Wasm>;
fn resolve_func(&self, name: &str) -> ResolveError<usize>;
fn dyn_func(&self, name: &str) -> ResolveResult<DynFunc>;
fn call(&self, name: &str, params: &[Value]) -> CallResult<Vec<Value>>;
fn context(&self) -> &Ctx;
fn context_mut(&mut self) -> &mut Ctx;
fn exports(&self) -> ExportsIter;
fn get_internal(&self, fields: &InternalField) -> u64;
fn set_internal(&self, fields: &InternalField, value: u64);
}
```
After:
```rust
impl Instance {
fn fun<Args, Rets>(&self, name: &str) -> Result<Func<Args, Rets>, ExportError>;
fn resolve_func(&self, name: &str) -> Result<usize, ()>;
fn dyn_func(&self, name: &str) -> Result<DynFunc, ExportError>;
fn call(&self, name: &str, params: &[Value]) -> Result<Vec<Value>, Box<dyn Error>>;
fn context(&self) -> Ref<Ctx>;
fn context_mut(&mut self) -> RefMut<Ctx>;
fn exports(&self) -> ExportsIterator<impl Iterator<Item = (&String, &Export)>>;
}
```
The `load`, `get_internal` and `set_internal` methods have been removed.
Some `Result`'s errors have changed: The `func` and `dyn_func` methods
return a `Result<…, ExportError>` instead of `ResolveResult<…>`; The
`resolve_func` method returns a `Result<usize, ()>` instead of
`ResolveError<usize>`; The `call` method returns a `Result<…, Box<dyn
Error>>` instead of `CallResult<…>`.
The `context` and `context_mut` methods respectively return a
`Ref<Ctx>` and `RefMut<Ctx>` instead of `&Ctx` and `&mut Ctx`.
The `exports` method returns an `ExportsIterator<impl Iterator<Item =
(&String, &Export)>>` instead of `ExportsIter`. That's basically the same.
### `Memory`
Before:
```rust
impl Memory {
fn new(desc: MemoryDescriptor) -> Result<Self, CreationError>;
fn grow(&self, delta: Pages) -> Result<Pages, GrowError>;
}
```
After:
```rust
impl Memory {
fn new(desc: MemoryDescriptor) -> Result<Self, MemoryError>;
fn grow(&self, delta: Pages) -> Result<Pages, MemoryError>;
}
```
Only the `Result`'s errors have changed between `new` and `grow` from
respectively `CreationError` and `GrowError` to a general
`MemoryError` type.
### `MemoryDescriptor`
Before
```rust
struct MemoryDescriptor {
memory_type: MemoryType,
}
```
After:
```rust
struct MemoryDescriptor {}
```
The `memory_type` field has been removed.
### `MemoryType`
Before:
```rust
enum MemoryType {
Static,
SharedStatic,
}
```
After:
```rust
enum MemoryType {
Static { bound: Pages },
}
```
The `SharedStatic` variant has been removed.
The `Static` variant is now a structure of type `Static { bound: Pages }`.
### `Module`
Before:
```rust
impl Module {
fn instantiate(&self, import_object: &ImportObject) -> Result<Instance>;
fn cache(&self) -> Result<Artifact, CacheError>;
fn custom_sections(&self, key: impl AsRef<str>) -> Option<&[Vec<u8>]>
}
```
After:
```rust
impl Module {
fn instantiate(&self, import_object: &ImportObject) -> Result<Instance, InstantiationError>;
fn cache(&self) -> Result<Artifact, Infallible>;
fn custom_sections(&self, key: impl AsRef<str>) -> Option<Vec<Vec<u8>>>;
}
```
The `Result`'s errors of `instantiate` and `cache` have changed
respectively from `Error` and `CacheError` to `InstantiationError` and
`Infallible`. For `cache`, it means that it will never fail. The
`Result` is kept to avoid a change in the API.
The `custom_sections` method returns an `Option<Vec<Vec<u8>>` instead
of `Option<&[Vec<u8>]>`.
### `ModuleInfo`
Before:
```rust
struct ModuleInfo {
backend: String,
custom_sections: HashMap<String, Vec<Vec<u8>>>,
data_initializers: Vec<DataInitializer>,
elem_initializers: Vec<TableInitializer>,
em_symbol_map: Option<HashMap<u32, String>>,
func_assoc: Map<FuncIndex, SigIndex>,
generate_debug_info: bool,
globals: Map<LocalGlobalIndex, GlobalInit>,
imported_functions: Map<ImportedFuncIndex, ImportName>,
imported_globals: Map<ImportedGlobalIndex, (ImportName, GlobalDescriptor)>,
imported_memories: Map<ImportedMemoryIndex, (ImportName, MemoryDescriptor)>,
imported_tables: Map<ImportedTableIndex, (ImportName, TableDescriptor)>,
memories: Map<LocalMemoryIndex, MemoryDescripto>,
name_table: StringTable<NameIndex>,
namespace_table: StringTable<NamespaceIndex>,
signatures: Map<SigIndex, FuncSig>,
start_func: Option<FuncIndex>,
tables: Map<LocalTableIndex, TableDescriptor>,
}
```
After:
```rust
struct ModuleInfo {
custom_sections: IndexMap<String, CustomSectionIndex>,
custom_sections_data: PrimaryMap<CustomSectionIndex, Arc<[u8]>>,
func_names: HashMap<FunctionIndex, String>,
functions: PrimaryMap<FunctionIndex, SignatureIndex>,
global_initializers: PrimaryMap<LocalGlobalIndex, GlobalInit>,
globals: PrimaryMap<GlobalIndex, GlobalType>,
id: ModuleId,
imports: IndexMap<(String, String, u32), ImportIndex>,
memories: PrimaryMap<MemoryIndex, MemoryType>,
name: Option<String>,
num_imported_funcs: usize,
num_imported_globals: usize,
num_imported_memories: usize,
num_imported_tables: usize,
passive_data: HashMap<DataIndex, Arc<[u8]>>,
passive_elements: HashMap<ElemIndex, Box<[FunctionIndex]>>,
signatures: PrimaryMap<SignatureIndex, FunctionType>,
start_func: Option<FunctionIndex>,
table_elements: Vec<TableElements>,
tables: PrimaryMap<TableIndex, TableType>,
}
```
We are not going to re-phrase the differences here, but clearly a lot
has changed.
### `Namespace`
Before:
```rust
impl Namespace {
fn insert<S, E>(&mut self, name: S, export: E) -> Option<Box<dyn IsExport + Send>>;
}
```
After:
```rust
impl Namespace {
fn insert<S, E>(&mut self, name: S, export: E);
}
```
The `insert` method no longer returns a value.
### `NativeWasmType`
Before:
```rust
trait NativeWasmType {
const TYPE: Type;
fn from_binary(bits: u64) -> Self;
fn to_binary(self) -> u64;
}
```
After:
```rust
trait NativeWasmType {
type Abi: Copy + std::fmt::Debug;
const WASM_TYPE: Type;
fn from_binary(binary: i128) -> Self;
fn to_binary(self) -> i128;
fn into_abi(self) -> Self::Abi;
fn from_abi(abi: Self::Abi) -> Self;
fn to_value<T>(self) -> Value<T>;
}
```
The `TYPE` constant has been renamed `WASM_TYPE`.
The `into_abi`, `from_abi` and `to_value` methods are new, in addition
to the `Abi` type.
The `to_binary` and `from_binary` methods now take a `i128` instead of
a `u64`.
### `Pages`
Before:
```rust
impl Pages {
fn checked_add(self, rhs: Self) -> Result<Pages, PageError>;
}
```
After:
```rust
impl Pages {
fn checked_add(self, rhs: Self) -> Option<Self>;
const fn max_values() -> Self;
}
```
The `checked_add` method now returns an `Option<Self>` rather than a
`Result<Pages, PageError>`.
The constant `max_values` function has been introduced.
### `Table`
Before:
```rust
impl Table {
fn new(desc: TableDescriptor) -> Result<Self, CreationError>;
fn set<T: StorableInTable>(&self, index: u32, element: T) -> Result<(), TableAccessError>;
fn grow(&self, delta: u32) -> Result<u32, GrowError>;
fn vm_local_table(&mut self) -> *mut LocalTable;
}
```
After:
```rust
impl Table {
fn new(desc: TableDescriptor, initial_value: Value) -> Result<Self, RuntimeError>;
fn set(&self, index: u32, element: Value) -> Result<(), RuntimeError>;
fn get(&self, index: u32) -> Option<Value>;
fn grow(&self, delta: u32, initial_value: Value) -> Result<u32, RuntimeError>;
}
```
The `new` constructor takes an `initial_value` and returns a
`RuntimeError` in case of an error instead of a `CreationError`.
The `set` method takes a `Value` rather a `T: StorableInTable`. It
also returns a `RuntimeError` rather than a `TableAccessError` in case
of an error.
The `get` method is new!
The `grow` method takes an `initial_value`, just like `new`, and also
returns a `RuntimeError` rather than a `GrowError`.
Finally, the `vm_local_table` method has been removed. It was quite an
internal API, it's unlikely it will impact your project.
### `TableDescriptor`
Before:
```rust
struct TableDescriptor {
element: ElementType,
}
```
After:
```rust
struct TableDescriptor {
ty: Type,
}
```
The `element` field has been renamed `ty`.
A `new` constructor has been introduced: `fn new(ty: Type, minimum:
u32, maximum: Option<u32>) -> Self`.
### `WasmTypeList`
Before:
```rust
trait WasmTypeList {
type RetArray: AsMut<[u64]>;
fn from_ret_array(array: Self::RetArray) -> Self;
fn empty_ret_array() -> Self::RetArray;
fn types() -> &'static [Type];
fn call<Rets>(self, f: NonNull<Func>, wasm: Wasm, ctx: *mut Ctx) -> Result<Rets, RuntimeError>;
}
```
After:
```rust
trait WasmTypeList {
type Array: AsMut<[i128]>;
fn from_array(array: Self::Array) -> Self;
fn empty_array(self) -> Self::Array;
fn wasm_types() -> &'static [Type];
fn from_slice(slice: &[i128]) -> Result<Self, TryFromSliceError>;
fn into_array(self) -> Self::Array;
}
```
The `RetArray` type has been renamed `Array`. The concrete type must
now implement the `AsMut<[i128]>` trait instead of `AsMut<[u64]>`.
`from_ret_array` and `empty_ret_array` have been renamed
accordingly like `from_array` and `empty_array`. Note that
`empty_array` is now a method instead of an associated function.
The `types` function has been renamed `wasm_types`.
The `call` method has been removed. It was part of an internal API,
it's unlikely it will break your code.
The `from_slice` constructor and the `into_array` method are new!
### `WasmHash`
Before:
```rust
impl WasmHash {
fn decode(hex_str: &str) -> Result<Self, Error>;
}
```
After:
```rust
impl WasmHash {
fn decode(hex_str: &str) -> Result<Self, DeserializeError>;
}
```
The `Result`'s error has changed from `Error` to `DeserializeError`
for the `decode` method.

1314
lib/deprecated/runtime-core/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,39 @@
[package]
name = "wasmer-runtime-core"
version = "0.18.0"
description = "Wasmer runtime core library"
license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
repository = "https://github.com/wasmerio/wasmer"
keywords = ["wasm", "webassembly", "runtime"]
categories = ["wasm"]
edition = "2018"
[badges]
maintenance = { status = "deprecated" }
[dependencies]
wasm-common = { path = "../../wasm-common", version = "1.0.0-alpha.1" }
wasmer = { path = "../../api", version = "1.0.0-alpha.1" }
wasmer-cache = { path = "../../cache", version = "1.0.0-alpha.1" }
wasmer-compiler = { path = "../../compiler", version = "1.0.0-alpha.1", features = ["translator"] }
wasmer-compiler-llvm = { path = "../../compiler-llvm", version = "1.0.0-alpha.1", optional = true }
wasmer-compiler-cranelift = { path = "../../compiler-cranelift", version = "1.0.0-alpha.1", optional = true }
wasmer-compiler-singlepass = { path = "../../compiler-singlepass", version = "1.0.0-alpha.1", optional = true }
wasmer-engine = { path = "../../engine", version = "1.0.0-alpha.1" }
wasmer-engine-jit = { path = "../../engine-jit", version = "1.0.0-alpha.1" }
wasmer-vm = { path = "../../vm", version = "1.0.0-alpha.1" }
lazy_static = "1.4"
[build-dependencies]
blake3 = "0.3.4"
[features]
default = ["wasmer/default"]
cranelift = ["wasmer/cranelift", "wasmer-compiler-cranelift"]
llvm = ["wasmer/llvm", "wasmer-compiler-llvm"]
singlepass = ["wasmer/singlepass", "wasmer-compiler-singlepass"]
default-backend-cranelift = ["cranelift"]
default-backend-llvm = ["llvm"]
default-backend-singlepass = ["singlepass"]
dynamicfunc-fat-closures = []

View File

@@ -0,0 +1,41 @@
# Wasmer Runtime Core Library [DEPRECATED]
## Important Note; Please Read
Thanks to users' feedbacks, collected experience and various usecases,
Wasmer has decided to entirely changed its API to offer the best user
experience and the best features to as many users as possible, just
before the 1.0 release. This new version of Wasmer includes many
improvements in terms of performance or the memory consumption, in
addition to a ton of new features and much better flexibility!
In order to help our existing users to enjoy the performance boost and
memory improvements without updating their program that much, we have
created a new version of the `wasmer-runtime-core` crate, which is now
*a port* of the new API but with the old API, as much as
possible. Indeed, it was not always possible to provide the exact same
API, but changes are subtle.
We have carefully documented most of the differences in [the
`CHANGES.md` document](./CHANGES.md).
It is important to understand the public of this port. We do not
recommend to advanced users of Wasmer to use this port. Advanced API,
like `ModuleInfo` or the `vm` module (incl. `vm::Ctx`) have not been
fully ported because it was very internals to Wasmer. For advanced
users, we highly recommend to migrate to the new version of Wasmer,
which is awesome by the way (completely neutral opinion). The public
for this port is beginners or regular users that do not necesarily
have time to update their code immediately but that want to enjoy a
performance boost and memory improvements.
## Introduction
The `wasmer-runtime-core` was the entry point to the Wasmer Runtime,
by providing common types to compile and to instantiate a WebAssembly
module.
Most Wasmer users should prefer the API which is re-exported by the
[`wasmer-runtime`](https://github.com/wasmerio/wasmer-reborn/tree/master/lib/deprecated/runtime)
library by default. This crate provides additional APIs which may be
useful to users that wish to customize the Wasmer runtime.

View File

@@ -0,0 +1,29 @@
use blake3::Hasher;
use std::{env, fs, io::Write, path::PathBuf};
const WASMER_VERSION: &'static str = env!("CARGO_PKG_VERSION");
fn main() {
// `wasmer_version_hash.txt`
{
let mut hasher = Hasher::new();
hasher.update(WASMER_VERSION.as_bytes());
let hasher = hasher.finalize();
let hash_string = hasher.to_hex().as_str().to_owned();
let crate_dir = env::var("OUT_DIR").unwrap();
let wasmer_version_hash_file = {
let mut path = PathBuf::from(&crate_dir);
path.push("wasmer_version_hash.txt");
path
};
let mut f_out = fs::File::create(wasmer_version_hash_file)
.expect("Could not create file for wasmer hash value");
f_out
.write_all(hash_string.as_bytes())
.expect("Could not write to file for wasmer hash value");
}
}

View File

@@ -0,0 +1,8 @@
struct Artifact {}
impl Artifact {
unsafe fn deserialize(bytes: &[u8]) -> Result<Self, Error>;
fn info(&self) -> &ModuleInfo;
fn serialize(&self) -> Result<Vec<u8>, Error>;
fn module(self) -> Module;
}

View File

@@ -0,0 +1 @@
struct Bytes(pub usize);

View File

@@ -0,0 +1,9 @@
struct DynamicFunc {}
impl DynamicFunc {
fn new<F>(signature: &FuncSig, func: F) -> Self;
fn signature(&self) -> &FuncSig;
fn params(&self) -> &[Type];
fn returns(&self) -> &[Type];
fn call(&self, params: &[Value]) -> Result<Box<[Value]>, RuntimeError>;
}

View File

@@ -0,0 +1,6 @@
struct DynFunc {}
impl DynFunc {
fn call(&self, params: &[Value]) -> Result<Vec<Value>, CallError>;
fn signature(&self) -> &FuncSig;
}

View File

@@ -0,0 +1,4 @@
struct ExportDescriptor {
name: String,
ty: ExternDescriptor,
}

View File

@@ -0,0 +1,6 @@
struct Exports {}
impl Exports {
fn get<'a, T: Exportable<'a> + Clone + 'a>(&'a self, name: &str) -> Result<T, ExportError>;
fn iter(&self) -> ExportsIterator<impl Iterator<Item = (&String, &Export)>>;
}

View File

@@ -0,0 +1,10 @@
struct Func<Args, Rets> {}
impl<Args, Rets> Func<Args, Rets> {
fn new<F>(func: F) -> Self;
fn params(&self) -> &[Type];
fn returns(&self) -> &[Type];
fn call(...) -> Result<Rets, RuntimeError>;
fn dyn_call(&self, params: &[Value]) -> Result<Box<[Value]>, RuntimeError>;
fn signature(&self) -> &FuncSig;
}

View File

@@ -0,0 +1,7 @@
struct FuncSig {}
impl FuncSig {
fn new<Params, Returns>(params: Params, returns: Returns) -> Self;
fn params(&self) -> &[Type];
fn results(&self) -> &[Type];
}

View File

@@ -0,0 +1,9 @@
struct Global {}
impl Global {
fn new(value: Value) -> Self;
fn new_mutable(value: Value) -> Self;
fn descriptor(&self) -> GlobalDescriptor;
fn set(&self, value: Value);
fn get(&self) -> Value;
}

View File

@@ -0,0 +1,4 @@
struct GlobalDescriptor {
mutable: bool,
ty: Type,
}

View File

@@ -0,0 +1,15 @@
enum GlobalInit {
I32Const(i32),
I64Const(i64),
F32Const(f32),
F64Const(f64),
V128Const(V128),
GetGlobal(GlobalIndex),
RefNullConst,
RefFunc(FunctionIndex),
}
impl GlobalInit {
fn from_value<T>(value: Value<T>) -> Self;
fn to_value<T>(&self) -> Value<T>;
}

View File

@@ -0,0 +1,3 @@
trait HostFunction<Args, Rets, Kind, T> {
fn function_body_ptr(self) -> *const VMFunctionBody;
}

View File

@@ -0,0 +1,5 @@
struct ImportDescriptor {
module: String,
name: String,
ty: ExternDescriptor,
}

View File

@@ -0,0 +1,11 @@
struct ImportObject {}
impl ImportObject {
fn new() -> Self;
fn new_with_data<F>(state_creator: F) -> Self;
fn register<S, N>(&mut self, name: S, namespace: N) -> Option<Box<dyn LikeNamespace>>;
fn contains_namespace(&self, name: &str) -> bool;
fn call_state_creator(&self) -> Option<(*mut c_void, fn(*mut c_void))>;
fn get_export(&self, module: &str, name: &str) -> Option<Export>;
fn clone_ref(&self) -> Self;
}

View File

@@ -0,0 +1,14 @@
struct Instance {
exports: Exports,
}
impl Instance {
fn fun<Args, Rets>(&self, name: &str) -> Result<Func<Args, Rets>, ExportError>;
fn resolve_func(&self, name: &str) -> Result<usize, ()>;
fn dyn_func(&self, name: &str) -> Result<DynFunc, ExportError>;
fn call(&self, name: &str, params: &[Value]) -> Result<Vec<Value>, Box<dyn Error>>;
fn context(&self) -> Ref<Ctx>;
fn context_mut(&mut self) -> RefMut<Ctx>;
fn exports(&self) -> ExportsIterator<impl Iterator<Item = (&String, &Export)>>;
fn module(&self) -> Module;
}

View File

@@ -0,0 +1,9 @@
struct Memory {}
impl Memory {
fn new(desc: MemoryDescriptor) -> Result<Self, MemoryError>;
fn descriptor(&self) -> MemoryDescriptor;
fn grow(&self, delta: Pages) -> Result<Pages, MemoryError>;
fn size(&self) -> Pages;
fn view<T: ValueType>(&self) -> MemoryView<T>;
}

View File

@@ -0,0 +1,5 @@
struct MemoryDescriptor {
minimum: Pages,
maximum: Option<Pages>,
shared: bool,
}

View File

@@ -0,0 +1,4 @@
enum MemoryType {
Dynamic,
Static { bound: Pages },
}

View File

@@ -0,0 +1,5 @@
struct MemoryView {}
impl MemoryView {
fn atomically(&self) -> MemoryView<'a, T::Output, Atomically>;
}

View File

@@ -0,0 +1,10 @@
struct Module {}
impl Module {
fn instantiate(&self, import_object: &ImportObject) -> Result<Instance, InstantiationError>;
fn cache(&self) -> Result<Artifact, Infallible>;
fn info(&self) -> &ModuleInfo;
fn imports(&self) -> Vec<ImportDescriptor>;
fn exports(&self) -> Vec<ExportDescriptor>;
fn custom_sections(&self, key: impl AsRef<str>) -> Option<Vec<Vec<u8>>>;
}

View File

@@ -0,0 +1,23 @@
struct ModuleInfo {
custom_sections: IndexMap<String, CustomSectionIndex>,
custom_sections_data: PrimaryMap<CustomSectionIndex, Arc<[u8]>>,
exports: IndexMap<String, ExportIndex>,
func_names: HashMap<FunctionIndex, String>,
functions: PrimaryMap<FunctionIndex, SignatureIndex>,
global_initializers: PrimaryMap<LocalGlobalIndex, GlobalInit>,
globals: PrimaryMap<GlobalIndex, GlobalType>,
id: ModuleId,
imports: IndexMap<(String, String, u32), ImportIndex>,
memories: PrimaryMap<MemoryIndex, MemoryType>,
name: Option<String>,
num_imported_funcs: usize,
num_imported_globals: usize,
num_imported_memories: usize,
num_imported_tables: usize,
passive_data: HashMap<DataIndex, Arc<[u8]>>,
passive_elements: HashMap<ElemIndex, Box<[FunctionIndex]>>,
signatures: PrimaryMap<SignatureIndex, FunctionType>,
start_func: Option<FunctionIndex>,
table_elements: Vec<TableElements>,
tables: PrimaryMap<TableIndex, TableType>,
}

View File

@@ -0,0 +1,7 @@
struct Namespace {}
impl Namespace {
fn new() -> Self;
fn insert<S, E>(&mut self, name: S, export: E);
fn contains_key<S>(&mut self, key: S) -> bool;
}

View File

@@ -0,0 +1,10 @@
trait NativeWasmType {
type Abi: Copy + std::fmt::Debug;
const WASM_TYPE: Type;
fn from_binary(binary: i128) -> Self;
fn to_binary(self) -> i128;
fn into_abi(self) -> Self::Abi;
fn from_abi(abi: Self::Abi) -> Self;
fn to_value<T>(self) -> Value<T>;
}

View File

@@ -0,0 +1,7 @@
struct Pages(pub u32);
impl Pages {
fn checked_add(self, rhs: Self) -> Option<Self>;
fn bytes(self) -> Bytes;
const fn max_values() -> Self;
}

View File

@@ -0,0 +1,10 @@
struct Table {}
impl Table {
fn new(desc: TableDescriptor, initial_value: Value) -> Result<Self, RuntimeError>;
fn descriptor(&self) -> TableDescriptor;
fn set(&self, index: u32, element: Value) -> Result<(), RuntimeError>;
fn get(&self, index: u32) -> Option<Value>;
fn size(&self) -> u32;
fn grow(&self, delta: u32, initial_value: Value) -> Result<u32, RuntimeError>;
}

View File

@@ -0,0 +1,9 @@
struct TableDescriptor {
ty: Type,
minimum: u32,
maximum: Option<u32>,
}
impl TableDescriptor {
fn new(ty: Type, minimum: u32, maximum: Option<u32>) -> Self;
}

View File

@@ -0,0 +1,6 @@
trait WasmExternType {
type Native: NativeWasmType;
fn from_native(native: Self::Native) -> Self;
fn to_native(self) -> Self::Native;
}

View File

@@ -0,0 +1,12 @@
trait WasmTypeList {
type CStruct;
type Array: AsMut<[i128]>;
fn from_array(array: Self::Array) -> Self;
fn empty_array(self) -> Self::Array;
fn from_c_struct(c_struct: Self::CStruct) -> Self;
fn into_c_struct(self) -> Self::CStruct;
fn wasm_types() -> &'static [Type];
fn from_slice(slice: &[i128]) -> Result<Self, TryFromSliceError>;
fn into_array(self) -> Self::Array;
}

View File

@@ -0,0 +1,7 @@
struct WasmHash {}
impl WasmHash {
fn generate(wasm: &[u8]) -> Self;
fn encode(self) -> String;
fn decode(hex_str: &str) -> Result<Self, DeserializeError>;
}

View File

@@ -0,0 +1,7 @@
struct Artifact {}
impl Artifact {
fn deserialize(bytes: &[u8]) -> Result<Self, Error>;
fn info(&self) -> &ModuleInfo;
fn serialize(&self) -> Result<Vec<u8>, Error>;
}

View File

@@ -0,0 +1 @@
struct Bytes(pub usize);

View File

@@ -0,0 +1,5 @@
struct DynamicFunc {}
impl DynamicFunc {
fn new<F>(signature: Arc<FuncSig>, func: F) -> Self;
}

View File

@@ -0,0 +1,7 @@
struct DynFunc {}
impl DynFunc {
fn call(&self, params: &[Value]) -> Result<Vec<Value>, CallError>;
fn signature(&self) -> &FuncSig;
fn raw(&self) -> *const Func;
}

View File

@@ -0,0 +1,4 @@
struct ExportDescriptor<'a> {
name: &'a str,
ty: ExternDescriptor,
}

View File

@@ -0,0 +1,6 @@
struct Exports {}
impl Exports {
fn get<'a, T: Exportable<'a>>(&'a self, name: &str) -> Result<T, ResolveError>;
fn into_iter(&self) -> ExportIter;
}

View File

@@ -0,0 +1,9 @@
struct Func<Args, Rets> {}
impl<Args, Rets> Func<Args, Rets> {
fn new<F>(func: F) -> Self;
fn params(&self) -> &'static [Type];
fn returns(&self) -> &'static [Type];
fn call(...) -> Result<Rets, RuntimeError>;
fn get_vm_func(&self) -> NonNull<Func>;
}

View File

@@ -0,0 +1,8 @@
struct FuncSig {}
impl FuncSig {
fn new<Params, Returns>(params: Params, returns: Returns) -> Self;
fn params(&self) -> &[Type];
fn returns(&self) -> &[Type];
fn check_param_value_types(&self, params: &[Value]) -> bool;
}

View File

@@ -0,0 +1,9 @@
struct Global {}
impl Global {
fn new(value: Value) -> Self;
fn new_mutable(value: Value) -> Self;
fn descriptor(&self) -> GlobalDescriptor;
fn set(&self, value: Value);
fn get(&self) -> Value;
}

View File

@@ -0,0 +1,4 @@
struct GlobalDescriptor {
mutable: bool,
ty: Type,
}

View File

@@ -0,0 +1,4 @@
struct GlobalInit {
desc: GlobalDescriptor,
init: Initializer,
}

View File

@@ -0,0 +1,3 @@
trait HostFunction<Kind, Args, Rets> {
fn to_raw(self) -> (NonNull<Func>, Option<NonNull<FuncEnv>>);
}

View File

@@ -0,0 +1,5 @@
struct ImportDescriptor {
namespace: String,
name: String,
ty: ExternDescriptor,
}

View File

@@ -0,0 +1,12 @@
struct ImportObject {
allow_missing_functions: bool,
}
impl ImportObject {
fn new() -> Self;
fn new_with_data<F>(state_creator: F) -> Self;
fn register<S, N>(&mut self, name: S, namespace: N) -> Option<Box<dyn LikeNamespace>>;
fn with_namespace<Func, InnerRet>(&self, namespace: &str, f: Func) -> Option<InnerRet>;
fn maybe_with_namespace<Func, InnerRet>(&self, namespace: &str, f: Func) -> Option<InnerRet>;
fn contains_namespace(&self, name: &str) -> bool;
}

View File

@@ -0,0 +1,18 @@
struct Instance {
module: Arc<ModuleInner>,
exports: Exports,
}
impl Instance {
fn load<T: Loader>(&self, loader: T) -> Result<T::Instance, T::Error>;
fn fun<Args, Rets>(&self, name: &str) -> ResolveResult<Args, Rets, Wasm>;
fn resolve_func(&self, name: &str) -> ResolveError<usize>;
fn dyn_func(&self, name: &str) -> ResolveResult<DynFunc>;
fn call(&self, name: &str, params: &[Value]) -> CallResult<Vec<Value>>;
fn context(&self) -> &Ctx;
fn context_mut(&mut self) -> &mut Ctx;
fn exports(&self) -> ExportsIter;
fn module(&self) -> Module;
fn get_internal(&self, fields: &InternalField) -> u64;
fn set_internal(&self, fields: &InternalField, value: u64);
}

View File

@@ -0,0 +1,9 @@
struct Memory {}
impl Memory {
fn new(desc: MemoryDescriptor) -> Result<Self, CreationError>;
fn descriptor(&self) -> MemoryDescriptor;
fn grow(&self, delta: Pages) -> Result<Pages, GrowError>;
fn size(&self) -> Pages;
fn view<T: ValueType>(&self) -> MemoryView<T>;
}

View File

@@ -0,0 +1,6 @@
struct MemoryDescriptor {
minimum: Pages,
maximum: Option<Pages>,
shared: bool,
memory_type: MemoryType,
}

View File

@@ -0,0 +1,5 @@
enum MemoryType {
Dynamic,
Static,
SharedStatic,
}

View File

@@ -0,0 +1,5 @@
struct MemoryView {}
impl MemoryView {
fn atomically(&self) -> MemoryView<'a, T::Output, Atomically>;
}

View File

@@ -0,0 +1,10 @@
struct Module {}
impl Module {
fn instantiate(&self, import_object: &ImportObject) -> Result<Instance>;
fn cache(&self) -> Result<Artifact, CacheError>;
fn info(&self) -> &ModuleInfo;
fn imports(&self) -> Vec<ImportDescriptor>;
fn exports(&self) -> Vec<ExportDescriptor>;
fn custom_sections(&self, key: impl AsRef<str>) -> Option<&[Vec<u8>]>
}

View File

@@ -0,0 +1,21 @@
struct ModuleInfo {
backend: String,
custom_sections: HashMap<String, Vec<Vec<u8>>>,
data_initializers: Vec<DataInitializer>,
elem_initializers: Vec<TableInitializer>,
em_symbol_map: Option<HashMap<u32, String>>,
exports: IndexMap<String, ExportIndex>,
func_assoc: Map<FuncIndex, SigIndex>,
generate_debug_info: bool,
globals: Map<LocalGlobalIndex, GlobalInit>,
imported_functions: Map<ImportedFuncIndex, ImportName>,
imported_globals: Map<ImportedGlobalIndex, (ImportName, GlobalDescriptor)>,
imported_memories: Map<ImportedMemoryIndex, (ImportName, MemoryDescriptor)>,
imported_tables: Map<ImportedTableIndex, (ImportName, TableDescriptor)>,
memories: Map<LocalMemoryIndex, MemoryDescripto>,
name_table: StringTable<NameIndex>,
namespace_table: StringTable<NamespaceIndex>,
signatures: Map<SigIndex, FuncSig>,
start_func: Option<FuncIndex>,
tables: Map<LocalTableIndex, TableDescriptor>,
}

View File

@@ -0,0 +1,7 @@
struct Namespace {}
impl Namespace {
fn new() -> Self;
fn insert<S, E>(&mut self, name: S, export: E) -> Option<Box<dyn IsExport + Send>>;
fn contains_key<S>(&mut self, key: S) -> bool;
}

View File

@@ -0,0 +1,6 @@
trait NativeWasmType {
const TYPE: Type;
fn from_binary(bits: u64) -> Self;
fn to_binary(self) -> u64;
}

View File

@@ -0,0 +1,6 @@
struct Pages(pub u32);
impl Pages {
fn checked_add(self, rhs: Self) -> Result<Pages, PageError>;
fn bytes(self) -> Bytes;
}

View File

@@ -0,0 +1,10 @@
struct Table {}
impl Table {
fn new(desc: TableDescriptor) -> Result<Self, CreationError>;
fn descriptor(&self) -> TableDescriptor;
fn set<T: StorableInTable>(&self, index: u32, element: T) -> Result<(), TableAccessError>;
fn size(&self) -> u32;
fn grow(&self, delta: u32) -> Result<u32, GrowError>;
fn vm_local_table(&mut self) -> *mut LocalTable;
}

View File

@@ -0,0 +1,5 @@
struct TableDescriptor {
element: ElementType,
minimum: u32,
maximum: Option<u32>,
}

View File

@@ -0,0 +1,6 @@
trait WasmExternType {
type Native: NativeWasmType;
fn from_native(native: Self::Native) -> Self;
fn to_native(self) -> Self::Native;
}

View File

@@ -0,0 +1,11 @@
trait WasmTypeList {
type CStruct;
type RetArray: AsMut<[u64]>;
fn from_ret_array(array: Self::RetArray) -> Self;
fn empty_ret_array() -> Self::RetArray;
fn from_c_struct(c_struct: Self::CStruct) -> Self;
fn into_c_struct(self) -> Self::CStruct;
fn types() -> &'static [Type];
fn call<Rets>(self, f: NonNull<Func>, wasm: Wasm, ctx: *mut Ctx) -> Result<Rets, RuntimeError>;
}

View File

@@ -0,0 +1,7 @@
struct WasmHash {}
impl WasmHash {
fn generate(wasm: &[u8]) -> Self;
fn encode(self) -> String;
fn decode(hex_str: &str) -> Result<Self, Error>;
}

View File

@@ -0,0 +1,10 @@
#!/bin/bash
for i in `\ls new-api | grep "${1-.}"`; do
clear
echo -e "#######\n### ${i}\n#######\n\n\n\n"
diff --report-identical-files --unified=10 old-api/${i} new-api/${i} | diffr
echo -e "\n\n\n\nPress any key to diff the next file…"
read
done

View File

@@ -0,0 +1,84 @@
use crate::new;
pub use new::wasmer_compiler::{Architecture, CpuFeature as Features};
/// Enum used to select which compiler should be used to generate code.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Backend {
#[cfg(feature = "singlepass")]
/// Singlepass backend
Singlepass,
#[cfg(feature = "cranelift")]
/// Cranelift backend
Cranelift,
#[cfg(feature = "llvm")]
/// LLVM backend
LLVM,
/// Auto backend
Auto,
}
impl Backend {
/// Get a list of the currently enabled (via feature flag) backends.
pub fn variants() -> &'static [&'static str] {
&[
#[cfg(feature = "singlepass")]
"singlepass",
#[cfg(feature = "cranelift")]
"cranelift",
#[cfg(feature = "llvm")]
"llvm",
"auto",
]
}
/// Stable string representation of the backend.
/// It can be used as part of a cache key, for example.
pub fn to_string(&self) -> &'static str {
match self {
#[cfg(feature = "singlepass")]
Backend::Singlepass => "singlepass",
#[cfg(feature = "cranelift")]
Backend::Cranelift => "cranelift",
#[cfg(feature = "llvm")]
Backend::LLVM => "llvm",
Backend::Auto => "auto",
}
}
}
impl Default for Backend {
fn default() -> Self {
#[cfg(feature = "default-backend-singlepass")]
return Backend::Singlepass;
#[cfg(feature = "default-backend-cranelift")]
return Backend::Cranelift;
#[cfg(feature = "default-backend-llvm")]
return Backend::LLVM;
#[cfg(not(any(
feature = "default-backend-singlepass",
feature = "default-backend-cranelift",
feature = "default-backend-llvm",
)))]
panic!("There is no default-backend set.");
}
}
impl std::str::FromStr for Backend {
type Err = String;
fn from_str(s: &str) -> Result<Backend, String> {
match s.to_lowercase().as_str() {
#[cfg(feature = "singlepass")]
"singlepass" => Ok(Backend::Singlepass),
#[cfg(feature = "cranelift")]
"cranelift" => Ok(Backend::Cranelift),
#[cfg(feature = "llvm")]
"llvm" => Ok(Backend::LLVM),
"auto" => Ok(Backend::Auto),
_ => Err(format!("The backend {} doesn't exist", s)),
}
}
}

View File

@@ -0,0 +1,94 @@
//! The cache module provides the common data structures used by compiler backends to allow
//! serializing compiled wasm code to a binary format. The binary format can be persisted,
//! and loaded to allow skipping compilation and fast startup.
use crate::{
get_global_store,
module::{Module, ModuleInfo},
new,
};
use std::str::FromStr;
pub use new::wasmer_cache::FileSystemCache;
/// Kinds of caching errors
#[derive(Debug)]
pub enum Error {
/// An error deserializing bytes into a cache data structure.
DeserializeError(new::wasmer_engine::DeserializeError),
/// An error serializing bytes from a cache data structure.
SerializeError(new::wasmer_engine::SerializeError),
}
/// Artifact are produced by caching, are serialized/deserialized to binaries, and contain
/// module info, backend metadata, and compiled code.
pub struct Artifact {
new_module: new::wasmer::Module,
}
impl Artifact {
pub(crate) fn new(new_module: new::wasmer::Module) -> Self {
Self { new_module }
}
/// Serializes the `Artifact` into a vector of bytes
pub fn serialize(&self) -> Result<Vec<u8>, Error> {
self.new_module.serialize().map_err(Error::SerializeError)
}
/// Deserializes an `Artifact` from the given byte slice.
pub unsafe fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
Ok(Self::new(
new::wasmer::Module::deserialize(&get_global_store(), bytes)
.map_err(Error::DeserializeError)?,
))
}
/// Get the associated module to this artifact.
pub fn module(self) -> Module {
Module::new(self.new_module)
}
/// A reference to the `Artifact`'s stored `ModuleInfo`
pub fn info(&self) -> &ModuleInfo {
self.new_module.info()
}
}
/// The hash of a wasm module.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct WasmHash {
new_hash: new::wasmer_cache::WasmHash,
}
impl WasmHash {
/// Hash a wasm module.
///
/// # Note
///
/// This does no verification that the supplied data
/// is, in fact, a wasm module.
pub fn generate(wasm_bytes: &[u8]) -> Self {
Self {
new_hash: new::wasmer_cache::WasmHash::generate(wasm_bytes),
}
}
/// Create the hexadecimal representation of the
/// stored hash.
pub fn encode(self) -> String {
self.new_hash.to_string()
}
/// Create hash from hexadecimal representation
pub fn decode(hex_str: &str) -> Result<Self, new::wasmer_engine::DeserializeError> {
Ok(Self {
new_hash: new::wasmer_cache::WasmHash::from_str(hex_str)?,
})
}
}
/// A unique ID generated from the version of Wasmer for use with cache versioning
pub const WASMER_VERSION_HASH: &'static str =
include_str!(concat!(env!("OUT_DIR"), "/wasmer_version_hash.txt"));

View File

@@ -0,0 +1,3 @@
pub use crate::new::wasmer::{ExportError, MemoryError};
pub use crate::new::wasmer_compiler::CompileError;
pub use crate::new::wasmer_engine::{InstantiationError, LinkError, RuntimeError};

View File

@@ -0,0 +1,4 @@
pub use crate::new::{
wasmer::{Exportable, Extern as Export},
wasmer_vm::Export as RuntimeExport,
};

View File

@@ -0,0 +1,110 @@
use crate::{
backend::Backend, cache::Artifact, get_global_store, module::Module, new,
renew_global_store_with,
};
use std::{convert::Infallible, error::Error};
pub use new::wasmer::wat2wasm;
/// Compile WebAssembly binary code into a [`Module`].
/// This function is useful if it is necessary to
/// compile a module before it can be instantiated
/// (otherwise, the [`instantiate`] function should be used).
///
/// [`Module`]: struct.Module.html
/// [`instantiate`]: fn.instantiate.html
///
/// # Params
///
/// * `wasm`: A `&[u8]` containing the
/// binary code of the wasm module you want to compile.
///
/// # Errors
///
/// If the operation fails, the function returns `Err(error::CompileError::...)`.
///
/// This function only exists if one of `default-backend-llvm`, `default-backend-cranelift`,
/// or `default-backend-singlepass` is set.
pub fn compile(bytes: &[u8]) -> Result<Module, Box<dyn Error>> {
compile_with(bytes, Backend::Auto)
}
/// Creates a new module from the given cache [`Artifact`]
pub fn load_cache_with(cache: Artifact) -> Result<Module, Infallible> {
Ok(cache.module())
}
/// Compile a [`Module`] using the provided compiler from
/// WebAssembly binary code. This function is useful if it
/// is necessary to a compile a module before it can be instantiated
/// and must be used if you wish to use a different backend from the default.
pub fn compile_with(bytes: &[u8], compiler: Backend) -> Result<Module, Box<dyn Error>> {
renew_global_store_with(compiler);
Ok(Module::new(new::wasmer::Module::new(
&get_global_store(),
bytes,
)?))
}
/// The same as `compile_with` but changes the compiler behavior
/// with the values in the `CompilerConfig`
///
/// # Note
///
/// The third parameter isn't used any more in the deprecated version
/// of `wasmer-runtime-core`.
pub fn compile_with_config(
bytes: &[u8],
compiler: Backend,
_compiler_config: (),
) -> Result<Module, Box<dyn Error>> {
renew_global_store_with(compiler);
Ok(Module::new(new::wasmer::Module::new(
&get_global_store(),
bytes,
)?))
}
/// Perform validation as defined by the
/// WebAssembly specification. Returns `true` if validation
/// succeeded, `false` if validation failed.
pub fn validate(bytes: &[u8]) -> bool {
new::wasmer::Module::validate(&get_global_store(), bytes).is_ok()
}
/// Helper macro to create a new `Func` object using the provided function pointer.
///
/// # Usage
///
/// Function pointers or closures are supported. Closures can capture
/// their environment (with `move`). The first parameter is of kind
/// `vm::Ctx`.
///
/// ```
/// # use wasmer_runtime_core::{imports, func, vm};
///
/// // A host function.
/// fn func(ctx: &mut vm::Ctx, n: i32) -> i32 {
/// n
/// }
///
/// let i = 7;
///
/// let import_object = imports! {
/// "env" => {
/// "foo" => func!(func),
/// // A closure with a captured environment.
/// "bar" => func!(move |_: &mut vm::Ctx, n: i32| -> i32 {
/// n + i
/// }),
/// },
/// };
/// ```
#[macro_export]
macro_rules! func {
($function:expr) => {
$crate::typed_func::Func::new($function)
};
}

View File

@@ -0,0 +1,105 @@
use crate::{
error::ExportError,
get_global_store, new,
types::{GlobalDescriptor, Value},
};
use std::fmt;
/// A handle to a Wasm Global
#[derive(Clone)]
pub struct Global {
new_global: new::wasmer::Global,
}
impl Global {
/// Create a new `Global` value.
///
/// # Example
///
/// ```
/// # use wasmer_runtime_core::{global::Global, types::Value};
/// let global = Global::new(Value::I32(42));
/// ```
pub fn new(value: Value) -> Self {
Self {
new_global: new::wasmer::Global::new(&get_global_store(), value),
}
}
/// Create a new, mutable `Global` value.
///
/// # Example
///
/// ```
/// # use wasmer_runtime_core::{global::Global, types::Value};
/// let global = Global::new_mutable(Value::I32(42));
/// ```
pub fn new_mutable(value: Value) -> Self {
Self {
new_global: new::wasmer::Global::new_mut(&get_global_store(), value),
}
}
/// Get the [`GlobalDescriptor`] generated for this global.
///
/// [`GlobalDescriptor`]: struct.GlobalDescriptor.html
pub fn descriptor(&self) -> GlobalDescriptor {
self.new_global.ty().into()
}
/// Set the value help by this global.
///
/// This method will panic if the value is
/// the wrong type.
pub fn set(&self, value: Value) {
self.new_global.set(value).unwrap()
}
/// Get the value held by this global.
///
/// # Example
///
/// ```
/// # use wasmer_runtime_core::{global::Global, types::Value};
/// let global = Global::new_mutable(Value::I32(42));
/// assert_eq!(global.get(), Value::I32(42));
///
/// global.set(Value::I32(7));
/// assert_eq!(global.get(), Value::I32(7));
/// ```
pub fn get(&self) -> Value {
self.new_global.get()
}
}
impl fmt::Debug for Global {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "{:?}", &self.new_global)
}
}
impl From<&new::wasmer::Global> for Global {
fn from(new_global: &new::wasmer::Global) -> Self {
Self {
new_global: new_global.clone(),
}
}
}
impl<'a> new::wasmer::Exportable<'a> for Global {
fn to_export(&self) -> new::wasmer_vm::Export {
self.new_global.to_export()
}
fn get_self_from_extern(r#extern: &'a new::wasmer::Extern) -> Result<&'a Self, ExportError> {
match r#extern {
new::wasmer::Extern::Global(global) => Ok(
// It's not ideal to call `Box::leak` here, but it
// would introduce too much changes in the
// `new::wasmer` API to support `Cow` or similar.
Box::leak(Box::<Global>::new(global.into())),
),
_ => Err(ExportError::IncompatibleType),
}
}
}

View File

@@ -0,0 +1,215 @@
use crate::{instance::Exports, new};
pub use new::wasmer::{namespace, ImportObject, ImportObjectIterator, LikeNamespace};
pub struct Namespace {
exports: Exports,
}
impl Namespace {
pub fn new() -> Self {
Self {
exports: Exports::new(),
}
}
pub fn insert<N, V>(&mut self, name: N, value: V)
where
N: Into<String>,
V: Into<new::wasmer::Extern> + 'static,
{
self.exports.new_exports.insert(name, value);
}
pub fn contains_key<N>(&mut self, name: N) -> bool
where
N: Into<String>,
{
self.exports.new_exports.contains(name)
}
}
impl LikeNamespace for Namespace {
fn get_namespace_export(&self, name: &str) -> Option<new::wasmer_vm::Export> {
self.exports.new_exports.get_namespace_export(name)
}
fn get_namespace_exports(&self) -> Vec<(String, new::wasmer_vm::Export)> {
self.exports.new_exports.get_namespace_exports()
}
}
#[deprecated(
since = "__NEXT_VERSION__",
note = "Please use the `Exportable` trait instead."
)]
pub trait IsExport {}
/// Generate an `ImportObject` easily with the `imports!` macro.
///
/// # Usage
///
/// ```
/// # use wasmer_runtime_core::{imports, func, vm::Ctx};
///
/// let import_object = imports! {
/// "env" => {
/// "foo" => func!(foo)
/// },
/// };
///
/// fn foo(_: &mut Ctx, n: i32) -> i32 {
/// n
/// }
/// ```
///
/// or by passing a state creator for the import object:
///
/// ```
/// # use wasmer_runtime_core::{imports, func, vm::Ctx};
///
/// let import_object = imports! {
/// || (0 as _, |_a| {}),
/// "env" => {
/// "foo" => func!(foo)
/// },
/// };
///
/// # fn foo(_: &mut Ctx, n: i32) -> i32 {
/// # n
/// # }
/// ```
#[macro_export]
macro_rules! imports {
( $( $namespace_name:expr => $namespace:tt ),* $(,)? ) => {
{
let mut import_object = $crate::import::ImportObject::new();
$({
let namespace = $crate::import_namespace!($namespace);
import_object.register($namespace_name, namespace);
})*
import_object
}
};
($state_creator:expr, $( $namespace_name:expr => $namespace:tt ),* $(,)? ) => {
{
let mut import_object = $crate::import::ImportObject::new_with_data($state_creator);
$({
let namespace = $crate::import_namespace!($namespace);
import_object.register($namespace_name, namespace);
})*
import_object
}
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! import_namespace {
( { $( $import_name:expr => $import_item:expr ),* $(,)? } ) => {
{
let mut namespace = $crate::import::Namespace::new();
$(
namespace.insert($import_name, $import_item);
)*
namespace
}
};
( $namespace:ident ) => {
$namespace
};
}
#[cfg(test)]
mod test {
use crate::{func, vm};
fn func(_: &mut vm::Ctx, arg: i32) -> i32 {
arg + 1
}
#[test]
fn imports_macro_allows_trailing_comma_and_none() {
let _ = imports! {
"env" => {
"func" => func!(func),
},
};
let _ = imports! {
"env" => {
"func" => func!(func),
}
};
let _ = imports! {
"env" => {
"func" => func!(func),
},
"abc" => {
"def" => func!(func),
}
};
let _ = imports! {
"env" => {
"func" => func!(func)
},
};
let _ = imports! {
"env" => {
"func" => func!(func)
}
};
let _ = imports! {
"env" => {
"func1" => func!(func),
"func2" => func!(func)
}
};
let _ = imports! {
"env" => {
"func1" => func!(func),
"func2" => func!(func),
}
};
}
#[test]
fn imports_macro_allows_trailing_comma_and_none_with_state() {
use std::{ffi, ptr};
fn dtor(_arg: *mut ffi::c_void) {}
fn state_creator() -> (*mut ffi::c_void, fn(*mut ffi::c_void)) {
(ptr::null_mut() as *mut ffi::c_void, dtor)
}
let _ = imports! {
state_creator,
"env" => {
"func1" => func!(func),
"func2" => func!(func),
}
};
let _ = imports! {
state_creator,
"env" => {
"func1" => func!(func),
"func2" => func!(func)
},
};
let _ = imports! {
state_creator,
"env" => {
"func1" => func!(func),
"func2" => func!(func),
},
};
}
}

View File

@@ -0,0 +1,272 @@
use crate::{
error::ExportError,
export::{Export, Exportable},
import::LikeNamespace,
module::{ExportIndex, Module},
new,
structures::TypedIndex,
typed_func::Func,
types::Value,
vm,
};
use std::{
cell::{Ref, RefCell, RefMut},
error::Error,
rc::Rc,
};
pub use crate::typed_func::DynamicFunc as DynFunc;
#[derive(Debug)]
pub(crate) struct PreInstance {
pub(crate) vmctx: Rc<RefCell<vm::Ctx>>,
}
impl PreInstance {
pub(crate) fn new() -> Self {
Self {
vmctx: Rc::new(RefCell::new(unsafe { vm::Ctx::new_uninit() })),
}
}
pub(crate) fn vmctx(&self) -> Rc<RefCell<vm::Ctx>> {
self.vmctx.clone()
}
pub(crate) fn vmctx_ptr(&self) -> *mut vm::Ctx {
self.vmctx.as_ptr()
}
}
/// An instantiated WebAssembly module.
///
/// An `Instance` represents a WebAssembly module that
/// has been instantiated with an [`ImportObject`] and is
/// ready to be called.
///
/// [`ImportObject`]: struct.ImportObject.html
pub struct Instance {
pre_instance: Box<PreInstance>,
/// The exports of this instance.
pub exports: Exports,
pub(crate) new_instance: new::wasmer::Instance,
}
impl Instance {
pub(crate) fn new(pre_instance: Box<PreInstance>, new_instance: new::wasmer::Instance) -> Self {
// Initialize the `vm::Ctx`
{
let mut vmctx = pre_instance.vmctx.borrow_mut();
vmctx.module_info = new_instance.module().info() as *const _;
}
Self {
pre_instance,
exports: new_instance.exports.clone().into(),
new_instance,
}
}
/// Through generic magic and the awe-inspiring power of traits, we bring you...
///
/// # "Func"
///
/// A [`Func`] allows you to call functions exported from wasm with
/// near zero overhead.
///
/// [`Func`]: struct.Func.html
///
/// # Usage
///
/// ```
/// # #![allow(deprecated)]
/// # use wasmer_runtime_core::{Func, Instance, error::ExportError};
/// # fn typed_func(instance: Instance) -> Result<(), ExportError> {
/// let func: Func<(i32, i32)> = instance.func("foo")?;
///
/// func.call(42, 43);
/// # Ok(())
/// # }
/// ```
#[deprecated(
since = "0.17.0",
note = "Please use `instance.exports.get(name)` instead"
)]
pub fn func<Args, Rets>(&self, name: &str) -> Result<Func<Args, Rets>, ExportError>
where
Args: new::wasmer::WasmTypeList + Clone,
Rets: new::wasmer::WasmTypeList + Clone,
{
self.exports.get(name)
}
/// This returns the representation of a function that can be called
/// safely.
///
/// # Usage
///
/// ```
/// # #![allow(deprecated)]
/// # use wasmer_runtime_core::{Instance};
/// # fn call_foo(instance: &mut Instance) -> Result<(), Box<dyn std::error::Error>> {
/// instance
/// .dyn_func("foo")?
/// .call(&[])?;
/// # Ok(())
/// # }
/// ```
#[deprecated(
since = "0.17.0",
note = "Please use `instance.exports.get(name)` instead"
)]
pub fn dyn_func(&self, name: &str) -> Result<DynFunc, ExportError> {
self.exports.get(name)
}
/// Resolve an exported function by name.
pub fn resolve_func(&self, name: &str) -> Result<usize, ()> {
self.new_instance
.module()
.info()
.exports
.iter()
.find_map(|(export_name, export_index)| {
if name == export_name {
match export_index {
ExportIndex::Function(index) => Some(index.index()),
_ => None,
}
} else {
None
}
})
.ok_or(())
}
/// Call an exported WebAssembly function given the export name.
/// Pass arguments by wrapping each one in the [`Value`] enum.
/// The returned values are also each wrapped in a [`Value`].
///
/// [`Value`]: enum.Value.html
///
/// # Note
///
/// This returns `Result<Vec<Value>, _>` in order to support
/// the future multi-value returns WebAssembly feature.
///
/// # Usage
///
/// Consider using the more explicit [`Exports::get`]` with [`DynFunc::call`]
/// instead. For example:
///
/// ```
/// # use wasmer_runtime_core::{types::Value, Instance, DynFunc};
/// # fn call_foo(instance: &mut Instance) -> Result<(), Box<dyn std::error::Error>> {
/// // …
/// let foo: DynFunc = instance.exports.get("foo")?;
/// let results = foo.call(&[Value::I32(42)])?;
/// // …
/// # Ok(())
/// # }
/// ```
///
/// Another example with `Instance::call` directly:
///
/// ```
/// # use wasmer_runtime_core::{types::Value, Instance};
/// # fn call_foo(instance: &mut Instance) -> Result<(), Box<dyn std::error::Error>> {
/// // ...
/// let results = instance.call("foo", &[Value::I32(42)])?;
/// // ...
/// # Ok(())
/// # }
/// ```
pub fn call(&self, name: &str, params: &[Value]) -> Result<Vec<Value>, Box<dyn Error>> {
Ok(self
.new_instance
.exports
.get_function(name)?
.call(params)?
.into_vec())
}
/// The module used to instantiate this Instance.
pub fn module(&self) -> Module {
Module::new(self.new_instance.module().clone())
}
/// Returns an immutable reference to the
/// [`Ctx`] used by this Instance.
///
/// [`Ctx`]: struct.Ctx.html
pub fn context(&self) -> Ref<vm::Ctx> {
self.pre_instance.vmctx.borrow()
}
/// Returns a mutable reference to the
/// [`Ctx`] used by this Instance.
///
/// [`Ctx`]: struct.Ctx.html
pub fn context_mut(&mut self) -> RefMut<vm::Ctx> {
self.pre_instance.vmctx.borrow_mut()
}
/// Returns an iterator over all of the items
/// exported from this instance.
pub fn exports(
&self,
) -> new::wasmer::ExportsIterator<impl Iterator<Item = (&String, &Export)>> {
self.new_instance.exports.iter()
}
}
impl LikeNamespace for Instance {
fn get_namespace_export(&self, name: &str) -> Option<new::wasmer_vm::Export> {
self.exports.new_exports.get_namespace_export(name)
}
fn get_namespace_exports(&self) -> Vec<(String, new::wasmer_vm::Export)> {
self.exports.new_exports.get_namespace_exports()
}
}
#[derive(Clone)]
pub struct Exports {
pub(crate) new_exports: new::wasmer::Exports,
}
impl Exports {
pub(crate) fn new() -> Self {
Self {
new_exports: new::wasmer::Exports::new(),
}
}
pub fn get<'a, T>(&'a self, name: &str) -> Result<T, ExportError>
where
T: Exportable<'a> + Clone + 'a,
{
Ok(self.new_exports.get::<T>(name)?.clone())
}
pub fn iter(&self) -> new::wasmer::ExportsIterator<impl Iterator<Item = (&String, &Export)>> {
self.new_exports.iter()
}
}
impl LikeNamespace for Exports {
fn get_namespace_export(&self, name: &str) -> Option<new::wasmer_vm::Export> {
self.new_exports.get_namespace_export(name)
}
fn get_namespace_exports(&self) -> Vec<(String, new::wasmer_vm::Export)> {
self.new_exports.get_namespace_exports()
}
}
impl From<new::wasmer::Exports> for Exports {
fn from(new_exports: new::wasmer::Exports) -> Self {
Self { new_exports }
}
}

View File

@@ -0,0 +1,161 @@
//! Wasmer Runtime Core Library
//!
//! # Important Note; Please Read
//!
//! Wasmer has entirely changed its API (called the “new API”
//! here). This new version of Wasmer improves the performance and the
//! memory consumption, in addition to a ton of new features and much
//! more flexibility! In order to help users to enjoy the performance
//! boost and memory improvements without updating your program that
//! much, we have created a new version of the `wasmer-runtime-core`
//! crate, which is now *a port* of the new API but with the old API,
//! as much as possible. Indeed, it was not always possible to provide
//! the exact same API, but changes are subtle.
//!
//! We have carefully documented most of the differences. It is
//! important to understand the public of this port (see the
//! `CHANGES.md`) document. We do not recommend to advanced users of
//! Wasmer to use this port. Advanced API, like `ModuleInfo` or the
//! `vm` module (incl. `vm::Ctx`) have not been fully ported because
//! it was very internals to Wasmer. For advanced users, we highly
//! recommend to migrate to the new version of Wasmer, which is
//! awesome by the way (completely neutral opinion). The public for
//! this port is beginners or regular users that do not necesarily
//! have time to update their code immediately but that want to enjoy
//! a performance boost and memory improvements.
//!
//! # Introduction
//!
//! This crate provides common data structures which are shared by
//! compiler backends to implement a WebAssembly runtime.
//!
//! This crate also provides an API for users who use wasmer as an
//! embedded wasm runtime which allows operations like compiling,
//! instantiating, providing imports, access exports, memories, and
//! tables for example.
//!
//! Most wasmer users should prefer the API which is re-exported by
//! the `wasmer-runtime` library by default. This crate provides
//! additional APIs which may be useful to users that wish to
//! customize the wasmer runtime.
pub(crate) mod new {
pub use wasm_common;
pub use wasmer;
pub use wasmer_cache;
pub use wasmer_compiler;
#[cfg(feature = "cranelift")]
pub use wasmer_compiler_cranelift;
#[cfg(feature = "llvm")]
pub use wasmer_compiler_llvm;
#[cfg(feature = "singlepass")]
pub use wasmer_compiler_singlepass;
pub use wasmer_engine;
pub use wasmer_engine_jit;
pub use wasmer_vm;
}
pub mod backend;
pub mod cache;
pub mod error;
pub mod export;
mod functional_api;
pub mod global;
pub mod import;
pub mod instance;
pub mod memory;
pub mod module;
pub mod structures;
pub mod table;
pub mod typed_func;
pub mod types;
pub mod units;
pub mod vm;
pub use crate::cache::{Artifact, WasmHash};
#[allow(deprecated)]
pub use crate::import::IsExport;
pub use crate::instance::{DynFunc, Exports, Instance};
pub use crate::module::Module;
pub use crate::new::wasmer_compiler::wasmparser;
pub use crate::typed_func::{DynamicFunc, Func};
pub use crate::units::{Bytes, Pages, WASM_MAX_PAGES, WASM_MIN_PAGES, WASM_PAGE_SIZE};
pub use functional_api::{
compile, compile_with, compile_with_config, load_cache_with, validate, wat2wasm,
};
pub mod prelude {
pub use crate::import::{namespace, ImportObject, Namespace};
pub use crate::typed_func::{DynamicFunc, Func};
pub use crate::types::{FuncIndex, GlobalIndex, MemoryIndex, TableIndex, Type, Value};
}
/// The current version of this crate
pub const VERSION: &'static str = env!("CARGO_PKG_VERSION");
use backend::Backend;
use std::sync::{Arc, Mutex};
struct GlobalStore {
store: Mutex<Arc<new::wasmer::Store>>,
backend: Backend,
}
impl GlobalStore {
fn new() -> Self {
Self {
store: Mutex::new(Arc::new(Default::default())),
backend: Backend::Auto,
}
}
fn renew_with(&self, compiler: backend::Backend) {
if compiler == self.backend {
return;
}
#[allow(unused_variables)]
let update = |compiler_config: &dyn new::wasmer_compiler::CompilerConfig,
global_store: &GlobalStore| {
let engine = new::wasmer_engine_jit::JIT::new(compiler_config).engine();
*self.store.lock().unwrap() = Arc::new(new::wasmer::Store::new(&engine));
};
match compiler {
#[cfg(feature = "singlepass")]
Backend::Singlepass => update(
&new::wasmer_compiler_singlepass::Singlepass::default(),
&self,
),
#[cfg(feature = "cranelift")]
Backend::Cranelift => {
update(&new::wasmer_compiler_cranelift::Cranelift::default(), &self)
}
#[cfg(feature = "llvm")]
Backend::LLVM => update(&new::wasmer_compiler_llvm::LLVM::default(), &self),
Backend::Auto => *self.store.lock().unwrap() = Arc::new(Default::default()),
};
}
fn inner_store(&self) -> Arc<new::wasmer::Store> {
(*self.store.lock().unwrap()).clone()
}
}
lazy_static::lazy_static! {
static ref GLOBAL_STORE: GlobalStore = GlobalStore::new();
}
/// Useful if one needs to update the global store.
pub(crate) fn renew_global_store_with(backend: Backend) {
GLOBAL_STORE.renew_with(backend);
}
/// Useful if one needs to migrate to the new Wasmer's API gently.
pub fn get_global_store() -> Arc<new::wasmer::Store> {
GLOBAL_STORE.inner_store()
}

View File

@@ -0,0 +1,139 @@
use crate::{
error::{ExportError, MemoryError},
get_global_store, new,
types::ValueType,
units::Pages,
};
pub mod ptr {
pub use crate::new::wasmer::{Array, Item, WasmPtr};
}
pub use new::wasm_common::MemoryType as MemoryDescriptor;
pub use new::wasmer::{Atomically, MemoryView};
pub use new::wasmer_vm::MemoryStyle as MemoryType;
/// A Wasm linear memory.
///
/// A `Memory` represents the memory used by a Wasm instance.
#[derive(Clone)]
pub struct Memory {
new_memory: new::wasmer::Memory,
}
impl Memory {
/// Create a new `Memory` from a [`MemoryDescriptor`]
///
/// [`MemoryDescriptor`]: struct.MemoryDescriptor.html
///
/// Usage:
///
/// ```
/// # use wasmer_runtime_core::{types::MemoryDescriptor, error::MemoryError, memory::Memory, units::Pages};
/// fn create_memory() -> Result<(), MemoryError> {
/// let descriptor = MemoryDescriptor::new(Pages(10), None, false);
///
/// let memory = Memory::new(descriptor)?;
/// Ok(())
/// }
/// ```
pub fn new(descriptor: MemoryDescriptor) -> Result<Self, MemoryError> {
Ok(Memory {
new_memory: new::wasmer::Memory::new(&get_global_store(), descriptor)?,
})
}
/// Return the [`MemoryDescriptor`] that this memory
/// was created with.
///
/// [`MemoryDescriptor`]: struct.MemoryDescriptor.html
pub fn descriptor(&self) -> MemoryDescriptor {
self.new_memory.ty().clone()
}
/// Grow this memory by the specified number of pages.
pub fn grow(&self, delta: Pages) -> Result<Pages, MemoryError> {
self.new_memory.grow(delta)
}
/// The size, in wasm pages, of this memory.
pub fn size(&self) -> Pages {
self.new_memory.size()
}
/// 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 [`atomically`] method.
///
/// [`atomically`]: struct.MemoryView.html#method.atomically
///
/// # 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_runtime_core::memory::{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> {
self.new_memory.view()
}
}
impl From<&new::wasmer::Memory> for Memory {
fn from(new_memory: &new::wasmer::Memory) -> Self {
Self {
new_memory: new_memory.clone(),
}
}
}
impl<'a> new::wasmer::Exportable<'a> for Memory {
fn to_export(&self) -> new::wasmer_vm::Export {
self.new_memory.to_export()
}
fn get_self_from_extern(r#extern: &'a new::wasmer::Extern) -> Result<&'a Self, ExportError> {
match r#extern {
new::wasmer::Extern::Memory(memory) => Ok(
// It's not ideal to call `Box::leak` here, but it
// would introduce too much changes in the
// `new::wasmer` API to support `Cow` or similar.
Box::leak(Box::<Memory>::new(memory.into())),
),
_ => Err(ExportError::IncompatibleType),
}
}
}
#[cfg(test)]
mod memory_tests {
use super::{Memory, MemoryDescriptor, Pages};
#[test]
fn test_initial_memory_size() {
let memory_desc = MemoryDescriptor::new(Pages(10), Some(Pages(20)), false);
let unshared_memory = Memory::new(memory_desc).unwrap();
assert_eq!(unshared_memory.size(), Pages(10));
}
}

View File

@@ -0,0 +1,241 @@
use crate::{
cache::Artifact,
error::{InstantiationError, RuntimeError},
import::{ImportObject, Namespace},
instance::{Instance, PreInstance},
new,
typed_func::DynamicCtx,
types::{FuncSig, Value},
vm,
};
use new::wasmer_vm::Export;
use std::{
cell::RefCell,
collections::HashMap,
convert::{AsRef, Infallible},
ptr,
};
pub use new::wasm_common::{DataInitializer, ExportIndex};
pub use new::wasmer_vm::{
//
MemoryStyle as MemoryType,
ModuleInfo,
TableElements as TableInitializer,
};
/// A compiled WebAssembly module.
///
/// `Module` is returned by the [`compile`] function.
///
/// [`compile`]: crate::compile
#[derive(Clone)]
pub struct Module {
pub(crate) new_module: new::wasmer::Module,
}
impl Module {
pub(crate) fn new(new_module: new::wasmer::Module) -> Self {
Self { new_module }
}
/// Instantiate a WebAssembly module with the provided [`ImportObject`].
///
/// [`ImportObject`]: struct.ImportObject.html
///
/// # Note
///
/// Instantiating a `Module` will also call the function designated as `start`
/// in the WebAssembly module, if there is one.
///
/// # Usage
///
/// ```
/// # use wasmer_runtime_core::{Module, imports, error::InstantiationError};
/// # fn instantiate(module: &Module) -> Result<(), InstantiationError> {
/// let import_object = imports! {
/// // ...
/// };
/// let instance = module.instantiate(&import_object)?;
/// // ...
/// # Ok(())
/// # }
/// ```
pub fn instantiate(
&self,
import_object: &ImportObject,
) -> Result<Instance, InstantiationError> {
let pre_instance = Box::new(PreInstance::new());
let import_object = {
// The problem is the following:
//
// * In the old API, `Instance` owns the host functions'
// environments of kind `vm::Ctx`, and mutably shares it
// with all host functions.
// * In the new API, every host function owns its env of
// any kind; `Instance` knows nothing about this
// environment.
//
// To reproduce the old API with the new API, host
// functions create an empty environment of kind
// `vm::Ctx`. It is stored internally behind a `VMContext`
// pointer. The hack consists of rebuilding an
// `ImportObject` (that holds all the host functions), and
// updates the `VMContext` pointer to use a shared
// `vm::Ctx` value owned by `Instance` (actually,
// `PreInstance`).
let mut new_import_object = ImportObject::new();
let mut new_namespaces: HashMap<String, Namespace> = HashMap::new();
let store = self.new_module.store();
import_object
.clone_ref()
.into_iter()
.map(|((namespace, name), export)| match export {
Export::Function(mut function) => {
{
// `function` is a static host function
// constructed with
// `new::wasmer::Function::new_env`.
if !function.address.is_null() {
// Properly drop the empty `vm::Ctx`
// created by the host function.
unsafe {
ptr::drop_in_place::<vm::Ctx>(function.vmctx as _);
}
// Update the pointer to `VMContext`,
// which is actually a `vm::Ctx`
// pointer, to fallback on the
// environment hack.
function.vmctx = pre_instance.vmctx_ptr() as _;
}
// `function` is a dynamic host function
// constructed with
// `new::wasmer::Function::new_dynamic_env`.
else {
// `VMContext` holds a complex type:
// `Box<VMDynamicFunctionContext<VMDynamicFunctionWithEnv<DynamicCtx>>>`.
//
// The type `VMDynamicFunctionWithEnv`
// is private to `new::wasmer`. Let's
// replicate it, and hope the layout
// is the same!
struct VMDynamicFunctionWithEnv<Env>
where
Env: Sized + 'static,
{
#[allow(unused)]
function_type: FuncSig,
#[allow(unused)]
func: Box<
dyn Fn(
&mut Env,
&[Value],
)
-> Result<Vec<Value>, RuntimeError>
+ 'static,
>,
env: RefCell<Env>,
}
// Get back the `vmctx` as it is
// stored by
// `new::wasmer::Function::new_dynamic_env`.
let vmctx: Box<
new::wasmer_vm::VMDynamicFunctionContext<
VMDynamicFunctionWithEnv<DynamicCtx>,
>,
> = unsafe { Box::from_raw(function.vmctx as *mut _) };
// Replace the environment by ours.
vmctx.ctx.env.borrow_mut().vmctx = pre_instance.vmctx();
// … without anyone noticing…
function.vmctx = Box::into_raw(vmctx) as _;
}
}
(
(namespace, name),
new::wasmer::Extern::from_export(store, Export::Function(function)),
)
}
export => (
(namespace, name),
new::wasmer::Extern::from_export(store, export),
),
})
.for_each(|((namespace, name), extern_)| {
if !new_namespaces.contains_key(&namespace) {
new_namespaces.insert(namespace.clone(), Namespace::new());
}
let new_namespace = new_namespaces.get_mut(&namespace).unwrap(); // it is safe because it has been verified that the key exists.
new_namespace.insert(&name, extern_);
});
new_namespaces
.into_iter()
.for_each(|(namespace_name, namespace)| {
new_import_object.register(namespace_name, namespace);
});
new_import_object
};
Ok(Instance::new(
pre_instance,
new::wasmer::Instance::new(&self.new_module, &import_object)?,
))
}
/// Create a cache artifact from this module.
pub fn cache(&self) -> Result<Artifact, Infallible> {
Ok(Artifact::new(self.new_module.clone()))
}
/// Get the module data for this module.
pub fn info(&self) -> &ModuleInfo {
&self.new_module.info()
}
/// Get the [`ImportDescriptor`]s describing the imports this [`Module`]
/// requires to be instantiated.
pub fn imports(&self) -> Vec<crate::types::ImportDescriptor> {
self.new_module.imports().collect()
}
/// Get the [`ExportDescriptor`]s of the exports this [`Module`] provides.
pub fn exports(&self) -> Vec<crate::types::ExportDescriptor> {
self.new_module.exports().collect()
}
/// Get the custom sections matching the given name.
pub fn custom_sections(&self, name: impl AsRef<str>) -> Option<Vec<Vec<u8>>> {
let custom_sections: Vec<Vec<u8>> = self
.new_module
.custom_sections(name.as_ref())
.map(|custom_section| custom_section.to_vec())
.collect();
if custom_sections.is_empty() {
None
} else {
Some(custom_sections)
}
}
#[doc(hidden)]
pub fn into_inner(&self) -> new::wasmer::Module {
self.new_module.clone()
}
}
impl Into<new::wasmer::Module> for Module {
fn into(self) -> new::wasmer::Module {
self.into_inner()
}
}

View File

@@ -0,0 +1 @@
pub use crate::new::wasm_common::entity::EntityRef as TypedIndex;

View File

@@ -0,0 +1,108 @@
use crate::{
error::{ExportError, RuntimeError},
get_global_store, new,
types::{TableDescriptor, Value},
};
/// Container with a descriptor and a reference to a table storage.
#[derive(Clone)]
pub struct Table {
new_table: new::wasmer::Table,
}
impl Table {
/// Create a new `Table` from a [`TableDescriptor`]
///
/// [`TableDescriptor`]: struct.TableDescriptor.html
///
/// # Usage
///
/// ```
/// # use wasmer_runtime_core::{types::{TableDescriptor, Type, Value}, table::Table, error::RuntimeError};
/// # fn create_table() -> Result<(), RuntimeError> {
/// let descriptor = TableDescriptor {
/// ty: Type::ExternRef,
/// minimum: 10,
/// maximum: None,
/// };
///
/// let table = Table::new(descriptor, Value::null())?;
/// # Ok(())
/// # }
/// ```
pub fn new(descriptor: TableDescriptor, initial_value: Value) -> Result<Self, RuntimeError> {
Ok(Self {
new_table: new::wasmer::Table::new(&get_global_store(), descriptor, initial_value)?,
})
}
/// Get the `TableDescriptor` used to create this `Table`.
pub fn descriptor(&self) -> TableDescriptor {
self.new_table.ty().clone()
}
/// Set the element at index.
pub fn set(&self, index: u32, value: Value) -> Result<(), RuntimeError> {
self.new_table.set(index, value)
}
pub fn get(&self, index: u32) -> Option<Value> {
self.new_table.get(index)
}
/// The current size of this table.
pub fn size(&self) -> u32 {
self.new_table.size()
}
/// Grow this table by `delta`.
pub fn grow(&self, delta: u32, initial_value: Value) -> Result<u32, RuntimeError> {
self.new_table.grow(delta, initial_value)
}
}
impl From<&new::wasmer::Table> for Table {
fn from(new_table: &new::wasmer::Table) -> Self {
Self {
new_table: new_table.clone(),
}
}
}
impl<'a> new::wasmer::Exportable<'a> for Table {
fn to_export(&self) -> new::wasmer_vm::Export {
self.new_table.to_export()
}
fn get_self_from_extern(r#extern: &'a new::wasmer::Extern) -> Result<&'a Self, ExportError> {
match r#extern {
new::wasmer::Extern::Table(table) => Ok(
// It's not ideal to call `Box::leak` here, but it
// would introduce too much changes in the
// `new::wasmer` API to support `Cow` or similar.
Box::leak(Box::<Table>::new(table.into())),
),
_ => Err(ExportError::IncompatibleType),
}
}
}
#[cfg(test)]
mod table_tests {
use super::{Table, TableDescriptor};
use crate::types::{Type, Value};
#[test]
fn test_initial_table_size() {
let table = Table::new(
TableDescriptor {
ty: Type::FuncRef,
minimum: 10,
maximum: Some(20),
},
Value::null(),
)
.unwrap();
assert_eq!(table.size(), 10);
}
}

View File

@@ -0,0 +1,409 @@
use crate::{
error::{ExportError, RuntimeError},
get_global_store, new,
types::{FuncSig, NativeWasmType, Type, Value, WasmExternType},
vm,
};
use std::marker::PhantomData;
pub use new::wasmer::{HostFunction, WasmTypeList};
/// Represents a function that can be used by WebAssembly.
#[derive(Clone)]
pub struct Func<Args = (), Rets = ()>
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
new_function: new::wasmer::Function,
_phantom: PhantomData<(Args, Rets)>,
}
impl<Args, Rets> Func<Args, Rets>
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
/// Creates a new `Func`.
pub fn new<F>(func: F) -> Self
where
F: HostFunction<Args, Rets, new::wasmer::internals::WithEnv, vm::Ctx>,
{
// Create an empty `vm::Ctx`, that is going to be overwritten by `Instance::new`.
let ctx = unsafe { vm::Ctx::new_uninit() };
Self {
new_function: new::wasmer::Function::new_env::<F, Args, Rets, vm::Ctx>(
&get_global_store(),
ctx,
func,
),
_phantom: PhantomData,
}
}
/// Returns the full function signature.
pub fn signature(&self) -> &FuncSig {
self.new_function.ty()
}
/// Returns the types of the function inputs.
pub fn params(&self) -> &[Type] {
self.signature().params()
}
/// Returns the types of the function outputs.
pub fn returns(&self) -> &[Type] {
self.signature().results()
}
/// Call the function by passing all arguments in a slice of `Value`.
pub fn dyn_call(&self, params: &[Value]) -> Result<Box<[Value]>, RuntimeError> {
self.new_function.call(params)
}
}
pub unsafe trait WasmExternTypeInner: WasmExternType
where
Self: Sized,
{
}
unsafe impl WasmExternTypeInner for i8 {}
unsafe impl WasmExternTypeInner for u8 {}
unsafe impl WasmExternTypeInner for i16 {}
unsafe impl WasmExternTypeInner for u16 {}
unsafe impl WasmExternTypeInner for i32 {}
unsafe impl WasmExternTypeInner for u32 {}
unsafe impl WasmExternTypeInner for i64 {}
unsafe impl WasmExternTypeInner for u64 {}
unsafe impl WasmExternTypeInner for f32 {}
unsafe impl WasmExternTypeInner for f64 {}
macro_rules! func_call {
( $( $x:ident ),* ) => {
#[allow(unused_parens)]
impl< $( $x, )* Rets: WasmTypeList > Func<( $( $x ),* ), Rets>
where
$( $x: WasmExternType + WasmExternTypeInner, )*
Rets: WasmTypeList
{
/// Call the function.
#[allow(non_snake_case, clippy::too_many_arguments)]
pub fn call(&self, $( $x: $x, )* ) -> Result<Rets, RuntimeError> {
// Two implementation choices:
// 1. Either by using the `NativeFunc` API, but a
// new native function must be created for each
// call,
// 2. Pack the parameters into a slice, call
// `dyn_call` with it, and unpack the results.
//
// The first implementation is the following:
//
// self.new_function.native::<( $( $x ),* ), Rets>().unwrap().call( $( $x ),* )
//
// The second implementation is the following active one:
// Pack the argument into a slice.
let params: &[Value] = &[
$(
$x.to_native().to_value()
),*
];
// Call the function with `dyn_call`, and transform the results into a vector.
let results: Vec<Value> = self.dyn_call(params)?.to_vec();
// Map the results into their binary form.
let rets: Vec<i128> = results
.iter()
.map(|value| {
Ok(match value {
Value::I32(value) => <i32 as WasmExternType>::from_native(*value).to_binary(),
Value::I64(value) => <i64 as WasmExternType>::from_native(*value).to_binary(),
Value::F32(value) => <f32 as WasmExternType>::from_native(*value).to_binary(),
Value::F64(value) => <f64 as WasmExternType>::from_native(*value).to_binary(),
value => return Err(RuntimeError::new(format!(
"value `{:?}` is not supported as a returned value of a host function for the moment; please use `dyn_call` or the new API",
value
))),
})
})
.collect::<Result<_, _>>()?;
// Convert `Vec<i128>` into a `WasmTypeList`.
let rets: Rets = Rets::from_slice(rets.as_slice()).map_err(|_| {
RuntimeError::new(format!(
"returned values (`{:?}`) do not match the expected returned type (`{:?}`)",
results,
Rets::wasm_types()
))
})?;
Ok(rets)
}
}
}
}
func_call!();
func_call!(A1);
func_call!(A1, A2);
func_call!(A1, A2, A3);
func_call!(A1, A2, A3, A4);
func_call!(A1, A2, A3, A4, A5);
func_call!(A1, A2, A3, A4, A5, A6);
func_call!(A1, A2, A3, A4, A5, A6, A7);
func_call!(A1, A2, A3, A4, A5, A6, A7, A8);
func_call!(A1, A2, A3, A4, A5, A6, A7, A8, A9);
func_call!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10);
func_call!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11);
func_call!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12);
func_call!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13);
func_call!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14);
func_call!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15);
func_call!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16);
func_call!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17);
func_call!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18);
func_call!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19);
func_call!(
A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20
);
impl<Args, Rets> From<Func<Args, Rets>> for new::wasmer::Extern
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
fn from(func: Func<Args, Rets>) -> Self {
new::wasmer::Extern::Function(func.new_function)
}
}
impl<Args, Rets> From<&new::wasmer::Function> for Func<Args, Rets>
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
fn from(new_function: &new::wasmer::Function) -> Self {
Self {
new_function: new_function.clone(),
_phantom: PhantomData,
}
}
}
impl<'a, Args, Rets> new::wasmer::Exportable<'a> for Func<Args, Rets>
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
fn to_export(&self) -> new::wasmer_vm::Export {
self.new_function.to_export()
}
fn get_self_from_extern(r#extern: &'a new::wasmer::Extern) -> Result<&'a Self, ExportError> {
match r#extern {
new::wasmer::Extern::Function(func) => Ok(
// It's not ideal to call `Box::leak` here, but it
// would introduce too much changes in the
// `new::wasmer` API to support `Cow` or similar.
Box::leak(Box::<Func<Args, Rets>>::new(func.into())),
),
_ => Err(ExportError::IncompatibleType),
}
}
}
/// Represents a type-erased function provided by either the host or the WebAssembly program.
#[derive(Clone)]
pub struct DynamicFunc {
new_function: new::wasmer::Function,
}
use std::{
cell::{RefCell, RefMut},
ops::DerefMut,
rc::Rc,
};
/// Specific context for `DynamicFunc`. It's a hack.
///
/// Initially, it holds an empty `vm::Ctx`, but it is replaced by the
/// `vm::Ctx` from `instance::PreInstance` in
/// `module::Module::instantiate`.
pub(crate) struct DynamicCtx {
pub(crate) vmctx: Rc<RefCell<vm::Ctx>>,
}
impl DynamicFunc {
/// Create a new `DynamicFunc`.
pub fn new<F>(signature: &FuncSig, func: F) -> Self
where
F: Fn(&mut vm::Ctx, &[Value]) -> Result<Vec<Value>, RuntimeError> + 'static,
{
// Create an empty `vm::Ctx`, that is going to be overwritten by `Instance::new`.
let ctx = DynamicCtx {
vmctx: Rc::new(RefCell::new(unsafe { vm::Ctx::new_uninit() })),
};
Self {
new_function: new::wasmer::Function::new_dynamic_env(
&get_global_store(),
signature,
ctx,
// Wrapper to safely extract a `&mut vm::Ctx` to pass
// to `func`.
move |dyn_ctx: &mut DynamicCtx,
params: &[Value]|
-> Result<Vec<Value>, RuntimeError> {
let cell: Rc<RefCell<vm::Ctx>> = dyn_ctx.vmctx.clone();
let mut vmctx: RefMut<vm::Ctx> = cell.borrow_mut();
func(vmctx.deref_mut(), params)
},
),
}
}
/// Returns the full function signature.
pub fn signature(&self) -> &FuncSig {
self.new_function.ty()
}
/// Returns the types of the function inputs.
pub fn params(&self) -> &[Type] {
self.signature().params()
}
/// Returns the types of the function outputs.
pub fn returns(&self) -> &[Type] {
self.signature().results()
}
/// Call the function. In this case, it's an alias to `dyn_call`.
pub fn call(&self, params: &[Value]) -> Result<Box<[Value]>, RuntimeError> {
self.dyn_call(params)
}
/// Call the function.
pub fn dyn_call(&self, params: &[Value]) -> Result<Box<[Value]>, RuntimeError> {
self.new_function.call(params)
}
}
impl From<DynamicFunc> for new::wasmer::Extern {
fn from(dynamic_func: DynamicFunc) -> Self {
new::wasmer::Extern::Function(dynamic_func.new_function)
}
}
impl From<&new::wasmer::Function> for DynamicFunc {
fn from(new_function: &new::wasmer::Function) -> Self {
Self {
new_function: new_function.clone(),
}
}
}
impl<'a> new::wasmer::Exportable<'a> for DynamicFunc {
fn to_export(&self) -> new::wasmer_vm::Export {
self.new_function.to_export()
}
fn get_self_from_extern(r#extern: &'a new::wasmer::Extern) -> Result<&'a Self, ExportError> {
match r#extern {
new::wasmer::Extern::Function(dynamic_func) => Ok(
// It's not ideal to call `Box::leak` here, but it
// would introduce too much changes in the
// `new::wasmer` API to support `Cow` or similar.
Box::leak(Box::<DynamicFunc>::new(dynamic_func.into())),
),
_ => Err(ExportError::IncompatibleType),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! test_func_arity_n {
($test_name:ident, $($x:ident),*) => {
#[test]
fn $test_name() {
use crate::vm;
fn with_vmctx(_: &mut vm::Ctx, $($x: i32),*) -> i32 {
vec![$($x),*].iter().sum()
}
let _ = Func::new(with_vmctx);
let _ = Func::new(|_: &mut vm::Ctx, $($x: i32),*| -> i32 {
vec![$($x),*].iter().sum()
});
}
}
}
#[test]
fn test_func_arity_0() {
fn foo(_: &mut vm::Ctx) -> i32 {
0
}
let _ = Func::new(foo);
let _ = Func::new(|_: &mut vm::Ctx| -> i32 { 0 });
}
test_func_arity_n!(test_func_arity_1, a);
test_func_arity_n!(test_func_arity_2, a, b);
test_func_arity_n!(test_func_arity_3, a, b, c);
test_func_arity_n!(test_func_arity_4, a, b, c, d);
test_func_arity_n!(test_func_arity_5, a, b, c, d, e);
test_func_arity_n!(test_func_arity_6, a, b, c, d, e, f);
test_func_arity_n!(test_func_arity_7, a, b, c, d, e, f, g);
test_func_arity_n!(test_func_arity_8, a, b, c, d, e, f, g, h);
test_func_arity_n!(test_func_arity_9, a, b, c, d, e, f, g, h, i);
test_func_arity_n!(test_func_arity_10, a, b, c, d, e, f, g, h, i, j);
test_func_arity_n!(test_func_arity_11, a, b, c, d, e, f, g, h, i, j, k);
test_func_arity_n!(test_func_arity_12, a, b, c, d, e, f, g, h, i, j, k, l);
test_func_arity_n!(test_func_arity_13, a, b, c, d, e, f, g, h, i, j, k, l, m);
test_func_arity_n!(test_func_arity_14, a, b, c, d, e, f, g, h, i, j, k, l, m, n);
#[rustfmt::skip] test_func_arity_n!(test_func_arity_15, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o);
#[rustfmt::skip] test_func_arity_n!(test_func_arity_16, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p);
#[rustfmt::skip] test_func_arity_n!(test_func_arity_17, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q);
#[rustfmt::skip] test_func_arity_n!(test_func_arity_18, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r);
#[rustfmt::skip] test_func_arity_n!(test_func_arity_19, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s);
#[rustfmt::skip] test_func_arity_n!(test_func_arity_20, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t);
#[rustfmt::skip] test_func_arity_n!(test_func_arity_21, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u);
#[rustfmt::skip] test_func_arity_n!(test_func_arity_22, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v);
#[rustfmt::skip] test_func_arity_n!(test_func_arity_23, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w);
#[rustfmt::skip] test_func_arity_n!(test_func_arity_24, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x);
#[rustfmt::skip] test_func_arity_n!(test_func_arity_25, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y);
#[rustfmt::skip] test_func_arity_n!(test_func_arity_26, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z);
#[test]
fn test_call() {
fn foo(_ctx: &mut vm::Ctx, a: i32, b: i32) -> (i32, i32) {
(a, b)
}
let _f = Func::new(foo);
}
#[test]
fn test_imports() {
use crate::{func, imports};
fn foo(_ctx: &mut vm::Ctx, a: i32) -> i32 {
a
}
let _import_object = imports! {
"env" => {
"foo" => func!(foo),
},
};
}
}

View File

@@ -0,0 +1,99 @@
use crate::new;
pub use new::wasm_common::{
//
ExportType as ExportDescriptor,
ExternType as ExternDescriptor,
FunctionIndex as FuncIndex,
FunctionIndex as ImportedFuncIndex,
FunctionType as FuncDescriptor,
FunctionType as FuncSig,
GlobalIndex as ImportedGlobalIndex,
GlobalIndex,
GlobalInit,
ImportType as ImportDescriptor,
LocalFunctionIndex as LocalFuncIndex,
LocalGlobalIndex,
LocalMemoryIndex,
LocalTableIndex,
MemoryIndex as ImportedMemoryIndex,
MemoryIndex,
MemoryType as MemoryDescriptor,
NativeWasmType,
SignatureIndex as SigIndex,
TableIndex as ImportedTableIndex,
TableIndex,
TableType as TableDescriptor,
Type,
ValueType,
};
pub use new::wasmer::{FromToNativeWasmType as WasmExternType, Val as Value};
/// Describes the mutability and type of a Global
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct GlobalDescriptor {
pub mutable: bool,
pub ty: Type,
}
impl From<&new::wasm_common::GlobalType> for GlobalDescriptor {
fn from(value: &new::wasm_common::GlobalType) -> Self {
Self {
mutable: value.mutability.into(),
ty: value.ty,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_native_types_round_trip() {
assert_eq!(
42i32,
i32::from_native(i32::from_binary((42i32).to_native().to_binary()))
);
assert_eq!(
-42i32,
i32::from_native(i32::from_binary((-42i32).to_native().to_binary()))
);
use std::i64;
let xi64 = i64::MAX;
assert_eq!(
xi64,
i64::from_native(i64::from_binary((xi64).to_native().to_binary()))
);
let yi64 = i64::MIN;
assert_eq!(
yi64,
i64::from_native(i64::from_binary((yi64).to_native().to_binary()))
);
assert_eq!(
16.5f32,
f32::from_native(f32::from_binary((16.5f32).to_native().to_binary()))
);
assert_eq!(
-16.5f32,
f32::from_native(f32::from_binary((-16.5f32).to_native().to_binary()))
);
use std::f64;
let xf64: f64 = f64::MAX;
assert_eq!(
xf64,
f64::from_native(f64::from_binary((xf64).to_native().to_binary()))
);
let yf64: f64 = f64::MIN;
assert_eq!(
yf64,
f64::from_native(f64::from_binary((yf64).to_native().to_binary()))
);
}
}

Some files were not shown because too many files have changed in this diff Show More