Merge branch 'master' into feature/cache-rework

This commit is contained in:
Lachlan Sneff 2019-02-21 11:54:57 -08:00 committed by GitHub
commit 2234f357b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 2886 additions and 179 deletions

View File

@ -12,6 +12,9 @@ environment:
ABI: msvc
TARGET: x86_64-pc-windows-msvc
cache:
- 'C:\Users\appveyor\.cargo'
install:
- appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
- rustup-init.exe -yv --default-host %target%
@ -27,9 +30,9 @@ test_script:
- cd ./lib/spectests && cargo test -- --test-threads 1 && cd ../..
before_deploy:
- cd installer
- cd ./src/installer
- iscc wasmer.iss
- copy /y .\WasmerInstaller.exe ..\WasmerInstaller-%APPVEYOR_REPO_TAG_NAME%.exe
- copy /y .\WasmerInstaller.exe ..\..\WasmerInstaller-%APPVEYOR_REPO_TAG_NAME%.exe
- appveyor PushArtifact WasmerInstaller-%APPVEYOR_REPO_TAG_NAME%.exe
artifacts:

View File

@ -2,19 +2,19 @@
Wasmer uses the following components:
- [Cranelift](https://github.com/cranestation/cranelift): for compiling WASM function binaries into Machine IR
- [wabt](https://github.com/pepyakin/wabt-rs): for transforming `.wast` files to `.wasm` and also to run WebAssembly spectests
- [wasmparser](https://github.com/yurydelendik/wasmparser.rs): for parsing the `.wasm` files and translating them into WebAssembly Modules
- [Cranelift](https://github.com/cranestation/cranelift): for compiling Wasm binaries to machine code
- [wabt](https://github.com/pepyakin/wabt-rs): for transforming `.wast` files to `.wasm` and running WebAssembly spec tests
- [wasmparser](https://github.com/yurydelendik/wasmparser.rs): for parsing the `.wasm` files and translating them into WebAssembly modules
## How Wasmer works?
## How Wasmer works
The first time you run `wasmer run myfile.wasm`, wasmer will:
The first time you run `wasmer run myfile.wasm`, Wasmer will:
- Check if is a `.wast` file. If so, transform it to `.wasm`
- Check that the provided binary is a valid WebAssembly one. That means, that its binary format starts with `\0asm`.
- If it looks like a WebAssembly file, try to parse it with `wasmparser` and generate a `Module` from it
- Once a `Module` is generated, an `Instance` is created with the proper `import_object` (that means, if is detected as an emscripten file, it will add the emscripten expected imports)
- Try to call the WebAssembly start function, or if unexistent try to search for the one that is exported as `main`.
- Check if is a `.wast` file, and if so, transform it to `.wasm`
- Check that the provided binary is a valid WebAssembly one, i.e. its binary format starts with `\0asm`.
- Parse it with `wasmparser` and generate a `Module` from it
- Generate an `Instance` with the proper `import_object` (that means, if is detected to be an Emscripten file, it will add the Emscripten expected imports)
- Try to call the WebAssembly `start` function, or if it does not exist, try to search for the function that is exported as `main`
Find a more detailed explanation of the process below:
@ -22,7 +22,7 @@ Find a more detailed explanation of the process below:
As the WebAssembly file is being parsed, it will read the sections in the WebAssembly file (memory, table, function, global and element definitions) using the `Module` (or `ModuleEnvironment`) as the structure to hold this information.
However, the real IR initialization happens while a function body is being parsed/created. That means, when the parser reads the section `(func ...)`.
However, the real IR initialization happens while a function body is being parsed/created, i.e. when the parser reads the section `(func ...)`.
While the function body is being parsed the corresponding `FuncEnvironment` methods will be called.
So for example, if the function is using a table, the `make_table` method within that `FuncEnvironment` will be called.
@ -41,15 +41,14 @@ Once we have the compiled values, we will push them to memory and mark them as e
#### Relocations
Sometimes the functions that we generated will need to call other functions.
However the generated code have no idea how to link this functions together.
Sometimes the functions that we generate will need to call other functions, but the generated code has no idea how to link these functions together.
For example, if a function `A` is calling function `B` (that means is having a `(call b)` on it's body) while compiling `A` we will have no idea where the function `B` lives on memory (as `B` is not yet compiled nor pushed into memory).
For example, if a function `A` is calling function `B` (that means is having a `(call b)` on its body) while compiling `A` we will have no idea where the function `B` lives on memory (as `B` is not yet compiled nor pushed into memory).
For that reason, we will start collecting all the calls that function `A` will need to do under the hood, and save it's offsets.
We do that, so we can patch the function calls after compilation, to point to the correct memory address.
Note: Sometimes this functions rather than living in the same WebAssembly module, they will be provided as import values.
Note: sometimes this functions rather than living in the same WebAssembly module, they will be provided as import values.
#### Traps
@ -66,5 +65,5 @@ Once that's finished, we will have a `Instance` function that will be ready to e
## Emscripten
The Wasmer Emscripten integration tries to wrap (and emulate) all the different syscalls that Emscripten needs.
We provide this integration by filling the `import_object` with the emscripten functions, while instantiating the WebAssembly Instance.
Wasmer's Emscripten integration tries to wrap (and emulate) all the different syscalls that Emscripten needs.
We provide this integration by filling the `import_object` with the Emscripten functions, while instantiating the WebAssembly Instance.

View File

@ -2,7 +2,7 @@
Wasmer is a community effort.
In order to build the best WebAssembly runtime it's our duty to see how other runtimes are approaching the same space
and get inspired from them on the things that they got right, so wasmer and its community can benefit from a solid
and get inspired from them on the things that they got right, so Wasmer and its community can benefit from a solid
foundation.
These are the different project that we used as inspiration:
@ -10,9 +10,9 @@ These are the different project that we used as inspiration:
- [Nebulet](https://github.com/nebulet/nebulet): as the base for creating a great Rust WebAssembly runtime
- [WAVM](https://github.com/wavm/wavm): for their great integration and testing framework
- [greenwasm](https://github.com/Kimundi/greenwasm): for their [spectests framework](https://github.com/Kimundi/greenwasm/tree/master/greenwasm-spectest)
- [wasmtime](/wasmtime): on their [mmap implementation](https://github.com/CraneStation/wasmtime/blob/3f24098edc81cd9bf0f877fb7fba018cad0f039e/lib/runtime/src/mmap.rs).
- [stackoverflow](https://stackoverflow.com/a/45795699/1072990): to create an efficient HashMap with pair keys.
- [Emscripten](https://github.com/kripken/emscripten): for emtests test sources to ensure compatibility.
- [wasmtime](https://github.com/CraneStation/wasmtime): for their [mmap implementation](https://github.com/CraneStation/wasmtime/blob/3f24098edc81cd9bf0f877fb7fba018cad0f039e/lib/runtime/src/mmap.rs)
- [stackoverflow](https://stackoverflow.com/a/45795699/1072990): to create an efficient HashMap with pair keys
- [Emscripten](https://github.com/kripken/emscripten): for emtests test sources to ensure compatibility
We would love to hear from you if you think we can take inspiration from other projects that we haven't covered here.
😊
@ -21,7 +21,7 @@ We would love to hear from you if you think we can take inspiration from other p
### Nebulet
```
```text
MIT License
Copyright (c) 2018
@ -47,7 +47,7 @@ SOFTWARE.
### WAVM
```
```text
Copyright (c) 2018, Andrew Scheidecker
All rights reserved.
@ -69,7 +69,7 @@ The contents of [Test/spec](Test/spec) is covered by the license in [Test/spec/L
### Greenwasm
```
```text
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
@ -275,7 +275,7 @@ limitations under the License.
### Wasmtime
```
```text
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
@ -497,7 +497,7 @@ Software.
```
### Emscripten
```
```text
Emscripten is available under 2 licenses, the MIT license and the
University of Illinois/NCSA Open Source License.

61
Cargo.lock generated
View File

@ -113,6 +113,21 @@ name = "cast"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cbindgen"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.58 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)",
"tempfile 3.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cc"
version = "1.0.28"
@ -713,6 +728,14 @@ dependencies = [
"ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "remove_dir_all"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rustc-demangle"
version = "0.1.13"
@ -753,6 +776,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "serde"
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde_derive 1.0.58 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde-bench"
@ -881,6 +907,19 @@ dependencies = [
"serde_json 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tempfile"
version = "3.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)",
"remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "termcolor"
version = "1.0.4"
@ -925,6 +964,14 @@ dependencies = [
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "toml"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "typenum"
version = "1.10.0"
@ -1058,6 +1105,16 @@ dependencies = [
"wasmer-runtime-core 0.1.2",
]
[[package]]
name = "wasmer-runtime-c-api"
version = "0.1.4"
dependencies = [
"cbindgen 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmer-runtime 0.1.4",
"wasmer-runtime-core 0.1.2",
]
[[package]]
name = "wasmer-runtime-core"
version = "0.1.2"
@ -1183,6 +1240,7 @@ dependencies = [
"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
"checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb"
"checksum cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "926013f2860c46252efceabb19f4a6b308197505082c609025aa6706c011d427"
"checksum cbindgen 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "32e01024aaf5390d6a8145047371a4f5b0063a14c1e411bc731353bd2278ca44"
"checksum cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4a8b715cb4597106ea87c7c84b2f1d452c7492033765df7f32651e66fcf749"
"checksum cexpr 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "644d693ecfa91955ed32dcc7eda4914e1be97a641fb6f0645a37348e20b230da"
"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
@ -1255,6 +1313,7 @@ dependencies = [
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
"checksum regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f"
"checksum regex-syntax 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8c2f35eedad5295fdf00a63d7d4b238135723f92b434ec06774dad15c7ab0861"
"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5"
"checksum rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "adacaae16d02b6ec37fdc7acfcddf365978de76d1983d3ee22afc260e1ca9619"
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
"checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7"
@ -1276,11 +1335,13 @@ dependencies = [
"checksum syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)" = "f92e629aa1d9c827b2bb8297046c1ccffc57c99b947a680d3ccff1f136a3bee9"
"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"
"checksum target-lexicon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4af5e2227f0b887d591d3724b796a96eff04226104d872f5b3883fcd427d64b9"
"checksum tempfile 3.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "37daa55a7240c4931c84559f03b3cad7d19535840d1c4a0cc4e9b2fb0dcf70ff"
"checksum termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4096add70612622289f2fdcdbd5086dc81c1e2675e6ae58d6c4f62a16c6d7f2f"
"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
"checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6"
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
"checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f"
"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169"
"checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
"checksum unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa6024fc12ddfd1c6dbc14a80fa2324d4568849869b779f6bd37e5e4c03344d1"

View File

@ -27,7 +27,7 @@ wasmer-runtime-core = { path = "lib/runtime-core" }
wasmer-emscripten = { path = "lib/emscripten" }
[workspace]
members = ["lib/clif-backend", "lib/runtime", "lib/runtime-core", "lib/emscripten", "lib/spectests", "lib/win-exception-handler"]
members = ["lib/clif-backend", "lib/runtime", "lib/runtime-core", "lib/emscripten", "lib/spectests", "lib/win-exception-handler", "lib/runtime-c-api"]
[build-dependencies]
wabt = "0.7.2"

View File

@ -1,6 +1,4 @@
The MIT License (MIT)
Copyright (c) 2018-Present Syrus Akbary
Copyright (c) 2019 Syrus Akbary
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -12,6 +12,9 @@ spectests:
emtests:
WASM_EMSCRIPTEN_GENERATE_EMTESTS=1 cargo build -p wasmer-emscripten
capi:
WASM_EMSCRIPTEN_GENERATE_C_API_HEADERS=1 cargo build --manifest-path lib/runtime-c-api/Cargo.toml --features generate-c-api-headers
# clean:
# rm -rf artifacts
@ -34,9 +37,10 @@ precommit: lint test
test:
# We use one thread so the emscripten stdouts doesn't collide
cargo test --all -- --test-threads=1 $(runargs)
cargo test --all --exclude wasmer-runtime-c-api -- --test-threads=1 $(runargs)
# cargo test --all --exclude wasmer-emscripten -- --test-threads=1 $(runargs)
# cargo test -p wasmer-spectests -- --test-threads=1 $(runargs)
cargo build -p wasmer-runtime-c-api
cargo test -p wasmer-runtime-c-api -- --nocapture
release:
# If you are in OS-X, you will need mingw-w64 for cross compiling to windows

View File

@ -1,16 +1,24 @@
<p align="center"><a href="https://wasmer.io" target="_blank" rel="noopener noreferrer"><img width="400" src="https://raw.githubusercontent.com/wasmerio/wasmer/master/logo.png" alt="Wasmer logo"></a></p>
<p align="center">
<a href="https://wasmer.io" target="_blank" rel="noopener noreferrer">
<img width="400" src="https://raw.githubusercontent.com/wasmerio/wasmer/master/logo.png" alt="Wasmer logo">
</a>
</p>
<p align="center">
<a href="https://circleci.com/gh/wasmerio/wasmer/"><img src="https://img.shields.io/circleci/project/github/wasmerio/wasmer/master.svg" alt="Build Status"></a>
<a href="https://github.com/wasmerio/wasmer/blob/master/LICENSE"><img src="https://img.shields.io/github/license/wasmerio/wasmer.svg" alt="License"></a>
<a href="https://circleci.com/gh/wasmerio/wasmer/">
<img src="https://img.shields.io/circleci/project/github/wasmerio/wasmer/master.svg" alt="Build Status">
</a>
<a href="https://github.com/wasmerio/wasmer/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/wasmerio/wasmer.svg" alt="License">
</a>
<a href="https://spectrum.chat/wasmer">
<img alt="Join the Wasmer Community" src="https://withspectrum.github.io/badge/badge.svg" />
<img src="https://withspectrum.github.io/badge/badge.svg" alt="Join the Wasmer Community">
</a>
</p>
## Introduction
[Wasmer](https://wasmer.io/) is a Standalone JIT WebAssembly runtime, aiming to be fully compatible with Emscripten, Rust and Go.
[Wasmer](https://wasmer.io/) is a standalone JIT WebAssembly runtime, aiming to be fully compatible with Emscripten, Rust and Go.
Install Wasmer with:
@ -18,20 +26,20 @@ Install Wasmer with:
curl https://get.wasmer.io -sSfL | sh
```
_**NEW ✨**: Now you can also embed Wasmer in your Rust application, check our [example repo](https://github.com/wasmerio/wasmer-rust-example) to see how to do it!_
_**NEW ✨**: You can now embed Wasmer in your Rust application, check our [example repo](https://github.com/wasmerio/wasmer-rust-example) to see how!_
### Usage
`wasmer` can execute both the standard binary format (`.wasm`) and the text
Wasmer can execute both the standard binary format (`.wasm`) and the text
format defined by the WebAssembly reference interpreter (`.wat`).
Once installed, you will be able to run any WebAssembly files (_including Nginx, and Lua!_):
Once installed, you will be able to run any WebAssembly files (_including nginx and Lua!_):
```sh
# Run Lua
wasmer run examples/lua.wasm
# Run Nginx
# Run nginx
wasmer run examples/nginx/nginx.wasm -- -p examples/nginx -c nginx.conf
```
@ -39,13 +47,13 @@ wasmer run examples/nginx/nginx.wasm -- -p examples/nginx -c nginx.conf
Wasmer is structured into different directories:
- [`src`](./src): code related to the wasmer excutable binary itself
- [`src`](./src): code related to the Wasmer executable itself
- [`lib`](./lib): modularized libraries that Wasmer uses under the hood
- [`examples`](./examples): some useful examples to getting started with wasmer
- [`examples`](./examples): some useful examples to getting started with Wasmer
## Dependencies
Building wasmer requires [rustup](https://rustup.rs/).
Building Wasmer requires [rustup](https://rustup.rs/).
To build on Windows, download and run [`rustup-init.exe`](https://win.rustup.rs/)
then follow the onscreen instructions.
@ -66,13 +74,13 @@ Please select your operating system:
#### macOS
If you have [homebrew](https://brew.sh/) installed:
If you have [Homebrew](https://brew.sh/) installed:
```sh
brew install cmake
```
Or, in case you have [ports](https://www.macports.org/install.php):
Or, in case you have [MacPorts](https://www.macports.org/install.php):
```sh
sudo port install cmake
@ -86,16 +94,16 @@ sudo apt install cmake
#### Windows (MSVC)
Windows support is _highly experimental_. Only simple wasm programs may be run, and no syscalls are allowed. This means
nginx and lua do not work on Windows. See [this issue for ongoing Emscripten syscall polyfills for Windows](https://github.com/wasmerio/wasmer/pull/176).
Windows support is _highly experimental_. Only simple Wasm programs may be run, and no syscalls are allowed. This means
nginx and Lua do not work on Windows. See [this issue](https://github.com/wasmerio/wasmer/issues/176) regarding Emscripten syscall polyfills for Windows.
1. Install Python for Windows (https://www.python.org/downloads/release/python-2714/). The Windows x86-64 MSI installer is fine.
You should change the installation to install the "Add python.exe to Path" feature.
1. Install [Python for Windows](https://www.python.org/downloads/release/python-2714/). The Windows x86-64 MSI installer is fine.
Make sure to enable "Add python.exe to Path" during installation.
2. Install Git for Windows (https://git-scm.com/download/win). DO allow it to add git.exe to the PATH (default
2. Install [Git for Windows](https://git-scm.com/download/win). Allow it to add `git.exe` to your PATH (default
settings for the installer are fine).
3. Install CMake (https://cmake.org/download/). Ensure CMake is in the PATH.
3. Install [CMake](https://cmake.org/download/). Ensure CMake is in your PATH.
## Building
@ -113,7 +121,7 @@ cargo install --path .
## Testing
Thanks to [spectests](https://github.com/wasmerio/wasmer/tree/master/lib/runtime-core/spectests) we can assure 100% compatibility with the WebAssembly spec test suite.
Thanks to [spec tests](https://github.com/wasmerio/wasmer/tree/master/lib/spectests/spectests) we can ensure 100% compatibility with the WebAssembly spec test suite.
Tests can be run with:
@ -121,7 +129,7 @@ Tests can be run with:
make test
```
If you need to re-generate the Rust tests from the spectests
If you need to regenerate the Rust tests from the spec tests
you can run:
```sh
@ -138,20 +146,20 @@ make integration-tests
Wasmer is an open project guided by strong principles, aiming to be modular, flexible and fast. It is open to the community to help set its direction.
Below are some of the goals (written with order) of this project:
Below are some of the goals of this project (in order of priority):
- [x] It should be 100% compatible with the [WebAssembly Spectest](https://github.com/wasmerio/wasmer/tree/master/spectests)
- [x] It should be 100% compatible with the [WebAssembly spec tests](https://github.com/wasmerio/wasmer/tree/master/lib/spectests/spectests)
- [x] It should be fast _(partially achieved)_
- [ ] Support Emscripten calls _(in the works)_
- [ ] Support Rust ABI calls
- [ ] Support GO ABI calls
- [ ] Support Go ABI calls
## Architecture
If you would like to know how Wasmer works under the hood, please visit our [ARCHITECTURE](https://github.com/wasmerio/wasmer/blob/master/ARCHITECTURE.md) document.
If you would like to know how Wasmer works under the hood, please see [ARCHITECTURE.md](./ARCHITECTURE.md).
## License
MIT/Apache-2.0
Wasmer is primarily distributed under the terms of the [MIT license](http://opensource.org/licenses/MIT) ([LICENSE](./LICENSE)).
<small>[Attributions](./ATTRIBUTIONS.md)</small>.
[ATTRIBUTIONS](./ATTRIBUTIONS.md)

View File

@ -1,8 +1,8 @@
# `lua` integration test
This starts wasmer with the lua wasm file. The test asserts on
the output of wasmer. Run test with:
This starts Wasmer with the Lua Wasm file. The test makes assertions on
the output of Wasmer. Run test with:
```
> ./integration_tests/lua/test.sh

View File

@ -1,7 +1,7 @@
# `nginx` integration test
This starts wasmer with the nginx wasm file and serves an html
This starts Wasmer with the nginx Wasm file and serves an HTML
file with some simple text to assert on. The test script does
the assertion.

View File

@ -2,34 +2,34 @@
Wasmer is modularized into different libraries, separated into three main sections:
- [Runtime](#Runtime)
- [Integrations](#Integrations)
- [Backends](#Backends)
- [Runtime](#runtime)
- [Integrations](#integrations)
- [Backends](#backends)
## Runtime
The core of Wasmer is the runtime, which provides the necessary
abstractions to create a good user-experience when embedding.
abstractions to create a good user experience when embedding.
The runtime is divided into two main libraries:
- [runtime-core](./runtime-core/): The main implementation of the runtime.
- [runtime](./runtime/): Easy-to-use api on top of runtime-core.
- [runtime](./runtime/): Easy-to-use API on top of `runtime-core`.
## Integrations
The intergration run on-top of the Wasmer runtime and allow us to run WebAssembly files compiled for different environments.
The integration builds on the Wasmer runtime and allow us to run WebAssembly files compiled for different environments.
Wasmer intends to support different integrations:
- [emscripten](./emscripten): run emscripten-generated WebAssembly files, such as [Lua](../examples/lua.wasm) or [Nginx](../examples/nginx/nginx.wasm).
- [emscripten](./emscripten): run Emscripten-generated WebAssembly files, such as [Lua](../examples/lua.wasm) or [nginx](../examples/nginx/nginx.wasm).
- Go ABI: _we will work on this soon! Want to give us a hand? ✋_
- Blazor: _researching period, see [tracking issue](https://github.com/wasmerio/wasmer/issues/97)_
- Blazor: _research period, see [tracking issue](https://github.com/wasmerio/wasmer/issues/97)_
## Backends
The Wasmer [runtime](./runtime) is designed to support multiple compiler backends, allowing the user
to tune the codegen properties (compile speed, performance, etc) to fit your usecase best.
to tune the codegen properties (compile speed, performance, etc) to best fit their use case.
Currently, we support a Cranelift compiler backend:

View File

@ -0,0 +1,175 @@
use crate::env::get_emscripten_data;
use wasmer_runtime_core::vm::Ctx;
pub fn setTempRet0(ctx: &mut Ctx, a: i32) {
debug!("emscripten::setTempRet0");
}
pub fn getTempRet0(ctx: &mut Ctx) -> i32 {
debug!("emscripten::getTempRet0");
0
}
pub fn nullFunc_ji(ctx: &mut Ctx, a: i32) {
debug!("emscripten::nullFunc_ji");
}
pub fn invoke_i(ctx: &mut Ctx, index: i32) -> i32 {
debug!("emscripten::invoke_i");
if let Some(dyn_call_i) = &get_emscripten_data(ctx).dyn_call_i {
dyn_call_i.call(index).unwrap()
} else {
panic!("dyn_call_i is set to None");
}
}
pub fn invoke_ii(ctx: &mut Ctx, index: i32, a1: i32) -> i32 {
debug!("emscripten::invoke_ii");
if let Some(dyn_call_ii) = &get_emscripten_data(ctx).dyn_call_ii {
dyn_call_ii.call(index, a1).unwrap()
} else {
panic!("dyn_call_ii is set to None");
}
}
pub fn invoke_iii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32) -> i32 {
debug!("emscripten::invoke_iii");
if let Some(dyn_call_iii) = &get_emscripten_data(ctx).dyn_call_iii {
dyn_call_iii.call(index, a1, a2).unwrap()
} else {
panic!("dyn_call_iii is set to None");
}
}
pub fn invoke_iiii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32) -> i32 {
debug!("emscripten::invoke_iiii");
if let Some(dyn_call_iiii) = &get_emscripten_data(ctx).dyn_call_iiii {
dyn_call_iiii.call(index, a1, a2, a3).unwrap()
} else {
panic!("dyn_call_iiii is set to None");
}
}
pub fn invoke_v(ctx: &mut Ctx, index: i32) {
debug!("emscripten::invoke_v");
if let Some(dyn_call_v) = &get_emscripten_data(ctx).dyn_call_v {
dyn_call_v.call(index).unwrap();
} else {
panic!("dyn_call_v is set to None");
}
}
pub fn invoke_vi(ctx: &mut Ctx, index: i32, a1: i32) {
debug!("emscripten::invoke_vi");
if let Some(dyn_call_vi) = &get_emscripten_data(ctx).dyn_call_vi {
dyn_call_vi.call(index, a1).unwrap();
} else {
panic!("dyn_call_vi is set to None");
}
}
pub fn invoke_vii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32) {
debug!("emscripten::invoke_vii");
if let Some(dyn_call_vii) = &get_emscripten_data(ctx).dyn_call_vii {
dyn_call_vii.call(index, a1, a2).unwrap();
} else {
panic!("dyn_call_vii is set to None");
}
}
pub fn invoke_viii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32) {
debug!("emscripten::invoke_viii");
if let Some(dyn_call_viii) = &get_emscripten_data(ctx).dyn_call_viii {
dyn_call_viii.call(index, a1, a2, a3).unwrap();
} else {
panic!("dyn_call_viii is set to None");
}
}
pub fn invoke_viiii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32, a4: i32) {
debug!("emscripten::invoke_viiii");
if let Some(dyn_call_viiii) = &get_emscripten_data(ctx).dyn_call_viiii {
dyn_call_viiii.call(index, a1, a2, a3, a4).unwrap();
} else {
panic!("dyn_call_viiii is set to None");
}
}
pub fn __Unwind_Backtrace(ctx: &mut Ctx, a: i32, b: i32) -> i32 {
debug!("emscripten::__Unwind_Backtrace");
0
}
pub fn __Unwind_FindEnclosingFunction(ctx: &mut Ctx, a: i32) -> i32 {
debug!("emscripten::__Unwind_FindEnclosingFunction");
0
}
pub fn __Unwind_GetIPInfo(ctx: &mut Ctx, a: i32, b: i32) -> i32 {
debug!("emscripten::__Unwind_GetIPInfo");
0
}
pub fn ___cxa_find_matching_catch_2(ctx: &mut Ctx) -> i32 {
debug!("emscripten::___cxa_find_matching_catch_2");
0
}
pub fn ___cxa_find_matching_catch_3(ctx: &mut Ctx, a: i32) -> i32 {
debug!("emscripten::___cxa_find_matching_catch_3");
0
}
pub fn ___cxa_free_exception(ctx: &mut Ctx, a: i32) {
debug!("emscripten::___cxa_free_exception");
}
pub fn ___resumeException(ctx: &mut Ctx, a: i32) {
debug!("emscripten::___resumeException");
}
pub fn _dladdr(ctx: &mut Ctx, a: i32, b: i32) -> i32 {
debug!("emscripten::_dladdr");
0
}
pub fn _pthread_cond_destroy(ctx: &mut Ctx, a: i32) -> i32 {
debug!("emscripten::_pthread_cond_destroy");
0
}
pub fn _pthread_cond_init(ctx: &mut Ctx, a: i32, b: i32) -> i32 {
debug!("emscripten::_pthread_cond_init");
0
}
pub fn _pthread_cond_signal(ctx: &mut Ctx, a: i32) -> i32 {
debug!("emscripten::_pthread_cond_signal");
0
}
pub fn _pthread_cond_wait(ctx: &mut Ctx, a: i32, b: i32) -> i32 {
debug!("emscripten::_pthread_cond_wait");
0
}
pub fn _pthread_condattr_destroy(ctx: &mut Ctx, a: i32) -> i32 {
debug!("emscripten::_pthread_condattr_destroy");
0
}
pub fn _pthread_condattr_init(ctx: &mut Ctx, a: i32) -> i32 {
debug!("emscripten::_pthread_condattr_init");
0
}
pub fn _pthread_condattr_setclock(ctx: &mut Ctx, a: i32, b: i32) -> i32 {
debug!("emscripten::_pthread_condattr_setclock");
0
}
pub fn _pthread_mutex_destroy(ctx: &mut Ctx, a: i32) -> i32 {
debug!("emscripten::_pthread_mutex_destroy");
0
}
pub fn _pthread_mutex_init(ctx: &mut Ctx, a: i32, b: i32) -> i32 {
debug!("emscripten::_pthread_mutex_init");
0
}
pub fn _pthread_mutexattr_destroy(ctx: &mut Ctx, a: i32) -> i32 {
debug!("emscripten::_pthread_mutexattr_destroy");
0
}
pub fn _pthread_mutexattr_init(ctx: &mut Ctx, a: i32) -> i32 {
debug!("emscripten::_pthread_mutexattr_init");
0
}
pub fn _pthread_mutexattr_settype(ctx: &mut Ctx, a: i32, b: i32) -> i32 {
debug!("emscripten::_pthread_mutexattr_settype");
0
}
pub fn _pthread_rwlock_rdlock(ctx: &mut Ctx, a: i32) -> i32 {
debug!("emscripten::_pthread_rwlock_rdlock");
0
}
pub fn _pthread_rwlock_unlock(ctx: &mut Ctx, a: i32) -> i32 {
debug!("emscripten::_pthread_rwlock_unlock");
0
}
pub fn ___gxx_personality_v0(ctx: &mut Ctx, a: i32, b: i32, c: i32, d: i32, e: i32, f: i32) -> i32 {
debug!("emscripten::___gxx_personality_v0");
0
}

View File

@ -27,6 +27,7 @@ mod file_descriptor;
pub mod stdio;
// EMSCRIPTEN APIS
mod emscripten_target;
mod env;
mod errno;
mod exception;
@ -92,8 +93,17 @@ pub struct EmscriptenData<'a> {
pub memalign: Option<Func<'a, (u32, u32), u32>>,
pub memset: Func<'a, (u32, u32, u32), u32>,
pub stack_alloc: Func<'a, u32, u32>,
pub jumps: Vec<UnsafeCell<[u32; 27]>>,
pub dyn_call_i: Option<Func<'a, i32, i32>>,
pub dyn_call_ii: Option<Func<'a, (i32, i32), i32>>,
pub dyn_call_iii: Option<Func<'a, (i32, i32, i32), i32>>,
pub dyn_call_iiii: Option<Func<'a, (i32, i32, i32, i32), i32>>,
pub dyn_call_v: Option<Func<'a, (i32)>>,
pub dyn_call_vi: Option<Func<'a, (i32, i32)>>,
pub dyn_call_vii: Option<Func<'a, (i32, i32, i32)>>,
pub dyn_call_viii: Option<Func<'a, (i32, i32, i32, i32)>>,
pub dyn_call_viiii: Option<Func<'a, (i32, i32, i32, i32, i32)>>,
}
impl<'a> EmscriptenData<'a> {
@ -108,6 +118,16 @@ impl<'a> EmscriptenData<'a> {
let memset = instance.func("_memset").unwrap();
let stack_alloc = instance.func("stackAlloc").unwrap();
let dyn_call_i = instance.func("dynCall_i").ok();
let dyn_call_ii = instance.func("dynCall_ii").ok();
let dyn_call_iii = instance.func("dynCall_iii").ok();
let dyn_call_iiii = instance.func("dynCall_iiii").ok();
let dyn_call_v = instance.func("dynCall_v").ok();
let dyn_call_vi = instance.func("dynCall_vi").ok();
let dyn_call_vii = instance.func("dynCall_vii").ok();
let dyn_call_viii = instance.func("dynCall_viii").ok();
let dyn_call_viiii = instance.func("dynCall_viiii").ok();
EmscriptenData {
malloc,
free,
@ -115,6 +135,15 @@ impl<'a> EmscriptenData<'a> {
memset,
stack_alloc,
jumps: Vec::new(),
dyn_call_i,
dyn_call_ii,
dyn_call_iii,
dyn_call_iiii,
dyn_call_v,
dyn_call_vi,
dyn_call_vii,
dyn_call_viii,
dyn_call_viiii,
}
}
}
@ -491,6 +520,42 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject
"_dlopen" => func!(crate::linking::_dlopen),
"_dlsym" => func!(crate::linking::_dlsym),
// wasm32-unknown-emscripten
"setTempRet0" => func!(crate::emscripten_target::setTempRet0),
"getTempRet0" => func!(crate::emscripten_target::getTempRet0),
"nullFunc_ji" => func!(crate::emscripten_target::nullFunc_ji),
"invoke_i" => func!(crate::emscripten_target::invoke_i),
"invoke_ii" => func!(crate::emscripten_target::invoke_ii),
"invoke_iii" => func!(crate::emscripten_target::invoke_iii),
"invoke_iiii" => func!(crate::emscripten_target::invoke_iiii),
"invoke_v" => func!(crate::emscripten_target::invoke_v),
"invoke_vi" => func!(crate::emscripten_target::invoke_vi),
"invoke_vii" => func!(crate::emscripten_target::invoke_vii),
"invoke_viii" => func!(crate::emscripten_target::invoke_viii),
"invoke_viiii" => func!(crate::emscripten_target::invoke_viiii),
"__Unwind_Backtrace" => func!(crate::emscripten_target::__Unwind_Backtrace),
"__Unwind_FindEnclosingFunction" => func!(crate::emscripten_target::__Unwind_FindEnclosingFunction),
"__Unwind_GetIPInfo" => func!(crate::emscripten_target::__Unwind_GetIPInfo),
"___cxa_find_matching_catch_2" => func!(crate::emscripten_target::___cxa_find_matching_catch_2),
"___cxa_find_matching_catch_3" => func!(crate::emscripten_target::___cxa_find_matching_catch_3),
"___cxa_free_exception" => func!(crate::emscripten_target::___cxa_free_exception),
"___resumeException" => func!(crate::emscripten_target::___resumeException),
"_dladdr" => func!(crate::emscripten_target::_dladdr),
"_pthread_cond_destroy" => func!(crate::emscripten_target::_pthread_cond_destroy),
"_pthread_cond_init" => func!(crate::emscripten_target::_pthread_cond_init),
"_pthread_cond_signal" => func!(crate::emscripten_target::_pthread_cond_signal),
"_pthread_cond_wait" => func!(crate::emscripten_target::_pthread_cond_wait),
"_pthread_condattr_destroy" => func!(crate::emscripten_target::_pthread_condattr_destroy),
"_pthread_condattr_init" => func!(crate::emscripten_target::_pthread_condattr_init),
"_pthread_condattr_setclock" => func!(crate::emscripten_target::_pthread_condattr_setclock),
"_pthread_mutex_destroy" => func!(crate::emscripten_target::_pthread_mutex_destroy),
"_pthread_mutex_init" => func!(crate::emscripten_target::_pthread_mutex_init),
"_pthread_mutexattr_destroy" => func!(crate::emscripten_target::_pthread_mutexattr_destroy),
"_pthread_mutexattr_init" => func!(crate::emscripten_target::_pthread_mutexattr_init),
"_pthread_mutexattr_settype" => func!(crate::emscripten_target::_pthread_mutexattr_settype),
"_pthread_rwlock_rdlock" => func!(crate::emscripten_target::_pthread_rwlock_rdlock),
"_pthread_rwlock_unlock" => func!(crate::emscripten_target::_pthread_rwlock_unlock),
"___gxx_personality_v0" => func!(crate::emscripten_target::___gxx_personality_v0),
},
"global" => {
"NaN" => Global::new(Value::F64(f64::NAN)),

View File

@ -0,0 +1,25 @@
[package]
name = "wasmer-runtime-c-api"
version = "0.1.4"
description = "Wasmer c-api library"
license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
repository = "https://github.com/wasmerio/wasmer"
edition = "2018"
readme = "README.md"
[dependencies]
wasmer-runtime = { path = "../runtime", version = "0.1.2" }
wasmer-runtime-core = { path = "../runtime-core", version = "0.1.2" }
libc = "0.2"
[lib]
crate-type = ["cdylib"]
[build-dependencies]
cbindgen = { version = "0.7.1", optional = true }
[features]
generate-c-api-headers = ["cbindgen"]

View File

@ -0,0 +1,10 @@
# Wasmer Runtime C API
## Generating header files
Run `make capi` from wasmer project root directory
## Running tests
The tests can be run via `cargo test`, E.g. `cargo test -p wasmer-runtime-c-api -- --nocapture`
*Running manually*
`cmake . && make && make test` from the `lib/runtime-c-api/tests` directory

View File

@ -0,0 +1,39 @@
#[cfg(feature = "generate-c-api-headers")]
extern crate cbindgen;
use std::env;
static CAPI_ENV_VAR: &str = "WASM_EMSCRIPTEN_GENERATE_C_API_HEADERS";
fn main() {
if env::var(CAPI_ENV_VAR).unwrap_or("0".to_string()) == "1" {
build();
}
}
#[cfg(feature = "generate-c-api-headers")]
fn build() {
let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
use cbindgen::Language;
cbindgen::Builder::new()
.with_crate(crate_dir.clone())
.with_language(Language::C)
.with_include_guard("WASMER_H")
.generate()
.expect("Unable to generate C bindings")
.write_to_file("wasmer.h");
cbindgen::Builder::new()
.with_crate(crate_dir)
.with_language(Language::Cxx)
.with_include_guard("WASMER_H")
.generate()
.expect("Unable to generate C++ bindings")
.write_to_file("wasmer.hh");
}
#[cfg(not(feature = "generate-c-api-headers"))]
fn build() {
panic!("environment var set to generate wasmer c API headers but generate-c-api-headers feature not enabled")
}

1105
lib/runtime-c-api/src/lib.rs Normal file

File diff suppressed because it is too large Load Diff

20
lib/runtime-c-api/tests/.gitignore vendored Normal file
View File

@ -0,0 +1,20 @@
CMakeLists.txt.user
CMakeCache.txt
CMakeFiles
CMakeScripts
Testing
Makefile
cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
_deps
test-globals
test-exports
test-instantiate
test-import-function
test-memory
test-module
test-tables
test-validate
rust-build

View File

@ -0,0 +1,48 @@
cmake_minimum_required (VERSION 2.6)
project (WasmerCApiTests)
add_executable(test-exports test-exports.c)
add_executable(test-globals test-globals.c)
add_executable(test-instantiate test-instantiate.c)
add_executable(test-import-function test-import-function.c)
add_executable(test-memory test-memory.c)
add_executable(test-module test-module.c)
add_executable(test-validate test-validate.c)
add_executable(test-tables test-tables.c)
find_library(
WASMER_LIB NAMES libwasmer_runtime_c_api.dylib libwasmer_runtime_c_api.so libwasmer_runtime_c_api.dll
PATHS ${CMAKE_SOURCE_DIR}/../../../target/debug/
)
if(NOT WASMER_LIB)
message(FATAL_ERROR "wasmer library not found")
endif()
target_link_libraries(test-exports
general ${WASMER_LIB})
target_link_libraries(test-globals
general ${WASMER_LIB})
target_link_libraries(test-instantiate
general ${WASMER_LIB})
target_link_libraries(test-import-function
general ${WASMER_LIB})
target_link_libraries(test-memory
general ${WASMER_LIB})
target_link_libraries(test-module
general ${WASMER_LIB})
target_link_libraries(test-validate
general ${WASMER_LIB})
target_link_libraries(test-tables
general ${WASMER_LIB})
enable_testing()
add_test(test-exports test-exports)
add_test(test-globals test-globals)
add_test(test-instantiate test-instantiate)
add_test(test-import-function test-import-function)
add_test(test-memory test-memory)
add_test(test-module test-module)
add_test(test-validate test-validate)
add_test(test-tables test-tables)

View File

@ -0,0 +1,39 @@
use std::process::Command;
#[test]
fn test_c_api() {
let project_tests_dir = concat!(env!("CARGO_MANIFEST_DIR"), "/tests");
run_command("cmake", project_tests_dir, Some("."));
run_command("make", project_tests_dir, None);
run_command("make", project_tests_dir, Some("test"));
}
fn run_command(command_str: &str, dir: &str, arg: Option<&str>) {
println!("Running command: `{}` arg: {:?}", command_str, arg);
let mut command = Command::new(command_str);
if let Some(a) = arg {
command.arg(a);
}
command.current_dir(dir);
let result = command.output();
match result {
Ok(r) => {
println!("output:");
if let Some(code) = r.status.code() {
println!("status: {}", code);
} else {
println!("status: None");
}
println!("stdout:");
println!("{}", String::from_utf8_lossy(&r.stdout[..]));
println!("stderr:");
println!("{}", String::from_utf8_lossy(&r.stderr[..]));
if r.status.success() {
assert!(true)
} else {
panic!("Command failed with exit status: {:?}", r.status);
}
}
Err(e) => panic!("Command failed: {}", e),
}
}

Binary file not shown.

View File

@ -0,0 +1,86 @@
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
int main()
{
// Read the wasm file bytes
FILE *file = fopen("sum.wasm", "r");
fseek(file, 0, SEEK_END);
long len = ftell(file);
uint8_t *bytes = malloc(len);
fseek(file, 0, SEEK_SET);
fread(bytes, 1, len, file);
fclose(file);
wasmer_import_t imports[] = {};
wasmer_instance_t *instance = NULL;
wasmer_result_t compile_result = wasmer_instantiate(&instance, bytes, len, imports, 0);
printf("Compile result: %d\n", compile_result);
assert(compile_result == WASMER_OK);
wasmer_exports_t *exports = NULL;
wasmer_instance_exports(instance, &exports);
int exports_len = wasmer_exports_len(exports);
printf("exports_len: %d\n", exports_len);
assert(exports_len == 1);
wasmer_export_t *export = wasmer_exports_get(exports, 0);
wasmer_import_export_kind kind = wasmer_export_kind(export);
assert(kind == WASM_FUNCTION);
wasmer_func_t *func = wasmer_export_to_func(export);
wasmer_byte_array name_bytes = wasmer_export_name(export);
assert(name_bytes.bytes_len == 3);
char expected[] = {'s', 'u', 'm'};
for(int idx = 0; idx < 3; idx++){
printf("%c\n", name_bytes.bytes[idx]);
assert(name_bytes.bytes[idx] == expected[idx]);
}
uint32_t params_arity;
wasmer_func_params_arity(func, &params_arity);
assert(params_arity == 2);
wasmer_value_tag *params_sig = malloc(sizeof(wasmer_value_tag) * params_arity);
wasmer_func_params(func, params_sig , params_arity);
assert(params_sig[0] == WASM_I32);
assert(params_sig[1] == WASM_I32);
free(params_sig);
uint32_t returns_arity;
wasmer_func_returns_arity(func, &returns_arity);
assert(returns_arity == 1);
wasmer_value_tag *returns_sig = malloc(sizeof(wasmer_value_tag) * returns_arity);
wasmer_func_returns(func, returns_sig , returns_arity);
assert(returns_sig[0] == WASM_I32);
free(returns_sig);
// wasmer_value_t param_one;
// param_one.tag = WASM_I32;
// param_one.value.I32 = 7;
// wasmer_value_t param_two;
// param_two.tag = WASM_I32;
// param_two.value.I32 = 8;
// wasmer_value_t params[] = {param_one, param_two};
// wasmer_value_t result_one;
// wasmer_value_t results[] = {result_one};
//
// wasmer_result_t call_result = wasmer_func_call(func, params, 2, results, 1);
// printf("Call result: %d\n", call_result);
// printf("Result: %d\n", results[0].value.I32);
// assert(results[0].value.I32 == 15);
// assert(call_result == WASMER_OK);
printf("Destroy instance\n");
wasmer_instance_destroy(instance);
printf("Destroy exports\n");
wasmer_exports_destroy(exports);
return 0;
}

View File

@ -0,0 +1,30 @@
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
int main()
{
wasmer_value_t val;
val.tag = WASM_I32;
val.value.I32 = 7;
wasmer_global_t *global = wasmer_global_new(val, true);
wasmer_value_t get_val = wasmer_global_get(global);
assert( get_val.value.I32 == 7);
wasmer_value_t val2;
val2.tag = WASM_I32;
val2.value.I32 = 14;
wasmer_global_set(global, val2);
wasmer_value_t new_get_val = wasmer_global_get(global);
assert( new_get_val.value.I32 == 14);
wasmer_global_descriptor_t desc = wasmer_global_get_descriptor(global);
assert(desc.mutable_);
assert(desc.kind == WASM_I32);
wasmer_global_destroy(global);
return 0;
}

View File

@ -0,0 +1,91 @@
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
#include <string.h>
static print_str_called = false;
static memory_len = 0;
static ptr_len = 0;
static char actual_str[14] = {};
void print_str(wasmer_instance_context_t *ctx, int32_t ptr, int32_t len)
{
wasmer_memory_t *memory = wasmer_instance_context_memory(ctx, 0);
uint32_t mem_len = wasmer_memory_length(memory);
uint8_t *mem_bytes = wasmer_memory_data(memory);
for (int32_t idx = 0; idx < len; idx++)
{
actual_str[idx] = mem_bytes[ptr + idx];
}
actual_str[13] = '\0';
printf("In print_str, memory len: %d, ptr_len: %d\n, str %s", mem_len, len, actual_str);
print_str_called = true;
memory_len = mem_len;
ptr_len = len;
}
int main()
{
wasmer_value_tag params_sig[] = {WASM_I32, WASM_I32};
wasmer_value_tag returns_sig[] = {};
printf("Creating new func\n");
wasmer_func_t *func = wasmer_func_new(print_str, params_sig, 2, returns_sig, 0);
wasmer_import_t import;
char *module_name = "env";
wasmer_byte_array module_name_bytes;
module_name_bytes.bytes = module_name;
module_name_bytes.bytes_len = strlen(module_name);
char *import_name = "print_str";
wasmer_byte_array import_name_bytes;
import_name_bytes.bytes = import_name;
import_name_bytes.bytes_len = strlen(import_name);
import.module_name = module_name_bytes;
import.import_name = import_name_bytes;
import.tag = WASM_FUNCTION;
import.value.func = func;
wasmer_import_t imports[] = {import};
// Read the wasm file bytes
FILE *file = fopen("wasm_sample_app.wasm", "r");
fseek(file, 0, SEEK_END);
long len = ftell(file);
uint8_t *bytes = malloc(len);
fseek(file, 0, SEEK_SET);
fread(bytes, 1, len, file);
fclose(file);
printf("Instantiating\n");
wasmer_instance_t *instance = NULL;
wasmer_result_t compile_result = wasmer_instantiate(&instance, bytes, len, imports, 1);
printf("Compile result: %d\n", compile_result);
assert(compile_result == WASMER_OK);
wasmer_value_t params[] = {};
wasmer_value_t results[] = {};
wasmer_result_t call_result = wasmer_instance_call(instance, "hello_wasm", params, 0, results, 0);
printf("Call result: %d\n", call_result);
int error_len = wasmer_last_error_length();
printf("Error len: `%d`\n", error_len);
char *error_str = malloc(error_len);
wasmer_last_error_message(error_str, error_len);
printf("Error str: `%s`\n", error_str);
assert(call_result == WASMER_OK);
assert(print_str_called);
assert(memory_len == 17);
assert(ptr_len == 13);
assert(0 == strcmp(actual_str, "Hello, World!"));
printf("Destroying func\n");
wasmer_func_destroy(func);
printf("Destroy instance\n");
wasmer_instance_destroy(instance);
return 0;
}

View File

@ -0,0 +1,56 @@
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
int main()
{
// Read the wasm file bytes
FILE *file = fopen("sum.wasm", "r");
fseek(file, 0, SEEK_END);
long len = ftell(file);
uint8_t *bytes = malloc(len);
fseek(file, 0, SEEK_SET);
fread(bytes, 1, len, file);
fclose(file);
wasmer_import_t imports[] = {};
wasmer_instance_t *instance = NULL;
wasmer_result_t compile_result = wasmer_instantiate(&instance, bytes, len, imports, 0);
printf("Compile result: %d\n", compile_result);
assert(compile_result == WASMER_OK);
wasmer_value_t param_one;
param_one.tag = WASM_I32;
param_one.value.I32 = 7;
wasmer_value_t param_two;
param_two.tag = WASM_I32;
param_two.value.I32 = 8;
wasmer_value_t params[] = {param_one, param_two};
wasmer_value_t result_one;
wasmer_value_t results[] = {result_one};
wasmer_result_t call_result = wasmer_instance_call(instance, "sum", params, 2, results, 1);
printf("Call result: %d\n", call_result);
printf("Result: %d\n", results[0].value.I32);
assert(results[0].value.I32 == 15);
assert(call_result == WASMER_OK);
wasmer_result_t call_result2 = wasmer_instance_call(instance, "sum", params, 1, results, 1);
printf("Call result bad: %d\n", call_result2);
assert(call_result2 == WASMER_ERROR);
int error_len = wasmer_last_error_length();
printf("Error len: `%d`\n", error_len);
char *error_str = malloc(error_len);
wasmer_last_error_message(error_str, error_len);
printf("Error str: `%s`\n", error_str);
assert(0 == strcmp(error_str, "Call error: Parameters of type [I32] did not match signature [I32, I32] -> [I32]"));
free(error_str);
printf("Destroy instance\n");
wasmer_instance_destroy(instance);
return 0;
}

View File

@ -0,0 +1,62 @@
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
int main()
{
wasmer_memory_t *memory = NULL;
wasmer_limits_t descriptor;
descriptor.min = 10;
wasmer_limit_option_t max;
max.has_some = true;
max.some = 15;
descriptor.max = max;
wasmer_result_t memory_result = wasmer_memory_new(&memory, descriptor);
printf("Memory result: %d\n", memory_result);
assert(memory_result == WASMER_OK);
uint32_t len = wasmer_memory_length(memory);
printf("Memory pages length: %d\n", len);
assert(len == 10);
wasmer_result_t grow_result = wasmer_memory_grow(memory, 2);
assert(grow_result == WASMER_OK);
uint32_t new_len = wasmer_memory_length(memory);
printf("Memory pages length: %d\n", new_len);
assert(new_len == 12);
uint32_t bytes_len = wasmer_memory_data_length(memory);
printf("Memory bytes length: %d\n", bytes_len);
assert(bytes_len == 12 * 65536);
// Err, grow beyond max
wasmer_result_t grow_result2 = wasmer_memory_grow(memory, 10);
assert(grow_result2 == WASMER_ERROR);
int error_len = wasmer_last_error_length();
char *error_str = malloc(error_len);
wasmer_last_error_message(error_str, error_len);
printf("Error str: `%s`\n", error_str);
assert(0 == strcmp(error_str, "unable to grow memory"));
free(error_str);
// wasmer_memory_t *bad_memory = NULL;
// wasmer_limits_t bad_descriptor;
// bad_descriptor.min = 15;
// bad_descriptor.max = 10;
// wasmer_result_t bad_memory_result = wasmer_memory_new(&bad_memory, bad_descriptor);
// printf("Bad memory result: %d\n", bad_memory_result);
// assert(memory_result == WASMER_MEMORY_ERROR);
//
// int error_len = wasmer_last_error_length();
// char *error_str = malloc(error_len);
// wasmer_last_error_message(error_str, error_len);
// assert(0 == strcmp(error_str, "Creation error"));
// free(error_str);
printf("Destroy memory\n");
wasmer_memory_destroy(memory);
return 0;
}

View File

@ -0,0 +1,25 @@
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
int main()
{
// Read the wasm file bytes
FILE *file = fopen("sum.wasm", "r");
fseek(file, 0, SEEK_END);
long len = ftell(file);
uint8_t *bytes = malloc(len);
fseek(file, 0, SEEK_SET);
fread(bytes, 1, len, file);
fclose(file);
wasmer_module_t *module = NULL;
wasmer_result_t compile_result = wasmer_compile(&module, bytes, len);
printf("Compile result: %d\n", compile_result);
assert(compile_result == WASMER_OK);
printf("Destroy module\n");
wasmer_module_destroy(module);
return 0;
}

View File

@ -0,0 +1,48 @@
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
int main()
{
wasmer_table_t *table = NULL;
wasmer_limits_t descriptor;
descriptor.min = 10;
wasmer_limit_option_t max;
// max.has_some = false;
max.has_some = true;
max.some = 15;
descriptor.max = max;
wasmer_result_t table_result = wasmer_table_new(&table, descriptor);
printf("Table result: %d\n", table_result);
assert(table_result == WASMER_OK);
uint32_t len = wasmer_table_length(table);
printf("Table length: %d\n", len);
assert(len == 15);
// wasmer_result_t grow_result1 = wasmer_table_grow(table, 5);
// assert(grow_result1 == WASMER_OK);
// uint32_t len_grow1 = wasmer_table_length(table);
// printf("Table length: %d\n", len_grow1);
// assert(len_grow1 == 15);
// // Try to grow beyond max
// wasmer_result_t grow_result2 = wasmer_table_grow(&table, 1);
// assert(grow_result2 == WASMER_ERROR);
// uint32_t len_grow2 = wasmer_table_length(table);
// printf("Table length: %d\n", len_grow2);
// assert(len_grow2 == 15);
// wasmer_table_t *table_bad = NULL;
// wasmer_limits_t bad_descriptor;
// bad_descriptor.min = 15;
// bad_descriptor.max = 10;
// wasmer_result_t table_bad_result = wasmer_table_new(&table_bad, bad_descriptor);
// printf("Table result: %d\n", table_bad_result);
// assert(table_result == WASMER_ERROR);
printf("Destroy table\n");
wasmer_table_destroy(table);
return 0;
}

View File

@ -0,0 +1,22 @@
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
int main()
{
// Read the wasm file bytes
FILE *file = fopen("sum.wasm", "r");
fseek(file, 0, SEEK_END);
long len = ftell(file);
uint8_t *bytes = malloc(len);
fseek(file, 0, SEEK_SET);
fread(bytes, 1, len, file);
fclose(file);
bool result = wasmer_validate(bytes, len);
printf("Result: %d", result);
assert(result);
return 0;
}

Binary file not shown.

381
lib/runtime-c-api/wasmer.h Normal file
View File

@ -0,0 +1,381 @@
#ifndef WASMER_H
#define WASMER_H
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
enum wasmer_import_export_kind {
WASM_FUNCTION,
WASM_GLOBAL,
WASM_MEMORY,
WASM_TABLE,
};
typedef uint32_t wasmer_import_export_kind;
typedef enum {
WASMER_OK = 1,
WASMER_ERROR = 2,
} wasmer_result_t;
enum wasmer_value_tag {
WASM_I32,
WASM_I64,
WASM_F32,
WASM_F64,
};
typedef uint32_t wasmer_value_tag;
typedef struct wasmer_instance_context_t wasmer_instance_context_t;
typedef struct wasmer_instance_t wasmer_instance_t;
typedef struct wasmer_module_t wasmer_module_t;
typedef struct {
} wasmer_export_t;
typedef struct {
const uint8_t *bytes;
uint32_t bytes_len;
} wasmer_byte_array;
typedef struct {
} wasmer_func_t;
typedef struct {
} wasmer_exports_t;
typedef union {
int32_t I32;
int64_t I64;
float F32;
double F64;
} wasmer_value;
typedef struct {
wasmer_value_tag tag;
wasmer_value value;
} wasmer_value_t;
typedef struct {
} wasmer_global_t;
typedef struct {
bool mutable_;
wasmer_value_tag kind;
} wasmer_global_descriptor_t;
typedef struct {
} wasmer_memory_t;
typedef struct {
} wasmer_table_t;
typedef union {
const wasmer_func_t *func;
const wasmer_table_t *table;
const wasmer_memory_t *memory;
const wasmer_global_t *global;
} wasmer_import_export_value;
typedef struct {
wasmer_byte_array module_name;
wasmer_byte_array import_name;
wasmer_import_export_kind tag;
wasmer_import_export_value value;
} wasmer_import_t;
typedef struct {
bool has_some;
uint32_t some;
} wasmer_limit_option_t;
typedef struct {
uint32_t min;
wasmer_limit_option_t max;
} wasmer_limits_t;
/**
* Creates a new Module from the given wasm bytes.
* Returns `wasmer_result_t::WASMER_OK` upon success.
* Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
* and `wasmer_last_error_message` to get an error message.
*/
wasmer_result_t wasmer_compile(wasmer_module_t **module,
uint8_t *wasm_bytes,
uint32_t wasm_bytes_len);
/**
* Gets wasmer_export kind
*/
wasmer_import_export_kind wasmer_export_kind(wasmer_export_t *export_);
/**
* Gets name from wasmer_export
*/
wasmer_byte_array wasmer_export_name(wasmer_export_t *export_);
/**
* Gets func from wasm_export
*/
const wasmer_func_t *wasmer_export_to_func(wasmer_export_t *export_);
/**
* Frees the memory for the given exports
*/
void wasmer_exports_destroy(wasmer_exports_t *exports);
/**
* Gets wasmer_export by index
*/
wasmer_export_t *wasmer_exports_get(wasmer_exports_t *exports, int idx);
/**
* Gets the length of the exports
*/
int wasmer_exports_len(wasmer_exports_t *exports);
/**
* Calls a `func` with the provided parameters.
* Results are set using the provided `results` pointer.
* Returns `wasmer_result_t::WASMER_OK` upon success.
* Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
* and `wasmer_last_error_message` to get an error message.
*/
wasmer_result_t wasmer_func_call(wasmer_func_t *func,
const wasmer_value_t *params,
int params_len,
wasmer_value_t *results,
int results_len);
/**
* Frees memory for the given Func
*/
void wasmer_func_destroy(wasmer_func_t *func);
/**
* Creates new func
* The caller owns the object and should call `wasmer_func_destroy` to free it.
*/
const wasmer_func_t *wasmer_func_new(void (*func)(void *data),
const wasmer_value_tag *params,
int params_len,
const wasmer_value_tag *returns,
int returns_len);
/**
* Sets the params buffer to the parameter types of the given wasmer_func_t
* Returns `wasmer_result_t::WASMER_OK` upon success.
* Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
* and `wasmer_last_error_message` to get an error message.
*/
wasmer_result_t wasmer_func_params(wasmer_func_t *func, wasmer_value_tag *params, int params_len);
/**
* Sets the result parameter to the arity of the params of the wasmer_func_t
* Returns `wasmer_result_t::WASMER_OK` upon success.
* Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
* and `wasmer_last_error_message` to get an error message.
*/
wasmer_result_t wasmer_func_params_arity(wasmer_func_t *func, uint32_t *result);
/**
* Sets the returns buffer to the parameter types of the given wasmer_func_t
* Returns `wasmer_result_t::WASMER_OK` upon success.
* Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
* and `wasmer_last_error_message` to get an error message.
*/
wasmer_result_t wasmer_func_returns(wasmer_func_t *func,
wasmer_value_tag *returns,
int returns_len);
/**
* Sets the result parameter to the arity of the returns of the wasmer_func_t
* Returns `wasmer_result_t::WASMER_OK` upon success.
* Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
* and `wasmer_last_error_message` to get an error message.
*/
wasmer_result_t wasmer_func_returns_arity(wasmer_func_t *func, uint32_t *result);
/**
* Frees memory for the given Global
*/
void wasmer_global_destroy(wasmer_global_t *global);
/**
* Gets the value stored by the given Global
*/
wasmer_value_t wasmer_global_get(wasmer_global_t *global);
/**
* Returns a descriptor (type, mutability) of the given Global
*/
wasmer_global_descriptor_t wasmer_global_get_descriptor(wasmer_global_t *global);
/**
* Creates a new Global and returns a pointer to it.
* The caller owns the object and should call `wasmer_global_destroy` to free it.
*/
wasmer_global_t *wasmer_global_new(wasmer_value_t value, bool mutable_);
/**
* Sets the value stored by the given Global
*/
void wasmer_global_set(wasmer_global_t *global, wasmer_value_t value);
/**
* Calls an instances exported function by `name` with the provided parameters.
* Results are set using the provided `results` pointer.
* Returns `wasmer_result_t::WASMER_OK` upon success.
* Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
* and `wasmer_last_error_message` to get an error message.
*/
wasmer_result_t wasmer_instance_call(wasmer_instance_t *instance,
const char *name,
const wasmer_value_t *params,
int params_len,
wasmer_value_t *results,
int results_len);
/**
* Gets the memory within the context at the index `memory_idx`.
* The index is always 0 until multiple memories are supported.
*/
const wasmer_memory_t *wasmer_instance_context_memory(wasmer_instance_context_t *ctx,
uint32_t memory_idx);
/**
* Frees memory for the given Instance
*/
void wasmer_instance_destroy(wasmer_instance_t *instance);
/**
* Gets Exports for the given instance
* The caller owns the object and should call `wasmer_exports_destroy` to free it.
*/
void wasmer_instance_exports(wasmer_instance_t *instance, wasmer_exports_t **exports);
/**
* Creates a new Instance from the given wasm bytes and imports.
* Returns `wasmer_result_t::WASMER_OK` upon success.
* Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
* and `wasmer_last_error_message` to get an error message.
*/
wasmer_result_t wasmer_instantiate(wasmer_instance_t **instance,
uint8_t *wasm_bytes,
uint32_t wasm_bytes_len,
wasmer_import_t *imports,
int imports_len);
/**
* Gets the length in bytes of the last error.
* This can be used to dynamically allocate a buffer with the correct number of
* bytes needed to store a message.
* # Example
* ```
* int error_len = wasmer_last_error_length();
* char *error_str = malloc(error_len);
* ```
*/
int wasmer_last_error_length(void);
/**
* Stores the last error message into the provided buffer up to the given `length`.
* The `length` parameter must be large enough to store the last error message.
* Returns the length of the string in bytes.
* Returns `-1` if an error occurs.
* # Example
* ```
* int error_len = wasmer_last_error_length();
* char *error_str = malloc(error_len);
* wasmer_last_error_message(error_str, error_len);
* printf("Error str: `%s`\n", error_str);
* ```
*/
int wasmer_last_error_message(char *buffer, int length);
/**
* Gets the start pointer to the bytes within a Memory
*/
uint8_t *wasmer_memory_data(wasmer_memory_t *mem);
/**
* Gets the size in bytes of a Memory
*/
uint32_t wasmer_memory_data_length(wasmer_memory_t *mem);
/**
* Frees memory for the given Memory
*/
void wasmer_memory_destroy(wasmer_memory_t *memory);
/**
* Grows a Memory by the given number of pages.
* Returns `wasmer_result_t::WASMER_OK` upon success.
* Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
* and `wasmer_last_error_message` to get an error message.
*/
wasmer_result_t wasmer_memory_grow(wasmer_memory_t *memory, uint32_t delta);
/**
* Returns the current length in pages of the given memory
*/
uint32_t wasmer_memory_length(wasmer_memory_t *memory);
/**
* Creates a new Memory for the given descriptor and initializes the given
* pointer to pointer to a pointer to the new memory.
* The caller owns the object and should call `wasmer_memory_destroy` to free it.
* Returns `wasmer_result_t::WASMER_OK` upon success.
* Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
* and `wasmer_last_error_message` to get an error message.
*/
wasmer_result_t wasmer_memory_new(wasmer_memory_t **memory, wasmer_limits_t limits);
/**
* Frees memory for the given Module
*/
void wasmer_module_destroy(wasmer_module_t *module);
/**
* Frees memory for the given Table
*/
void wasmer_table_destroy(wasmer_table_t *table);
/**
* Grows a Table by the given number of elements.
* Returns `wasmer_result_t::WASMER_OK` upon success.
* Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
* and `wasmer_last_error_message` to get an error message.
*/
wasmer_result_t wasmer_table_grow(wasmer_table_t *table, uint32_t delta);
/**
* Returns the current length of the given Table
*/
uint32_t wasmer_table_length(wasmer_table_t *table);
/**
* Creates a new Table for the given descriptor and initializes the given
* pointer to pointer to a pointer to the new Table.
* The caller owns the object and should call `wasmer_table_destroy` to free it.
* Returns `wasmer_result_t::WASMER_OK` upon success.
* Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
* and `wasmer_last_error_message` to get an error message.
*/
wasmer_result_t wasmer_table_new(wasmer_table_t **table, wasmer_limits_t limits);
/**
* Returns true for valid wasm bytes and false for invalid bytes
*/
bool wasmer_validate(uint8_t *wasm_bytes, uint32_t wasm_bytes_len);
#endif /* WASMER_H */

306
lib/runtime-c-api/wasmer.hh Normal file
View File

@ -0,0 +1,306 @@
#ifndef WASMER_H
#define WASMER_H
#include <cstdarg>
#include <cstdint>
#include <cstdlib>
enum class wasmer_import_export_kind : uint32_t {
WASM_FUNCTION,
WASM_GLOBAL,
WASM_MEMORY,
WASM_TABLE,
};
enum class wasmer_result_t {
WASMER_OK = 1,
WASMER_ERROR = 2,
};
enum class wasmer_value_tag : uint32_t {
WASM_I32,
WASM_I64,
WASM_F32,
WASM_F64,
};
struct wasmer_instance_context_t;
struct wasmer_instance_t;
struct wasmer_module_t;
struct wasmer_export_t {
};
struct wasmer_byte_array {
const uint8_t *bytes;
uint32_t bytes_len;
};
struct wasmer_func_t {
};
struct wasmer_exports_t {
};
union wasmer_value {
int32_t I32;
int64_t I64;
float F32;
double F64;
};
struct wasmer_value_t {
wasmer_value_tag tag;
wasmer_value value;
};
struct wasmer_global_t {
};
struct wasmer_global_descriptor_t {
bool mutable_;
wasmer_value_tag kind;
};
struct wasmer_memory_t {
};
struct wasmer_table_t {
};
union wasmer_import_export_value {
const wasmer_func_t *func;
const wasmer_table_t *table;
const wasmer_memory_t *memory;
const wasmer_global_t *global;
};
struct wasmer_import_t {
wasmer_byte_array module_name;
wasmer_byte_array import_name;
wasmer_import_export_kind tag;
wasmer_import_export_value value;
};
struct wasmer_limit_option_t {
bool has_some;
uint32_t some;
};
struct wasmer_limits_t {
uint32_t min;
wasmer_limit_option_t max;
};
extern "C" {
/// Creates a new Module from the given wasm bytes.
/// Returns `wasmer_result_t::WASMER_OK` upon success.
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
/// and `wasmer_last_error_message` to get an error message.
wasmer_result_t wasmer_compile(wasmer_module_t **module,
uint8_t *wasm_bytes,
uint32_t wasm_bytes_len);
/// Gets wasmer_export kind
wasmer_import_export_kind wasmer_export_kind(wasmer_export_t *export_);
/// Gets name from wasmer_export
wasmer_byte_array wasmer_export_name(wasmer_export_t *export_);
/// Gets func from wasm_export
const wasmer_func_t *wasmer_export_to_func(wasmer_export_t *export_);
/// Frees the memory for the given exports
void wasmer_exports_destroy(wasmer_exports_t *exports);
/// Gets wasmer_export by index
wasmer_export_t *wasmer_exports_get(wasmer_exports_t *exports, int idx);
/// Gets the length of the exports
int wasmer_exports_len(wasmer_exports_t *exports);
/// Calls a `func` with the provided parameters.
/// Results are set using the provided `results` pointer.
/// Returns `wasmer_result_t::WASMER_OK` upon success.
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
/// and `wasmer_last_error_message` to get an error message.
wasmer_result_t wasmer_func_call(wasmer_func_t *func,
const wasmer_value_t *params,
int params_len,
wasmer_value_t *results,
int results_len);
/// Frees memory for the given Func
void wasmer_func_destroy(wasmer_func_t *func);
/// Creates new func
/// The caller owns the object and should call `wasmer_func_destroy` to free it.
const wasmer_func_t *wasmer_func_new(void (*func)(void *data),
const wasmer_value_tag *params,
int params_len,
const wasmer_value_tag *returns,
int returns_len);
/// Sets the params buffer to the parameter types of the given wasmer_func_t
/// Returns `wasmer_result_t::WASMER_OK` upon success.
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
/// and `wasmer_last_error_message` to get an error message.
wasmer_result_t wasmer_func_params(wasmer_func_t *func, wasmer_value_tag *params, int params_len);
/// Sets the result parameter to the arity of the params of the wasmer_func_t
/// Returns `wasmer_result_t::WASMER_OK` upon success.
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
/// and `wasmer_last_error_message` to get an error message.
wasmer_result_t wasmer_func_params_arity(wasmer_func_t *func, uint32_t *result);
/// Sets the returns buffer to the parameter types of the given wasmer_func_t
/// Returns `wasmer_result_t::WASMER_OK` upon success.
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
/// and `wasmer_last_error_message` to get an error message.
wasmer_result_t wasmer_func_returns(wasmer_func_t *func,
wasmer_value_tag *returns,
int returns_len);
/// Sets the result parameter to the arity of the returns of the wasmer_func_t
/// Returns `wasmer_result_t::WASMER_OK` upon success.
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
/// and `wasmer_last_error_message` to get an error message.
wasmer_result_t wasmer_func_returns_arity(wasmer_func_t *func, uint32_t *result);
/// Frees memory for the given Global
void wasmer_global_destroy(wasmer_global_t *global);
/// Gets the value stored by the given Global
wasmer_value_t wasmer_global_get(wasmer_global_t *global);
/// Returns a descriptor (type, mutability) of the given Global
wasmer_global_descriptor_t wasmer_global_get_descriptor(wasmer_global_t *global);
/// Creates a new Global and returns a pointer to it.
/// The caller owns the object and should call `wasmer_global_destroy` to free it.
wasmer_global_t *wasmer_global_new(wasmer_value_t value, bool mutable_);
/// Sets the value stored by the given Global
void wasmer_global_set(wasmer_global_t *global, wasmer_value_t value);
/// Calls an instances exported function by `name` with the provided parameters.
/// Results are set using the provided `results` pointer.
/// Returns `wasmer_result_t::WASMER_OK` upon success.
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
/// and `wasmer_last_error_message` to get an error message.
wasmer_result_t wasmer_instance_call(wasmer_instance_t *instance,
const char *name,
const wasmer_value_t *params,
int params_len,
wasmer_value_t *results,
int results_len);
/// Gets the memory within the context at the index `memory_idx`.
/// The index is always 0 until multiple memories are supported.
const wasmer_memory_t *wasmer_instance_context_memory(wasmer_instance_context_t *ctx,
uint32_t memory_idx);
/// Frees memory for the given Instance
void wasmer_instance_destroy(wasmer_instance_t *instance);
/// Gets Exports for the given instance
/// The caller owns the object and should call `wasmer_exports_destroy` to free it.
void wasmer_instance_exports(wasmer_instance_t *instance, wasmer_exports_t **exports);
/// Creates a new Instance from the given wasm bytes and imports.
/// Returns `wasmer_result_t::WASMER_OK` upon success.
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
/// and `wasmer_last_error_message` to get an error message.
wasmer_result_t wasmer_instantiate(wasmer_instance_t **instance,
uint8_t *wasm_bytes,
uint32_t wasm_bytes_len,
wasmer_import_t *imports,
int imports_len);
/// Gets the length in bytes of the last error.
/// This can be used to dynamically allocate a buffer with the correct number of
/// bytes needed to store a message.
/// # Example
/// ```
/// int error_len = wasmer_last_error_length();
/// char *error_str = malloc(error_len);
/// ```
int wasmer_last_error_length();
/// Stores the last error message into the provided buffer up to the given `length`.
/// The `length` parameter must be large enough to store the last error message.
/// Returns the length of the string in bytes.
/// Returns `-1` if an error occurs.
/// # Example
/// ```
/// int error_len = wasmer_last_error_length();
/// char *error_str = malloc(error_len);
/// wasmer_last_error_message(error_str, error_len);
/// printf("Error str: `%s`\n", error_str);
/// ```
int wasmer_last_error_message(char *buffer, int length);
/// Gets the start pointer to the bytes within a Memory
uint8_t *wasmer_memory_data(wasmer_memory_t *mem);
/// Gets the size in bytes of a Memory
uint32_t wasmer_memory_data_length(wasmer_memory_t *mem);
/// Frees memory for the given Memory
void wasmer_memory_destroy(wasmer_memory_t *memory);
/// Grows a Memory by the given number of pages.
/// Returns `wasmer_result_t::WASMER_OK` upon success.
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
/// and `wasmer_last_error_message` to get an error message.
wasmer_result_t wasmer_memory_grow(wasmer_memory_t *memory, uint32_t delta);
/// Returns the current length in pages of the given memory
uint32_t wasmer_memory_length(wasmer_memory_t *memory);
/// Creates a new Memory for the given descriptor and initializes the given
/// pointer to pointer to a pointer to the new memory.
/// The caller owns the object and should call `wasmer_memory_destroy` to free it.
/// Returns `wasmer_result_t::WASMER_OK` upon success.
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
/// and `wasmer_last_error_message` to get an error message.
wasmer_result_t wasmer_memory_new(wasmer_memory_t **memory, wasmer_limits_t limits);
/// Frees memory for the given Module
void wasmer_module_destroy(wasmer_module_t *module);
/// Frees memory for the given Table
void wasmer_table_destroy(wasmer_table_t *table);
/// Grows a Table by the given number of elements.
/// Returns `wasmer_result_t::WASMER_OK` upon success.
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
/// and `wasmer_last_error_message` to get an error message.
wasmer_result_t wasmer_table_grow(wasmer_table_t *table, uint32_t delta);
/// Returns the current length of the given Table
uint32_t wasmer_table_length(wasmer_table_t *table);
/// Creates a new Table for the given descriptor and initializes the given
/// pointer to pointer to a pointer to the new Table.
/// The caller owns the object and should call `wasmer_table_destroy` to free it.
/// Returns `wasmer_result_t::WASMER_OK` upon success.
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
/// and `wasmer_last_error_message` to get an error message.
wasmer_result_t wasmer_table_new(wasmer_table_t **table, wasmer_limits_t limits);
/// Returns true for valid wasm bytes and false for invalid bytes
bool wasmer_validate(uint8_t *wasm_bytes, uint32_t wasm_bytes_len);
} // extern "C"
#endif // WASMER_H

View File

@ -260,6 +260,8 @@ impl std::fmt::Display for CallError {
}
}
impl std::error::Error for CallError {}
/// This error type is produced when creating something,
/// like a `Memory` or a `Table`.
#[derive(Debug, Clone)]

View File

@ -1,136 +1,135 @@
This directory contains tests for the core WebAssembly semantics, as described in [Semantics.md](https://github.com/WebAssembly/design/blob/master/Semantics.md) and specified by the [spec interpreter](https://github.com/WebAssembly/spec/blob/master/interpreter/spec).
This files should be a direct copy of the original [WebAssembly spec tests](https://github.com/WebAssembly/spec/tree/master/test/core).
These files should be a direct copy of the original [WebAssembly spec tests](/test/core).
Tests are written in the [S-Expression script format](https://github.com/WebAssembly/spec/blob/master/interpreter/README.md#s-expression-syntax) defined by the interpreter.
## Autogenerated Rust test cases
This files will serve as base for autogenerating Rust testcases
These files will serve as a base for autogenerating Rust testcases
when `WASM_GENERATE_SPECTESTS=1 cargo build` is executed
([src/build_spectests.rs](https://github.com/wasmerio/wasmer/blob/master/src/build_spectests.rs)).
([src/build_spectests.rs](/src/build_spectests.rs)).
The result autogenerated spectests live in the [src/spectests](https://github.com/wasmerio/wasmer/tree/master/src/spectests)
directory.
The resulting autogenerated spec tests live in the [src/spectests](/src/spectests).
## Testcases
Currently supported command assertions:
- [x] Module _mostly implemented_ (it should support named modules `(module $Xx)`).
- [x] AssertReturn _mostly implemented_ (it should support calls to named modules `(invoke $Xx "call")`).
- [x] AssertReturnCanonicalNan _fully implemented_
- [x] AssertReturnArithmeticNan _fully implemented_
- [x] AssertTrap _fully implemented_
- [x] AssertInvalid _Fully implemented_ (it should not require to do validation separate from compilation)
- [x] AssertMalformed _Fully implemented_
- [ ] AssertUninstantiable _not implemented yet_
- [ ] AssertExhaustion _not implemented yet_
- [ ] Register _not implemented yet_
- [x] PerformAction _partially implemented, only function invokations for now_
- [x] `module` _mostly implemented_ (it should support named modules `(module $Xx)`).
- [x] `assert_return` _mostly implemented_ (it should support calls to named modules `(invoke $Xx "call")`).
- [x] `assert_return_canonical_nan` _fully implemented_
- [x] `assert_return_arithmetic_nan` _fully implemented_
- [x] `assert_trap` _fully implemented_
- [x] `assert_invalid` _fully implemented_ (it should not require validation to be performed separate from compilation)
- [x] `assert_malformed` _fully implemented_
- [ ] `assert_uninstantiable` _not implemented yet_
- [ ] `assert_exhaustion` _not implemented yet_
- [ ] `register` _not implemented yet_
- [x] `perform_action` _partially implemented, only function invocations for now_
### Covered spectests
### Covered spec tests
This spectests are currently covered:
The following spec tests are currently covered:
- address.wast
- align.wast
- binary.wast
- block.wast
- br.wast
- br_if.wast
- br_table.wast
- break-drop.wast
- call.wast
- call_indirect.wast
- comments.wast
- const.wast
- conversions.wast
- custom.wast
- data.wast
- elem.wast
- endianness.wast
- exports.wast
- f32.wast
- f32_bitwise.wast
- f32_cmp.wast
- f64.wast
- f64_bitwise.wast
- f64_cmp.wast
- fac.wast
- float_exprs.wast
- float_literals.wast
- float_memory.wast
- float_misc.wast
- forward.wast
- func.wast
- func_ptrs.wast
- get_local.wast
- globals.wast
- i32.wast
- i64.wast
- if.wast
- imports.wast
- inline-module.wast
- int_exprs.wast
- int_literals.wast
- labels.wast
- left-to-right.wast
- linking.wast
- loop.wast
- memory.wast
- memory_grow.wast
- memory_redundancy.wast
- memory_trap.wast
- names.wast
- nop.wast
- return.wast
- select.wast
- set_local.wast
- skip-stack-guard-page.wast
- stack.wast
- start.wast
- store_retval.wast
- switch.wast
- tee_local.wast
- token.wast
- traps.wast
- type.wast
- typecheck.wast
- unreachable.wast
- unreached-invalid.wast
- unwind.wast
- utf8-custom-section-id.wast
- utf8-import-field.wast
- utf8-import-module.wast
- utf8-invalid-encoding.wast
- [x] address.wast
- [x] align.wast
- [x] binary.wast
- [x] block.wast
- [x] br.wast
- [x] br_if.wast
- [x] br_table.wast
- [x] break-drop.wast
- [x] call.wast
- [x] call_indirect.wast
- [x] comments.wast
- [x] const.wast
- [x] conversions.wast
- [x] custom.wast
- [x] data.wast
- [ ] elem.wast
- [x] endianness.wast
- [x] exports.wast
- [x] f32.wast
- [x] f32_bitwise.wast
- [x] f32_cmp.wast
- [x] f64.wast
- [x] f64_bitwise.wast
- [x] f64_cmp.wast
- [x] fac.wast
- [x] float_exprs.wast
- [x] float_literals.wast
- [x] float_memory.wast
- [x] float_misc.wast
- [x] forward.wast
- [x] func.wast
- [x] func_ptrs.wast
- [x] get_local.wast
- [x] globals.wast
- [x] i32.wast
- [x] i64.wast
- [x] if.wast
- [ ] imports.wast
- [ ] inline-module.wast
- [x] int_exprs.wast
- [x] int_literals.wast
- [x] labels.wast
- [x] left-to-right.wast
- [ ] linking.wast
- [x] loop.wast
- [x] memory.wast
- [x] memory_grow.wast
- [x] memory_redundancy.wast
- [x] memory_trap.wast
- [x] names.wast
- [x] nop.wast
- [x] return.wast
- [x] select.wast
- [x] set_local.wast
- [ ] skip-stack-guard-page.wast
- [x] stack.wast
- [x] start.wast
- [x] store_retval.wast
- [x] switch.wast
- [x] tee_local.wast
- [x] token.wast
- [x] traps.wast
- [x] type.wast
- [x] typecheck.wast
- [ ] unreachable.wast
- [ ] unreached-invalid.wast
- [x] unwind.wast
- [ ] utf8-custom-section-id.wast
- [ ] utf8-import-field.wast
- [ ] utf8-import-module.wast
- [ ] utf8-invalid-encoding.wast
### Specific non-supported cases
There are some cases that we decided to skip for now to fasten the time to release:
There are some cases that we decided to skip for now to accelerate the release schedule:
- `SKIP_MUTABLE_GLOBALS`: Right now the WASM parser can't validate a module with imported/exported mut globals. We decided to skip the tests until Cranelift and wasmparser can handle this (original spec proposal: https://github.com/WebAssembly/mutable-global). Spectests affected:
- `SKIP_MUTABLE_GLOBALS`: Right now the Wasm parser can't validate a module with imported/exported `mut` globals. We decided to skip the tests until Cranelift and wasmparser can handle this (see [original spec proposal](https://github.com/WebAssembly/mutable-global)). Spec tests affected:
- `globals.wast`
- `SKIP_CALL_INDIRECT_TYPE_MISMATCH`: we implemented traps in a fast way. We haven't covered yet the type mismatch on `call_indirect`. Specs affected:
- `SKIP_CALL_INDIRECT_TYPE_MISMATCH`: we implemented traps in a fast way. We haven't yet covered the type mismatch on `call_indirect`. Specs affected:
- `call_indirect.wast`
- `SKIP_CALL_UNDEFINED_ELEMENT`
Tables are imported into every spec module, even for modules that don't expect it. We need to figure out a way to prevent import of objects that are not explicitly imported into the module.
Tables are imported into every spec module, even for modules that don't expect it. We need to figure out a way to prevent importing of objects that are not explicitly imported into the module.
Currently cranelift_wasm::ModuleEnvironment does not provide `declare_table_import`, etc. so there is no meaningful way of fixing this yet.
Currently `cranelift_wasm::ModuleEnvironment` does not provide `declare_table_import`, etc. so there is no meaningful way of fixing this yet.
- `call_indirect.wast`
- `SKIP_SHARED_TABLE` [elem.wast]
Currently sharing tables between instances/modules does not work. Below are some of the reasons it is hard to achieve.
Currently sharing tables between instances/modules does not work. Below are some of the reasons it is hard to achieve:
- Rust naturally prevents such because of the possibility of race conditions
- ImportObject is just a wrapper, what we really care about is references to its content.
- Instance::new contains a mutation points, the part where after getting the object (memory or table) we push values to it
table[table_element_index] = func_addr
- Instance has its own created memories and tables and references to them must outlive Instance::new()
- Possible strategy
- `ImportObject` is just a wrapper, what we really care about is references to its content.
- `Instance::new` contains a mutation points, the part where after getting the object (memory or table) we push values to it
`table[table_element_index] = func_addr`
- Instance has its own created memories and tables and references to them must outlive `Instance::new()`
- Possible strategy:
```rust
// ImportObject should be passed by ref