diff --git a/.appveyor.yml b/.appveyor.yml
index 16432ed6b..488e400fd 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -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:
diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md
index b2f30f753..e3d87738d 100644
--- a/ARCHITECTURE.md
+++ b/ARCHITECTURE.md
@@ -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.
diff --git a/ATTRIBUTIONS.md b/ATTRIBUTIONS.md
index eff1434ab..54e0eed02 100644
--- a/ATTRIBUTIONS.md
+++ b/ATTRIBUTIONS.md
@@ -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,10 +21,10 @@ We would love to hear from you if you think we can take inspiration from other p
### Nebulet
-```
+```text
MIT License
-Copyright (c) 2018
+Copyright (c) 2018
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -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.
@@ -557,7 +557,7 @@ the following conditions:
Neither the names of Mozilla,
nor the names of its contributors may be used to endorse
or promote products derived from this Software without specific prior
- written permission.
+ written permission.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
diff --git a/Cargo.lock b/Cargo.lock
index 2eaad2339..ebfe7ac0d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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"
diff --git a/Cargo.toml b/Cargo.toml
index 9d96e28cd..67fbda1b7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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"
diff --git a/LICENSE b/LICENSE
index 689596400..7e40523b0 100644
--- a/LICENSE
+++ b/LICENSE
@@ -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
diff --git a/Makefile b/Makefile
index c176dc7d1..2506f196c 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/README.md b/README.md
index df4ac0186..bdb921afd 100644
--- a/README.md
+++ b/README.md
@@ -1,16 +1,24 @@
-
+
+
+
+
+
-
-
+
+
+
+
+
+
-
+
## 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)).
-[Attributions](./ATTRIBUTIONS.md) .
+[ATTRIBUTIONS](./ATTRIBUTIONS.md)
diff --git a/integration_tests/lua/README.md b/integration_tests/lua/README.md
index ce2b03f6f..208e3438d 100644
--- a/integration_tests/lua/README.md
+++ b/integration_tests/lua/README.md
@@ -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
diff --git a/integration_tests/nginx/README.md b/integration_tests/nginx/README.md
index 9df8af773..3e9e46d8b 100644
--- a/integration_tests/nginx/README.md
+++ b/integration_tests/nginx/README.md
@@ -1,11 +1,11 @@
# `nginx` integration test
-This starts wasmer with the nginx wasm file and serves an html
-file with some simple text to assert on. The test script does
+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.
-Run test with:
+Run test with:
```
> ./integration_tests/nginx/test.sh
diff --git a/lib/README.md b/lib/README.md
index 44b316865..79ae446d3 100644
--- a/lib/README.md
+++ b/lib/README.md
@@ -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:
diff --git a/lib/emscripten/src/emscripten_target.rs b/lib/emscripten/src/emscripten_target.rs
new file mode 100644
index 000000000..4f831e909
--- /dev/null
+++ b/lib/emscripten/src/emscripten_target.rs
@@ -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
+}
diff --git a/lib/emscripten/src/lib.rs b/lib/emscripten/src/lib.rs
index 5c2a04e57..8911b823d 100644
--- a/lib/emscripten/src/lib.rs
+++ b/lib/emscripten/src/lib.rs
@@ -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>,
pub memset: Func<'a, (u32, u32, u32), u32>,
pub stack_alloc: Func<'a, u32, u32>,
-
pub jumps: Vec>,
+
+ pub dyn_call_i: Option>,
+ pub dyn_call_ii: Option>,
+ pub dyn_call_iii: Option>,
+ pub dyn_call_iiii: Option>,
+ pub dyn_call_v: Option>,
+ pub dyn_call_vi: Option>,
+ pub dyn_call_vii: Option>,
+ pub dyn_call_viii: Option>,
+ pub dyn_call_viiii: Option>,
}
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)),
diff --git a/lib/runtime-c-api/Cargo.toml b/lib/runtime-c-api/Cargo.toml
new file mode 100644
index 000000000..692c03cd7
--- /dev/null
+++ b/lib/runtime-c-api/Cargo.toml
@@ -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 "]
+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"]
+
+
diff --git a/lib/runtime-c-api/README.md b/lib/runtime-c-api/README.md
new file mode 100644
index 000000000..02ba9a705
--- /dev/null
+++ b/lib/runtime-c-api/README.md
@@ -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
diff --git a/lib/runtime-c-api/build.rs b/lib/runtime-c-api/build.rs
new file mode 100644
index 000000000..f78c5cf69
--- /dev/null
+++ b/lib/runtime-c-api/build.rs
@@ -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")
+}
diff --git a/lib/runtime-c-api/src/lib.rs b/lib/runtime-c-api/src/lib.rs
new file mode 100644
index 000000000..5b1dd1955
--- /dev/null
+++ b/lib/runtime-c-api/src/lib.rs
@@ -0,0 +1,1105 @@
+extern crate wasmer_runtime;
+extern crate wasmer_runtime_core;
+
+use libc::{c_char, c_int, int32_t, int64_t, uint32_t, uint8_t};
+use std::cell::RefCell;
+use std::collections::HashMap;
+use std::error::Error;
+use std::ffi::CStr;
+use std::fmt;
+use std::slice;
+use std::sync::Arc;
+use std::{ffi::c_void, ptr};
+use wasmer_runtime::{Ctx, Global, ImportObject, Instance, Memory, Module, Table, Value};
+use wasmer_runtime_core::export::{Context, Export, FuncPointer};
+use wasmer_runtime_core::import::Namespace;
+use wasmer_runtime_core::types::{ElementType, FuncSig, MemoryDescriptor, TableDescriptor, Type};
+use wasmer_runtime_core::units::{Bytes, Pages};
+
+#[allow(non_camel_case_types)]
+pub struct wasmer_import_object_t();
+
+#[allow(non_camel_case_types)]
+pub struct wasmer_module_t();
+
+#[allow(non_camel_case_types)]
+pub struct wasmer_instance_t();
+
+#[allow(non_camel_case_types)]
+pub struct wasmer_instance_context_t();
+
+#[allow(non_camel_case_types)]
+#[no_mangle]
+#[repr(C)]
+pub enum wasmer_result_t {
+ WASMER_OK = 1,
+ WASMER_ERROR = 2,
+}
+
+#[repr(u32)]
+#[derive(Clone)]
+pub enum wasmer_value_tag {
+ WASM_I32,
+ WASM_I64,
+ WASM_F32,
+ WASM_F64,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub union wasmer_value {
+ I32: int32_t,
+ I64: int64_t,
+ F32: f32,
+ F64: f64,
+}
+
+#[repr(C)]
+#[derive(Clone)]
+pub struct wasmer_value_t {
+ tag: wasmer_value_tag,
+ value: wasmer_value,
+}
+
+#[repr(C)]
+#[derive(Clone)]
+pub struct wasmer_global_descriptor_t {
+ mutable: bool,
+ kind: wasmer_value_tag,
+}
+
+#[repr(C)]
+#[derive(Clone)]
+pub struct wasmer_memory_t();
+
+#[repr(C)]
+#[derive(Clone)]
+pub struct wasmer_table_t();
+
+#[repr(C)]
+#[derive(Clone)]
+pub struct wasmer_func_t();
+
+#[repr(C)]
+#[derive(Clone)]
+pub struct wasmer_global_t();
+
+#[repr(C)]
+pub struct wasmer_limits_t {
+ pub min: uint32_t,
+ pub max: wasmer_limit_option_t,
+}
+
+#[repr(C)]
+pub struct wasmer_limit_option_t {
+ pub has_some: bool,
+ pub some: uint32_t,
+}
+
+#[repr(C)]
+pub struct wasmer_func_signature {
+ pub params: *const wasmer_value_tag,
+ pub params_len: c_int,
+ pub returns: *const wasmer_value_tag,
+ pub returns_len: c_int,
+}
+
+#[repr(C)]
+pub struct wasmer_import_t {
+ module_name: wasmer_byte_array,
+ import_name: wasmer_byte_array,
+ tag: wasmer_import_export_kind,
+ value: wasmer_import_export_value,
+}
+
+#[repr(C)]
+#[derive(Clone)]
+pub struct wasmer_export_t;
+
+#[repr(C)]
+#[derive(Clone)]
+pub struct wasmer_exports_t;
+
+#[repr(u32)]
+#[derive(Clone)]
+pub enum wasmer_import_export_kind {
+ WASM_FUNCTION,
+ WASM_GLOBAL,
+ WASM_MEMORY,
+ WASM_TABLE,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub union wasmer_import_export_value {
+ func: *const wasmer_func_t,
+ table: *const wasmer_table_t,
+ memory: *const wasmer_memory_t,
+ global: *const wasmer_global_t,
+}
+
+#[repr(C)]
+pub struct wasmer_byte_array {
+ bytes: *const uint8_t,
+ bytes_len: uint32_t,
+}
+
+/// Returns true for valid wasm bytes and false for invalid bytes
+#[allow(clippy::cast_ptr_alignment)]
+#[no_mangle]
+pub unsafe extern "C" fn wasmer_validate(
+ wasm_bytes: *mut uint8_t,
+ wasm_bytes_len: uint32_t,
+) -> bool {
+ if wasm_bytes.is_null() {
+ return false;
+ }
+ let bytes: &[u8] =
+ unsafe { ::std::slice::from_raw_parts_mut(wasm_bytes, wasm_bytes_len as usize) };
+ wasmer_runtime_core::validate(bytes)
+}
+
+/// 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.
+#[no_mangle]
+pub unsafe extern "C" fn wasmer_memory_new(
+ mut memory: *mut *mut wasmer_memory_t,
+ limits: wasmer_limits_t,
+) -> wasmer_result_t {
+ let max = if limits.max.has_some {
+ Some(Pages(limits.max.some))
+ } else {
+ None
+ };
+ let desc = MemoryDescriptor {
+ minimum: Pages(limits.min),
+ maximum: max,
+ shared: false,
+ };
+ let result = Memory::new(desc);
+ let new_memory = match result {
+ Ok(memory) => memory,
+ Err(error) => {
+ update_last_error(error);
+ return wasmer_result_t::WASMER_ERROR;
+ }
+ };
+ unsafe { *memory = Box::into_raw(Box::new(new_memory)) as *mut wasmer_memory_t };
+ wasmer_result_t::WASMER_OK
+}
+
+/// 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.
+#[allow(clippy::cast_ptr_alignment)]
+#[no_mangle]
+pub extern "C" fn wasmer_memory_grow(
+ memory: *mut wasmer_memory_t,
+ delta: uint32_t,
+) -> wasmer_result_t {
+ let memory = unsafe { &*(memory as *mut Memory) };
+ let maybe_delta = memory.grow(Pages(delta));
+ if let Some(_delta) = maybe_delta {
+ wasmer_result_t::WASMER_OK
+ } else {
+ update_last_error(CApiError {
+ msg: "unable to grow memory".to_string(),
+ });
+ wasmer_result_t::WASMER_ERROR
+ }
+}
+
+/// Returns the current length in pages of the given memory
+#[allow(clippy::cast_ptr_alignment)]
+#[no_mangle]
+pub extern "C" fn wasmer_memory_length(memory: *mut wasmer_memory_t) -> uint32_t {
+ let memory = unsafe { &*(memory as *mut Memory) };
+ let Pages(len) = memory.size();
+ len
+}
+
+/// 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.
+#[no_mangle]
+pub unsafe extern "C" fn wasmer_table_new(
+ mut table: *mut *mut wasmer_table_t,
+ limits: wasmer_limits_t,
+) -> wasmer_result_t {
+ let max = if limits.max.has_some {
+ Some(limits.max.some)
+ } else {
+ None
+ };
+ let desc = TableDescriptor {
+ element: ElementType::Anyfunc,
+ minimum: limits.min,
+ maximum: max,
+ };
+ let result = Table::new(desc);
+ let new_table = match result {
+ Ok(table) => table,
+ Err(error) => {
+ update_last_error(error);
+ return wasmer_result_t::WASMER_ERROR;
+ }
+ };
+ unsafe { *table = Box::into_raw(Box::new(new_table)) as *mut wasmer_table_t };
+ wasmer_result_t::WASMER_OK
+}
+
+/// 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.
+#[allow(clippy::cast_ptr_alignment)]
+#[no_mangle]
+pub extern "C" fn wasmer_table_grow(
+ table: *mut wasmer_table_t,
+ delta: uint32_t,
+) -> wasmer_result_t {
+ let table = unsafe { &*(table as *mut Table) };
+ let maybe_delta = table.grow(delta);
+ if let Some(_delta) = maybe_delta {
+ wasmer_result_t::WASMER_OK
+ } else {
+ update_last_error(CApiError {
+ msg: "unable to grow table".to_string(),
+ });
+ wasmer_result_t::WASMER_ERROR
+ }
+}
+
+/// Returns the current length of the given Table
+#[allow(clippy::cast_ptr_alignment)]
+#[no_mangle]
+pub extern "C" fn wasmer_table_length(table: *mut wasmer_table_t) -> uint32_t {
+ let table = unsafe { &*(table as *mut Table) };
+ let len = table.size();
+ len
+}
+
+/// Frees memory for the given Table
+#[allow(clippy::cast_ptr_alignment)]
+#[no_mangle]
+pub extern "C" fn wasmer_table_destroy(table: *mut wasmer_table_t) {
+ if !table.is_null() {
+ drop(unsafe { Box::from_raw(table as *mut Table) });
+ }
+}
+
+/// Creates a new Global and returns a pointer to it.
+/// The caller owns the object and should call `wasmer_global_destroy` to free it.
+#[no_mangle]
+pub unsafe extern "C" fn wasmer_global_new(
+ value: wasmer_value_t,
+ mutable: bool,
+) -> *mut wasmer_global_t {
+ let global = if mutable {
+ Global::new_mutable(value.into())
+ } else {
+ Global::new(value.into())
+ };
+ unsafe { Box::into_raw(Box::new(global)) as *mut wasmer_global_t }
+}
+
+/// Gets the value stored by the given Global
+#[allow(clippy::cast_ptr_alignment)]
+#[no_mangle]
+pub extern "C" fn wasmer_global_get(global: *mut wasmer_global_t) -> wasmer_value_t {
+ let global = unsafe { &*(global as *mut Global) };
+ let value: wasmer_value_t = global.get().into();
+ value
+}
+
+/// Sets the value stored by the given Global
+#[allow(clippy::cast_ptr_alignment)]
+#[no_mangle]
+pub extern "C" fn wasmer_global_set(global: *mut wasmer_global_t, value: wasmer_value_t) {
+ let global = unsafe { &*(global as *mut Global) };
+ global.set(value.into());
+}
+
+/// Returns a descriptor (type, mutability) of the given Global
+#[allow(clippy::cast_ptr_alignment)]
+#[no_mangle]
+pub extern "C" fn wasmer_global_get_descriptor(
+ global: *mut wasmer_global_t,
+) -> wasmer_global_descriptor_t {
+ let global = unsafe { &*(global as *mut Global) };
+ let descriptor = global.descriptor();
+ let desc = wasmer_global_descriptor_t {
+ mutable: descriptor.mutable,
+ kind: descriptor.ty.into(),
+ };
+ desc
+}
+
+/// Frees memory for the given Global
+#[allow(clippy::cast_ptr_alignment)]
+#[no_mangle]
+pub extern "C" fn wasmer_global_destroy(global: *mut wasmer_global_t) {
+ if !global.is_null() {
+ drop(unsafe { Box::from_raw(global as *mut Global) });
+ }
+}
+
+/// Frees memory for the given Memory
+#[allow(clippy::cast_ptr_alignment)]
+#[no_mangle]
+pub extern "C" fn wasmer_memory_destroy(memory: *mut wasmer_memory_t) {
+ if !memory.is_null() {
+ drop(unsafe { Box::from_raw(memory as *mut Memory) });
+ }
+}
+
+/// 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.
+#[allow(clippy::cast_ptr_alignment)]
+#[no_mangle]
+pub unsafe extern "C" fn wasmer_compile(
+ mut module: *mut *mut wasmer_module_t,
+ wasm_bytes: *mut uint8_t,
+ wasm_bytes_len: uint32_t,
+) -> wasmer_result_t {
+ let bytes: &[u8] =
+ unsafe { ::std::slice::from_raw_parts_mut(wasm_bytes, wasm_bytes_len as usize) };
+ let result = wasmer_runtime::compile(bytes);
+ let new_module = match result {
+ Ok(instance) => instance,
+ Err(error) => {
+ update_last_error(error);
+ return wasmer_result_t::WASMER_ERROR;
+ }
+ };
+ unsafe { *module = Box::into_raw(Box::new(new_module)) as *mut wasmer_module_t };
+ wasmer_result_t::WASMER_OK
+}
+
+/// Frees memory for the given Module
+#[allow(clippy::cast_ptr_alignment)]
+#[no_mangle]
+pub extern "C" fn wasmer_module_destroy(module: *mut wasmer_module_t) {
+ if !module.is_null() {
+ drop(unsafe { Box::from_raw(module as *mut Module) });
+ }
+}
+
+/// 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.
+#[allow(clippy::cast_ptr_alignment)]
+#[no_mangle]
+pub unsafe extern "C" fn wasmer_instantiate(
+ mut instance: *mut *mut wasmer_instance_t,
+ wasm_bytes: *mut uint8_t,
+ wasm_bytes_len: uint32_t,
+ imports: *mut wasmer_import_t,
+ imports_len: c_int,
+) -> wasmer_result_t {
+ if wasm_bytes.is_null() {
+ update_last_error(CApiError {
+ msg: "wasm bytes ptr is null".to_string(),
+ });
+ return wasmer_result_t::WASMER_ERROR;
+ }
+ let imports: &[wasmer_import_t] = slice::from_raw_parts(imports, imports_len as usize);
+ let mut import_object = ImportObject::new();
+ let mut namespaces = HashMap::new();
+ for import in imports {
+ let module_name = slice::from_raw_parts(
+ import.module_name.bytes,
+ import.module_name.bytes_len as usize,
+ );
+ let module_name = if let Ok(s) = std::str::from_utf8(module_name) {
+ s
+ } else {
+ update_last_error(CApiError {
+ msg: "error converting module name to string".to_string(),
+ });
+ return wasmer_result_t::WASMER_ERROR;
+ };
+ let import_name = slice::from_raw_parts(
+ import.import_name.bytes,
+ import.import_name.bytes_len as usize,
+ );
+ let import_name = if let Ok(s) = std::str::from_utf8(import_name) {
+ s
+ } else {
+ update_last_error(CApiError {
+ msg: "error converting import_name to string".to_string(),
+ });
+ return wasmer_result_t::WASMER_ERROR;
+ };
+
+ let namespace = namespaces
+ .entry(module_name)
+ .or_insert_with(|| Namespace::new());
+
+ let export = match import.tag {
+ wasmer_import_export_kind::WASM_MEMORY => import.value.memory as *mut Export,
+ wasmer_import_export_kind::WASM_FUNCTION => import.value.func as *mut Export,
+ wasmer_import_export_kind::WASM_GLOBAL => import.value.global as *mut Export,
+ wasmer_import_export_kind::WASM_TABLE => import.value.table as *mut Export,
+ };
+ namespace.insert(import_name, unsafe { (&*export).clone() });
+ }
+ for (module_name, namespace) in namespaces.into_iter() {
+ import_object.register(module_name, namespace);
+ }
+
+ let bytes: &[u8] =
+ unsafe { ::std::slice::from_raw_parts_mut(wasm_bytes, wasm_bytes_len as usize) };
+ let result = wasmer_runtime::instantiate(bytes, &import_object);
+ let new_instance = match result {
+ Ok(instance) => instance,
+ Err(error) => {
+ // TODO the trait bound `wasmer_runtime::error::Error: std::error::Error` is not satisfied
+ //update_last_error(error);
+ update_last_error(CApiError {
+ msg: "error instantiating".to_string(),
+ });
+ return wasmer_result_t::WASMER_ERROR;
+ }
+ };
+ unsafe { *instance = Box::into_raw(Box::new(new_instance)) as *mut wasmer_instance_t };
+ wasmer_result_t::WASMER_OK
+}
+
+/// 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.
+#[allow(clippy::cast_ptr_alignment)]
+#[no_mangle]
+pub unsafe extern "C" fn wasmer_instance_call(
+ instance: *mut wasmer_instance_t,
+ name: *const c_char,
+ params: *const wasmer_value_t,
+ params_len: c_int,
+ results: *mut wasmer_value_t,
+ results_len: c_int,
+) -> wasmer_result_t {
+ if instance.is_null() {
+ update_last_error(CApiError {
+ msg: "instance ptr is null".to_string(),
+ });
+ return wasmer_result_t::WASMER_ERROR;
+ }
+ if name.is_null() {
+ update_last_error(CApiError {
+ msg: "name ptr is null".to_string(),
+ });
+ return wasmer_result_t::WASMER_ERROR;
+ }
+ if params.is_null() {
+ update_last_error(CApiError {
+ msg: "params ptr is null".to_string(),
+ });
+ return wasmer_result_t::WASMER_ERROR;
+ }
+
+ let params: &[wasmer_value_t] = slice::from_raw_parts(params, params_len as usize);
+ let params: Vec = params.iter().cloned().map(|x| x.into()).collect();
+
+ let func_name_c = unsafe { CStr::from_ptr(name) };
+ let func_name_r = func_name_c.to_str().unwrap();
+
+ let results: &mut [wasmer_value_t] = slice::from_raw_parts_mut(results, results_len as usize);
+ let result = (&*(instance as *mut Instance)).call(func_name_r, ¶ms[..]);
+
+ match result {
+ Ok(results_vec) => {
+ if results_vec.len() > 0 {
+ let ret = match results_vec[0] {
+ Value::I32(x) => wasmer_value_t {
+ tag: wasmer_value_tag::WASM_I32,
+ value: wasmer_value { I32: x },
+ },
+ Value::I64(x) => wasmer_value_t {
+ tag: wasmer_value_tag::WASM_I64,
+ value: wasmer_value { I64: x },
+ },
+ Value::F32(x) => wasmer_value_t {
+ tag: wasmer_value_tag::WASM_F32,
+ value: wasmer_value { F32: x },
+ },
+ Value::F64(x) => wasmer_value_t {
+ tag: wasmer_value_tag::WASM_F64,
+ value: wasmer_value { F64: x },
+ },
+ };
+ results[0] = ret;
+ }
+ wasmer_result_t::WASMER_OK
+ }
+ Err(err) => {
+ update_last_error(err);
+ wasmer_result_t::WASMER_ERROR
+ }
+ }
+}
+
+/// Gets Exports for the given instance
+///
+/// The caller owns the object and should call `wasmer_exports_destroy` to free it.
+#[allow(clippy::cast_ptr_alignment)]
+#[no_mangle]
+pub unsafe extern "C" fn wasmer_instance_exports(
+ instance: *mut wasmer_instance_t,
+ exports: *mut *mut wasmer_exports_t,
+) {
+ let mut instance = unsafe { &mut *(instance as *mut Instance) };
+ let named_exports: Box =
+ Box::new(NamedExports(instance.exports().map(|e| e.into()).collect()));
+ unsafe { *exports = Box::into_raw(named_exports) as *mut wasmer_exports_t };
+}
+
+pub struct NamedExports(Vec);
+
+/// Frees the memory for the given exports
+#[allow(clippy::cast_ptr_alignment)]
+#[no_mangle]
+pub unsafe extern "C" fn wasmer_exports_destroy(exports: *mut wasmer_exports_t) {
+ if !exports.is_null() {
+ drop(unsafe { Box::from_raw(exports as *mut NamedExports) });
+ }
+}
+
+/// Gets the length of the exports
+#[allow(clippy::cast_ptr_alignment)]
+#[no_mangle]
+pub unsafe extern "C" fn wasmer_exports_len(exports: *mut wasmer_exports_t) -> c_int {
+ if exports.is_null() {
+ return 0;
+ }
+ (*(exports as *mut NamedExports)).0.len() as c_int
+}
+
+/// Gets wasmer_export by index
+#[allow(clippy::cast_ptr_alignment)]
+#[no_mangle]
+pub unsafe extern "C" fn wasmer_exports_get(
+ exports: *mut wasmer_exports_t,
+ idx: c_int,
+) -> *mut wasmer_export_t {
+ if exports.is_null() {
+ return ptr::null_mut();
+ }
+ let mut named_exports = unsafe { &mut *(exports as *mut NamedExports) };
+ let ptr = &mut (*named_exports).0[idx as usize] as *mut NamedExport as *mut wasmer_export_t;
+ ptr
+}
+
+/// Gets wasmer_export kind
+#[no_mangle]
+#[allow(clippy::cast_ptr_alignment)]
+pub unsafe extern "C" fn wasmer_export_kind(
+ export: *mut wasmer_export_t,
+) -> wasmer_import_export_kind {
+ let named_export = &*(export as *mut NamedExport);
+ match named_export.export {
+ Export::Table(_) => wasmer_import_export_kind::WASM_TABLE,
+ Export::Function { .. } => wasmer_import_export_kind::WASM_FUNCTION,
+ Export::Global(_) => wasmer_import_export_kind::WASM_GLOBAL,
+ Export::Memory(_) => wasmer_import_export_kind::WASM_MEMORY,
+ }
+}
+
+/// Creates new func
+///
+/// The caller owns the object and should call `wasmer_func_destroy` to free it.
+#[no_mangle]
+#[allow(clippy::cast_ptr_alignment)]
+pub unsafe extern "C" fn wasmer_func_new(
+ func: extern "C" fn(data: *mut c_void),
+ params: *const wasmer_value_tag,
+ params_len: c_int,
+ returns: *const wasmer_value_tag,
+ returns_len: c_int,
+) -> *const wasmer_func_t {
+ let params: &[wasmer_value_tag] = slice::from_raw_parts(params, params_len as usize);
+ let params: Vec = params.iter().cloned().map(|x| x.into()).collect();
+ let returns: &[wasmer_value_tag] = slice::from_raw_parts(returns, returns_len as usize);
+ let returns: Vec = returns.iter().cloned().map(|x| x.into()).collect();
+
+ let export = Box::new(Export::Function {
+ func: unsafe { FuncPointer::new(func as _) },
+ ctx: Context::Internal,
+ signature: Arc::new(FuncSig::new(params, returns)),
+ });
+ Box::into_raw(export) as *mut wasmer_func_t
+}
+
+/// 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.
+#[no_mangle]
+#[allow(clippy::cast_ptr_alignment)]
+pub unsafe extern "C" fn wasmer_func_params_arity(
+ func: *mut wasmer_func_t,
+ result: *mut uint32_t,
+) -> wasmer_result_t {
+ let mut export = unsafe { &mut *(func as *mut Export) };
+ let result = if let Export::Function { ref signature, .. } = *export {
+ unsafe { *result = signature.params().len() as uint32_t };
+ wasmer_result_t::WASMER_OK
+ } else {
+ update_last_error(CApiError {
+ msg: "func ptr error in wasmer_func_params_arity".to_string(),
+ });
+ wasmer_result_t::WASMER_ERROR
+ };
+ result
+}
+
+/// 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.
+#[no_mangle]
+#[allow(clippy::cast_ptr_alignment)]
+pub unsafe extern "C" fn wasmer_func_params(
+ func: *mut wasmer_func_t,
+ params: *mut wasmer_value_tag,
+ params_len: c_int,
+) -> wasmer_result_t {
+ let mut export = unsafe { &mut *(func as *mut Export) };
+ let result = if let Export::Function { ref signature, .. } = *export {
+ let params: &mut [wasmer_value_tag] =
+ slice::from_raw_parts_mut(params, params_len as usize);
+ for (i, item) in signature.params().iter().enumerate() {
+ params[i] = item.into();
+ }
+ wasmer_result_t::WASMER_OK
+ } else {
+ update_last_error(CApiError {
+ msg: "func ptr error in wasmer_func_params".to_string(),
+ });
+ wasmer_result_t::WASMER_ERROR
+ };
+ 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.
+#[no_mangle]
+#[allow(clippy::cast_ptr_alignment)]
+pub unsafe extern "C" fn wasmer_func_returns(
+ func: *mut wasmer_func_t,
+ returns: *mut wasmer_value_tag,
+ returns_len: c_int,
+) -> wasmer_result_t {
+ let mut export = unsafe { &mut *(func as *mut Export) };
+ let result = if let Export::Function { ref signature, .. } = *export {
+ let returns: &mut [wasmer_value_tag] =
+ slice::from_raw_parts_mut(returns, returns_len as usize);
+ for (i, item) in signature.returns().iter().enumerate() {
+ returns[i] = item.into();
+ }
+ wasmer_result_t::WASMER_OK
+ } else {
+ update_last_error(CApiError {
+ msg: "func ptr error in wasmer_func_returns".to_string(),
+ });
+ wasmer_result_t::WASMER_ERROR
+ };
+ result
+}
+
+/// 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.
+#[no_mangle]
+#[allow(clippy::cast_ptr_alignment)]
+pub unsafe extern "C" fn wasmer_func_returns_arity(
+ func: *mut wasmer_func_t,
+ result: *mut uint32_t,
+) -> wasmer_result_t {
+ let mut export = unsafe { &*(func as *mut Export) };
+ let result = if let Export::Function { ref signature, .. } = *export {
+ unsafe { *result = signature.returns().len() as uint32_t };
+ wasmer_result_t::WASMER_OK
+ } else {
+ update_last_error(CApiError {
+ msg: "func ptr error in wasmer_func_results_arity".to_string(),
+ });
+ wasmer_result_t::WASMER_ERROR
+ };
+ result
+}
+
+/// Frees memory for the given Func
+#[allow(clippy::cast_ptr_alignment)]
+#[no_mangle]
+pub extern "C" fn wasmer_func_destroy(func: *mut wasmer_func_t) {
+ if !func.is_null() {
+ drop(unsafe { Box::from_raw(func as *mut Export) });
+ }
+}
+
+/// Gets func from wasm_export
+#[no_mangle]
+#[allow(clippy::cast_ptr_alignment)]
+pub unsafe extern "C" fn wasmer_export_to_func(
+ export: *mut wasmer_export_t,
+) -> *const wasmer_func_t {
+ let named_export = &*(export as *mut NamedExport);
+ &named_export.export as *const Export as *const wasmer_func_t
+}
+
+/// Gets name from wasmer_export
+#[no_mangle]
+#[allow(clippy::cast_ptr_alignment)]
+pub unsafe extern "C" fn wasmer_export_name(export: *mut wasmer_export_t) -> wasmer_byte_array {
+ let named_export = &*(export as *mut NamedExport);
+ wasmer_byte_array {
+ bytes: named_export.name.as_ptr(),
+ bytes_len: named_export.name.len() as u32,
+ }
+}
+
+/// 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.
+#[allow(clippy::cast_ptr_alignment)]
+#[no_mangle]
+pub unsafe extern "C" fn wasmer_func_call(
+ func: *mut wasmer_func_t,
+ params: *const wasmer_value_t,
+ params_len: c_int,
+ results: *mut wasmer_value_t,
+ results_len: c_int,
+) -> wasmer_result_t {
+ if func.is_null() {
+ update_last_error(CApiError {
+ msg: "func ptr is null".to_string(),
+ });
+ return wasmer_result_t::WASMER_ERROR;
+ }
+ if params.is_null() {
+ update_last_error(CApiError {
+ msg: "params ptr is null".to_string(),
+ });
+ return wasmer_result_t::WASMER_ERROR;
+ }
+
+ let params: &[wasmer_value_t] = slice::from_raw_parts(params, params_len as usize);
+ let params: Vec = params.iter().cloned().map(|x| x.into()).collect();
+
+ let export_func = unsafe { &*(func as *mut Export) };
+
+ let results: &mut [wasmer_value_t] = slice::from_raw_parts_mut(results, results_len as usize);
+ // TODO implement func.call
+ update_last_error(CApiError {
+ msg: "wasmer_func_call not yet implemented".to_string(),
+ });
+ wasmer_result_t::WASMER_ERROR
+ // let result = instance.call(func_name_r, ¶ms[..]);
+ // Box::into_raw(export_func);
+ // match result {
+ // Ok(results_vec) => {
+ // if results_vec.len() > 0 {
+ // let ret = match results_vec[0] {
+ // Value::I32(x) => wasmer_value_t {
+ // tag: wasmer_value_tag::WASM_I32,
+ // value: wasmer_value { I32: x },
+ // },
+ // Value::I64(x) => wasmer_value_t {
+ // tag: wasmer_value_tag::WASM_I64,
+ // value: wasmer_value { I64: x },
+ // },
+ // Value::F32(x) => wasmer_value_t {
+ // tag: wasmer_value_tag::WASM_F32,
+ // value: wasmer_value { F32: x },
+ // },
+ // Value::F64(x) => wasmer_value_t {
+ // tag: wasmer_value_tag::WASM_F64,
+ // value: wasmer_value { F64: x },
+ // },
+ // };
+ // results[0] = ret;
+ // }
+ // wasmer_result_t::WASMER_OK
+ // }
+ // Err(err) => {
+ // update_last_error(err);
+ // wasmer_result_t::WASMER_ERROR
+ // }
+ // }
+}
+
+/// Gets the memory within the context at the index `memory_idx`.
+/// The index is always 0 until multiple memories are supported.
+#[allow(clippy::cast_ptr_alignment)]
+#[no_mangle]
+pub extern "C" fn wasmer_instance_context_memory(
+ ctx: *mut wasmer_instance_context_t,
+ memory_idx: uint32_t,
+) -> *const wasmer_memory_t {
+ let mut ctx = unsafe { &mut *(ctx as *mut Ctx) };
+ let memory = ctx.memory(0);
+ memory as *const Memory as *const wasmer_memory_t
+}
+
+/// Gets the start pointer to the bytes within a Memory
+#[allow(clippy::cast_ptr_alignment)]
+#[no_mangle]
+pub extern "C" fn wasmer_memory_data(mem: *mut wasmer_memory_t) -> *mut uint8_t {
+ let memory = mem as *mut Memory;
+ use std::cell::Cell;
+ unsafe { ((*memory).view::()[..]).as_ptr() as *mut Cell as *mut u8 }
+}
+
+/// Gets the size in bytes of a Memory
+#[allow(clippy::cast_ptr_alignment)]
+#[no_mangle]
+pub extern "C" fn wasmer_memory_data_length(mem: *mut wasmer_memory_t) -> uint32_t {
+ let memory = mem as *mut Memory;
+ let Bytes(len) = unsafe { (*memory).size().bytes() };
+ len as uint32_t
+}
+
+/// Frees memory for the given Instance
+#[allow(clippy::cast_ptr_alignment)]
+#[no_mangle]
+pub extern "C" fn wasmer_instance_destroy(instance: *mut wasmer_instance_t) {
+ if !instance.is_null() {
+ drop(unsafe { Box::from_raw(instance as *mut Instance) });
+ }
+}
+
+impl From for Value {
+ fn from(v: wasmer_value_t) -> Self {
+ unsafe {
+ match v {
+ wasmer_value_t {
+ tag: WASM_I32,
+ value: wasmer_value { I32 },
+ } => Value::I32(I32),
+ wasmer_value_t {
+ tag: WASM_I64,
+ value: wasmer_value { I64 },
+ } => Value::I64(I64),
+ wasmer_value_t {
+ tag: WASM_F32,
+ value: wasmer_value { F32 },
+ } => Value::F32(F32),
+ wasmer_value_t {
+ tag: WASM_F64,
+ value: wasmer_value { F64 },
+ } => Value::F64(F64),
+ _ => panic!("not implemented"),
+ }
+ }
+ }
+}
+
+impl From for wasmer_value_t {
+ fn from(val: Value) -> Self {
+ match val {
+ Value::I32(x) => wasmer_value_t {
+ tag: wasmer_value_tag::WASM_I32,
+ value: wasmer_value { I32: x },
+ },
+ Value::I64(x) => wasmer_value_t {
+ tag: wasmer_value_tag::WASM_I64,
+ value: wasmer_value { I64: x },
+ },
+ Value::F32(x) => wasmer_value_t {
+ tag: wasmer_value_tag::WASM_F32,
+ value: wasmer_value { F32: x },
+ },
+ Value::F64(x) => wasmer_value_t {
+ tag: wasmer_value_tag::WASM_F64,
+ value: wasmer_value { F64: x },
+ },
+ }
+ }
+}
+
+impl From for wasmer_value_tag {
+ fn from(ty: Type) -> Self {
+ match ty {
+ Type::I32 => wasmer_value_tag::WASM_I32,
+ Type::I64 => wasmer_value_tag::WASM_I64,
+ Type::F32 => wasmer_value_tag::WASM_F32,
+ Type::F64 => wasmer_value_tag::WASM_F64,
+ _ => panic!("not implemented"),
+ }
+ }
+}
+
+impl From for Type {
+ fn from(v: wasmer_value_tag) -> Self {
+ unsafe {
+ match v {
+ wasmer_value_tag::WASM_I32 => Type::I32,
+ wasmer_value_tag::WASM_I64 => Type::I64,
+ wasmer_value_tag::WASM_F32 => Type::F32,
+ wasmer_value_tag::WASM_F64 => Type::F64,
+ _ => panic!("not implemented"),
+ }
+ }
+ }
+}
+
+impl From<(std::string::String, wasmer_runtime_core::export::Export)> for NamedExport {
+ fn from((name, export): (String, Export)) -> Self {
+ NamedExport { name, export }
+ }
+}
+
+impl From<&wasmer_runtime::wasm::Type> for wasmer_value_tag {
+ fn from(ty: &Type) -> Self {
+ match *ty {
+ Type::I32 => wasmer_value_tag::WASM_I32,
+ Type::I64 => wasmer_value_tag::WASM_I64,
+ Type::F32 => wasmer_value_tag::WASM_F32,
+ Type::F64 => wasmer_value_tag::WASM_F64,
+ }
+ }
+}
+
+// Error reporting
+
+thread_local! {
+ static LAST_ERROR: RefCell>> = RefCell::new(None);
+}
+
+fn update_last_error(err: E) {
+ LAST_ERROR.with(|prev| {
+ *prev.borrow_mut() = Some(Box::new(err));
+ });
+}
+
+/// Retrieve the most recent error, clearing it in the process.
+fn take_last_error() -> Option> {
+ LAST_ERROR.with(|prev| prev.borrow_mut().take())
+}
+
+/// 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);
+/// ```
+#[no_mangle]
+pub extern "C" fn wasmer_last_error_length() -> c_int {
+ LAST_ERROR.with(|prev| match *prev.borrow() {
+ Some(ref err) => err.to_string().len() as c_int + 1,
+ None => 0,
+ })
+}
+
+/// 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);
+/// ```
+#[no_mangle]
+pub unsafe extern "C" fn wasmer_last_error_message(buffer: *mut c_char, length: c_int) -> c_int {
+ if buffer.is_null() {
+ // buffer pointer is null
+ return -1;
+ }
+
+ let last_error = match take_last_error() {
+ Some(err) => err,
+ None => return 0,
+ };
+
+ let error_message = last_error.to_string();
+
+ let buffer = slice::from_raw_parts_mut(buffer as *mut u8, length as usize);
+
+ if error_message.len() >= buffer.len() {
+ // buffer to small for err message
+ return -1;
+ }
+
+ ptr::copy_nonoverlapping(
+ error_message.as_ptr(),
+ buffer.as_mut_ptr(),
+ error_message.len(),
+ );
+
+ // Add a trailing null so people using the string as a `char *` don't
+ // accidentally read into garbage.
+ buffer[error_message.len()] = 0;
+
+ error_message.len() as c_int
+}
+
+#[derive(Debug)]
+struct CApiError {
+ msg: String,
+}
+
+impl fmt::Display for CApiError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}", &self.msg)
+ }
+}
+
+impl Error for CApiError {}
+
+struct NamedExport {
+ name: String,
+ export: Export,
+}
diff --git a/lib/runtime-c-api/tests/.gitignore b/lib/runtime-c-api/tests/.gitignore
new file mode 100644
index 000000000..6ea57c28d
--- /dev/null
+++ b/lib/runtime-c-api/tests/.gitignore
@@ -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
\ No newline at end of file
diff --git a/lib/runtime-c-api/tests/CMakeLists.txt b/lib/runtime-c-api/tests/CMakeLists.txt
new file mode 100644
index 000000000..788ee1c6a
--- /dev/null
+++ b/lib/runtime-c-api/tests/CMakeLists.txt
@@ -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)
+
diff --git a/lib/runtime-c-api/tests/runtime_c_api_tests.rs b/lib/runtime-c-api/tests/runtime_c_api_tests.rs
new file mode 100644
index 000000000..b605da49e
--- /dev/null
+++ b/lib/runtime-c-api/tests/runtime_c_api_tests.rs
@@ -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),
+ }
+}
diff --git a/lib/runtime-c-api/tests/sum.wasm b/lib/runtime-c-api/tests/sum.wasm
new file mode 100644
index 000000000..135da2604
Binary files /dev/null and b/lib/runtime-c-api/tests/sum.wasm differ
diff --git a/lib/runtime-c-api/tests/test-exports.c b/lib/runtime-c-api/tests/test-exports.c
new file mode 100644
index 000000000..3f458ebab
--- /dev/null
+++ b/lib/runtime-c-api/tests/test-exports.c
@@ -0,0 +1,86 @@
+#include
+#include "../wasmer.h"
+#include
+#include
+
+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, ¶ms_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;
+}
\ No newline at end of file
diff --git a/lib/runtime-c-api/tests/test-globals.c b/lib/runtime-c-api/tests/test-globals.c
new file mode 100644
index 000000000..a694d2e75
--- /dev/null
+++ b/lib/runtime-c-api/tests/test-globals.c
@@ -0,0 +1,30 @@
+#include
+#include "../wasmer.h"
+#include
+#include
+
+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;
+}
diff --git a/lib/runtime-c-api/tests/test-import-function.c b/lib/runtime-c-api/tests/test-import-function.c
new file mode 100644
index 000000000..e7caed874
--- /dev/null
+++ b/lib/runtime-c-api/tests/test-import-function.c
@@ -0,0 +1,91 @@
+#include
+#include "../wasmer.h"
+#include
+#include
+#include
+
+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;
+}
\ No newline at end of file
diff --git a/lib/runtime-c-api/tests/test-instantiate.c b/lib/runtime-c-api/tests/test-instantiate.c
new file mode 100644
index 000000000..2a675de12
--- /dev/null
+++ b/lib/runtime-c-api/tests/test-instantiate.c
@@ -0,0 +1,56 @@
+#include
+#include "../wasmer.h"
+#include
+#include
+
+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;
+}
\ No newline at end of file
diff --git a/lib/runtime-c-api/tests/test-memory.c b/lib/runtime-c-api/tests/test-memory.c
new file mode 100644
index 000000000..63913c73b
--- /dev/null
+++ b/lib/runtime-c-api/tests/test-memory.c
@@ -0,0 +1,62 @@
+#include
+#include "../wasmer.h"
+#include
+#include
+
+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;
+}
diff --git a/lib/runtime-c-api/tests/test-module.c b/lib/runtime-c-api/tests/test-module.c
new file mode 100644
index 000000000..bacd37c3a
--- /dev/null
+++ b/lib/runtime-c-api/tests/test-module.c
@@ -0,0 +1,25 @@
+#include
+#include "../wasmer.h"
+#include
+#include
+
+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;
+}
\ No newline at end of file
diff --git a/lib/runtime-c-api/tests/test-tables.c b/lib/runtime-c-api/tests/test-tables.c
new file mode 100644
index 000000000..66ebf832d
--- /dev/null
+++ b/lib/runtime-c-api/tests/test-tables.c
@@ -0,0 +1,48 @@
+#include
+#include "../wasmer.h"
+#include
+#include
+
+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;
+}
diff --git a/lib/runtime-c-api/tests/test-validate.c b/lib/runtime-c-api/tests/test-validate.c
new file mode 100644
index 000000000..689cf50f2
--- /dev/null
+++ b/lib/runtime-c-api/tests/test-validate.c
@@ -0,0 +1,22 @@
+#include
+#include "../wasmer.h"
+#include
+#include
+
+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;
+}
diff --git a/lib/runtime-c-api/tests/wasm_sample_app.wasm b/lib/runtime-c-api/tests/wasm_sample_app.wasm
new file mode 100755
index 000000000..7c8c4b72a
Binary files /dev/null and b/lib/runtime-c-api/tests/wasm_sample_app.wasm differ
diff --git a/lib/runtime-c-api/wasmer.h b/lib/runtime-c-api/wasmer.h
new file mode 100644
index 000000000..8d328836f
--- /dev/null
+++ b/lib/runtime-c-api/wasmer.h
@@ -0,0 +1,381 @@
+#ifndef WASMER_H
+#define WASMER_H
+
+#include
+#include
+#include
+#include
+
+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 */
diff --git a/lib/runtime-c-api/wasmer.hh b/lib/runtime-c-api/wasmer.hh
new file mode 100644
index 000000000..ab2808bf3
--- /dev/null
+++ b/lib/runtime-c-api/wasmer.hh
@@ -0,0 +1,306 @@
+#ifndef WASMER_H
+#define WASMER_H
+
+#include
+#include
+#include
+
+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
diff --git a/lib/runtime-core/src/error.rs b/lib/runtime-core/src/error.rs
index e88a5692c..33c9fd15f 100644
--- a/lib/runtime-core/src/error.rs
+++ b/lib/runtime-core/src/error.rs
@@ -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)]
diff --git a/lib/spectests/spectests/README.md b/lib/spectests/spectests/README.md
index b9c26d864..669246528 100644
--- a/lib/spectests/spectests/README.md
+++ b/lib/spectests/spectests/README.md
@@ -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
diff --git a/installer/wasmer.iss b/src/installer/wasmer.iss
similarity index 100%
rename from installer/wasmer.iss
rename to src/installer/wasmer.iss