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 @@ -

Wasmer logo

+

+ + Wasmer logo + +

- Build Status - License + + Build Status + + + License + - Join the Wasmer Community + Join the Wasmer Community

## 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