diff --git a/.appveyor.yml b/.appveyor.yml index 8bcc3b4c7..56df0ef21 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,11 +1,12 @@ -branches: - except: - - master - version: "{build} ~ {branch}" os: Visual Studio 2017 +branches: + only: + - staging + - trying + environment: matrix: - CHANNEL: stable @@ -13,20 +14,60 @@ environment: ABI: msvc TARGET: x86_64-pc-windows-msvc +cache: + - 'C:\Users\appveyor\.cargo' + - target + install: - - appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe - - rustup-init.exe -yv --default-host %target% - - set PATH=%PATH%;%USERPROFILE%\.cargo\bin + # # Install LLVM + # - mkdir C:\projects\deps + # - cd C:\projects\deps + # - appveyor DownloadFile http://prereleases.llvm.org/win-snapshots/LLVM-7.0.0-r336178-win64.exe -FileName llvm.exe + # - 7z x llvm.exe -oC:\projects\deps\llvm + # # - set "PATH=%PATH%;C:\projects\deps\llvm\bin" + # - set "LLD_LINK=C:\projects\deps\llvm\bin\lld-link.exe" + # - set "LLVM_SYS_70_PREFIX=C:\projects\deps\llvm" + # - cd "%APPVEYOR_BUILD_FOLDER%" + + # Install Rust + # uncomment these lines if the cache is cleared, or if we must re-install rust for some reason + # - appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe + # - rustup-init.exe -yv --default-host %target% + - set PATH=%PATH%;C:\\Libraries\\llvm-5.0.0\\bin;%USERPROFILE%\.cargo\bin + - rustup default stable-%target% + - rustup update - rustc -vV - cargo -vV -artifacts: - - path: target\debug\wasmer.exe - name: wasmer.exe + # Install InnoSetup + - appveyor-retry appveyor DownloadFile https://s3-us-west-1.amazonaws.com/rust-lang-ci2/rust-ci-mirror/2017-08-22-is.exe + - 2017-08-22-is.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /SP- + - set PATH="C:\Program Files (x86)\Inno Setup 5";%PATH% +# uncomment to RDP to appveyor +# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) build_script: - - cargo build --verbose + - cargo build --release --verbose test_script: - - set RUST_BACKTRACE=1 - - cargo test --verbose + - cargo test --manifest-path lib/spectests/Cargo.toml --features clif + +before_deploy: + - cd ./src/installer + - iscc wasmer.iss + - copy /y .\WasmerInstaller.exe ..\..\WasmerInstaller-%APPVEYOR_REPO_TAG_NAME%.exe + - appveyor PushArtifact ..\..\WasmerInstaller-%APPVEYOR_REPO_TAG_NAME%.exe + - cd ..\..\ + +matrix: + fast_finish: true + +deploy: + description: 'WasmerInstaller' + artifact: /.*\.exe/ + auth_token: + secure: CaKtncy7S1PWxzDUQ0p2264pe3HwxzDn5VIyRizDaa72/SVfskNcoMjwwRh0ut22 + provider: GitHub + on: + branch: master + appveyor_repo_tag: true diff --git a/.circleci/config.yml b/.circleci/config.yml index 52160f216..1a462b680 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -13,6 +13,8 @@ jobs: name: Install dependencies command: | sudo apt-get install -y cmake + curl -O https://releases.llvm.org/7.0.0/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz + tar xf clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz - run: name: Install lint deps command: | @@ -20,7 +22,9 @@ jobs: rustup component add clippy - run: name: Execute lints - command: make lint + command: | + export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/" + make lint - save_cache: paths: - /usr/local/cargo/registry @@ -41,8 +45,23 @@ jobs: name: Install dependencies command: | sudo apt-get install -y cmake - - run: make test - - run: make integration-tests + curl -O https://releases.llvm.org/7.0.0/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz + tar xf clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz + - run: + name: Tests + command: | + export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/" + make test + - run: + name: Emscripten Tests + command: | + export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/" + make test-emscripten + - run: + name: Integration Tests + command: | + export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/" + make integration-tests - save_cache: paths: - /usr/local/cargo/registry @@ -66,6 +85,9 @@ jobs: curl -O https://cmake.org/files/v3.4/cmake-3.4.1-Darwin-x86_64.tar.gz tar xf cmake-3.4.1-Darwin-x86_64.tar.gz export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH" + # Installing LLVM outside of brew + curl -O https://releases.llvm.org/7.0.0/clang+llvm-7.0.0-x86_64-apple-darwin.tar.xz + tar xf clang+llvm-7.0.0-x86_64-apple-darwin.tar.xz - run: name: Install Rust command: | @@ -73,19 +95,31 @@ jobs: export PATH="$HOME/.cargo/bin:$PATH" cargo --version - run: - name: Execute tests + name: Tests command: | export PATH="$HOME/.cargo/bin:$PATH" export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH" + export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-apple-darwin/" # We increase the ulimit for fixing cargo unclosed files in mac ulimit -n 8000 sudo sysctl -w kern.maxfiles=655360 kern.maxfilesperproc=327680 make test - run: - name: Execute integration tests + name: Emscripten Tests command: | export PATH="$HOME/.cargo/bin:$PATH" export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH" + export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-apple-darwin/" + # We increase the ulimit for fixing cargo unclosed files in mac + ulimit -n 8000 + sudo sysctl -w kern.maxfiles=655360 kern.maxfilesperproc=327680 + make test-emscripten + - run: + name: Integration Tests + command: | + export PATH="$HOME/.cargo/bin:$PATH" + export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH" + export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-apple-darwin/" make integration-tests - save_cache: paths: @@ -110,12 +144,22 @@ jobs: name: Install dependencies command: | sudo apt-get install -y cmake + curl -O https://releases.llvm.org/7.0.0/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz + tar xf clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz - run: - name: Execute tests - command: make test - - run: - name: Make release build + name: Tests command: | + export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/" + make test + - run: + name: Emscripten Tests + command: | + export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/" + make test-emscripten + - run: + name: Release Build + command: | + export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/" make release mkdir -p artifacts VERSION=$(cargo pkgid | cut -d# -f2 | cut -d: -f2) @@ -153,6 +197,9 @@ jobs: curl -O https://cmake.org/files/v3.4/cmake-3.4.1-Darwin-x86_64.tar.gz tar xf cmake-3.4.1-Darwin-x86_64.tar.gz export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH" + # Installing LLVM outside of brew + curl -O https://releases.llvm.org/7.0.0/clang+llvm-7.0.0-x86_64-apple-darwin.tar.xz + tar xf clang+llvm-7.0.0-x86_64-apple-darwin.tar.xz - run: name: Install Rust command: | @@ -160,19 +207,31 @@ jobs: export PATH="$HOME/.cargo/bin:$PATH" cargo --version - run: - name: Execute tests + name: Tests command: | export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH" export PATH="$HOME/.cargo/bin:$PATH" + export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-apple-darwin/" # We increase the ulimit for fixing cargo unclosed files in mac ulimit -n 8000 sudo sysctl -w kern.maxfiles=655360 kern.maxfilesperproc=327680 make test - run: - name: Make release build + name: Emscripten Tests command: | export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH" export PATH="$HOME/.cargo/bin:$PATH" + export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-apple-darwin/" + # We increase the ulimit for fixing cargo unclosed files in mac + ulimit -n 8000 + sudo sysctl -w kern.maxfiles=655360 kern.maxfilesperproc=327680 + make test-emscripten + - run: + name: Release Build + command: | + export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH" + export PATH="$HOME/.cargo/bin:$PATH" + export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-apple-darwin/" make release mkdir -p artifacts # VERSION=$(cargo pkgid | cut -d# -f2 | cut -d: -f2) @@ -205,8 +264,12 @@ jobs: name: Install dependencies command: | sudo apt-get install -y cmake + curl -O https://releases.llvm.org/7.0.0/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz + tar xf clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz - run: rustup default nightly - - run: make test + - run: | + export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/" + make test - save_cache: paths: - /usr/local/cargo/registry @@ -249,23 +312,31 @@ workflows: - test: filters: branches: - ignore: master + only: + - trying + - staging - test-macos: filters: branches: - ignore: master + only: + - trying + - staging - test-and-build: filters: branches: - only: master + only: + - master - test-and-build-macos: filters: branches: - only: master + only: + - master - test-rust-nightly: filters: branches: - only: master + only: + - trying + - staging - publish-github-release: requires: - lint diff --git a/.github/ISSUE_TEMPLATE/---bug-report.md b/.github/ISSUE_TEMPLATE/---bug-report.md new file mode 100644 index 000000000..7a7e83e12 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/---bug-report.md @@ -0,0 +1,43 @@ +--- +name: "\U0001F41E Bug report" +about: Create a report to help us improve +title: '' +labels: "\U0001F41E bug" +assignees: '' + +--- + +Thanks for the bug report! + +### Describe the bug + +A clear and concise description of what the bug is. + +Copy and paste the result of executing the following in your shell, so we can know the version of wasmer, Rust (if available) and architecture of your environment. + +```bash +echo "`wasmer -V` | `rustc -V` | `uname -m`" +``` + +### Steps to reproduce + +1. Go to '…' +2. Compile with '…' +3. Run '…' +4. See error + +If applicable, add a link to a test case (as a zip file or link to a repository we can clone). + +### Expected behavior + +A clear and concise description of what you expected to happen. + +### Actual behavior + +A clear and concise description of what actually happened. + +If applicable, add screenshots to help explain your problem. + +### Additional context + +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/---feature-request.md b/.github/ISSUE_TEMPLATE/---feature-request.md new file mode 100644 index 000000000..be5746331 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/---feature-request.md @@ -0,0 +1,26 @@ +--- +name: "\U0001F389 Feature request" +about: Suggest an idea for this project +title: '' +labels: "\U0001F389 enhancement" +assignees: '' + +--- + +Thanks for proposing a new feature! + +### Motivation + +A clear and concise description of what the motivation for the new feature is, and what problem it is solving. + +### Proposed solution + +A clear and concise description of the feature you would like to add, and how it solves the motivating problem. + +### Alternatives + +A clear and concise description of any alternative solutions or features you've considered, and why you're proposed solution is better. + +### Additional context + +Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/--question.md b/.github/ISSUE_TEMPLATE/--question.md new file mode 100644 index 000000000..ad79ab2f5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/--question.md @@ -0,0 +1,16 @@ +--- +name: "❓ Question" +about: Ask a question about this project +title: '' +labels: "❓ question" +assignees: '' + +--- + +### Summary + +A clear and concise summary of your question. + +### Additional details + +Provide any additional details here. diff --git a/.gitignore b/.gitignore index dd8330f3e..141efbf78 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ /artifacts .DS_Store .idea +**/.vscode 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 4776d7f42..165eb0044 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,3 +1,13 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "aho-corasick" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "ansi_term" version = "0.11.0" @@ -6,12 +16,25 @@ dependencies = [ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "arrayref" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "arrayvec" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "atty" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -21,54 +44,135 @@ name = "autocfg" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "backtrace" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "backtrace-sys" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cexpr 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "clang-sys 0.26.4 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "which 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "bitflags" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "block-buffer" -version = "0.7.0" +name = "blake2b_simd" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "block-padding 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "block-padding" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "byte-tools" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "byteorder" version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "capstone" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "capstone-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "capstone-sys" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cast" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "cbindgen" +version = "0.8.2" +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)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)", + "tempfile 3.0.7 (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" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "cfg-if" -version = "0.1.6" +name = "cexpr" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nom 4.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cfg-if" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "clang-sys" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libloading 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "clap" @@ -97,9 +201,14 @@ name = "cmake" version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "constant_time_eq" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "cranelift-bforest" version = "0.26.0" @@ -170,6 +279,88 @@ dependencies = [ "wasmparser 0.22.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "criterion" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", + "criterion-plot 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "csv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_xoshiro 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "tinytemplate 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "criterion-plot" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-deque" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-utils" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "csv" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "csv-core 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "csv-core" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "digest" version = "0.8.0" @@ -180,16 +371,16 @@ dependencies = [ [[package]] name = "dynasm" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -202,13 +393,39 @@ dependencies = [ "take_mut 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "either" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "enum-methods" +version = "0.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "env_logger" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "errno" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "errno-dragonfly 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -218,7 +435,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -226,6 +443,7 @@ name = "failure" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "backtrace 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -234,34 +452,20 @@ name = "failure_derive" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)", "synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "fake-simd" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "field-offset" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "fuchsia-zircon" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "fuchsia-zircon-sys" -version = "0.3.3" +name = "fuchsia-cprng" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -282,6 +486,16 @@ name = "glob" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "goblin" +version = "0.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "scroll 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "hashbrown" version = "0.1.8" @@ -289,7 +503,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -300,11 +514,53 @@ dependencies = [ "unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "hex" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "humantime" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "indexmap" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "inkwell" +version = "0.1.0" +source = "git+https://github.com/wasmerio/inkwell?branch=llvm7-0#b541566a0ac18a0f52bef54596d00c740c8b0597" +dependencies = [ + "either 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "enum-methods 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "inkwell_internal_macros 0.1.0 (git+https://github.com/wasmerio/inkwell?branch=llvm7-0)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "llvm-sys 70.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "inkwell_internal_macros" +version = "0.1.0" +source = "git+https://github.com/wasmerio/inkwell?branch=llvm7-0#b541566a0ac18a0f52bef54596d00c740c8b0597" +dependencies = [ + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "itertools" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "either 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "itoa" version = "0.4.3" @@ -321,18 +577,34 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.48" -source = "git+https://github.com/rust-lang/libc#42cd3ba27254c423e03f6f4324de57075047f6a0" +version = "0.2.50" +source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "libc" -version = "0.2.48" +name = "libloading" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "llvm-sys" +version = "70.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "lock_api" @@ -348,7 +620,15 @@ name = "log" version = "0.4.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)", + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "memchr" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -356,7 +636,7 @@ name = "memmap" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -365,19 +645,24 @@ name = "memmap" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "memoffset" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "nix" version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", - "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)", + "cc 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -387,17 +672,39 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", - "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)", + "cc 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "opaque-debug" -version = "0.2.2" +name = "nodrop" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "nom" +version = "4.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-traits" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "num_cpus" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "owning_ref" version = "0.3.3" @@ -420,7 +727,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -438,27 +745,47 @@ name = "parking_lot_core" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "proc-macro2" -version = "0.4.26" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "quick-error" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "quote" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "quote" version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -467,14 +794,14 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_jitter 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_os 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_jitter 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -519,34 +846,34 @@ dependencies = [ [[package]] name = "rand_jitter" -version = "0.1.0" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand_os" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand_pcg" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -557,16 +884,46 @@ dependencies = [ "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rand_xoshiro" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "raw-cpuid" version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rayon" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rayon-core" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rdrand" version = "0.4.0" @@ -588,6 +945,39 @@ dependencies = [ "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "regex" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex-syntax" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "rustc_version" version = "0.2.3" @@ -601,11 +991,38 @@ name = "ryu" version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "same-file" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "scopeguard" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "scroll" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "scroll_derive 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "scroll_derive" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "semver" version = "0.9.0" @@ -621,8 +1038,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.85" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "serde-bench" @@ -630,55 +1050,41 @@ version = "0.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_bytes" -version = "0.10.4" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_derive" -version = "1.0.85" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_json" -version = "1.0.37" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "sha2" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-buffer 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "smallvec" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "stable_deref_trait" @@ -692,42 +1098,60 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "structopt" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", - "structopt-derive 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt-derive 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "structopt-derive" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "syn" -version = "0.15.26" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "syn" +version = "0.15.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "synom" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "synstructure" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -743,7 +1167,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tempfile" +version = "3.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (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" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -751,7 +1196,7 @@ name = "termion" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -764,21 +1209,51 @@ dependencies = [ "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "thread_local" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "time" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tinytemplate" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.39 (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.89 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "typenum" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "ucd-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unicode-segmentation" version = "1.2.1" @@ -789,24 +1264,31 @@ name = "unicode-width" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unicode-xid" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unicode-xid" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "unreachable" -version = "1.0.0" +name = "utf8-ranges" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "vec_map" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "version_check" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "void" version = "1.0.2" @@ -817,9 +1299,9 @@ name = "wabt" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", "wabt-sys 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -828,27 +1310,38 @@ name = "wabt-sys" version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", "cmake 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "walkdir" +version = "2.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "wasmer" -version = "0.1.4" +version = "0.2.1" dependencies = [ "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "structopt 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-clif-backend 0.1.2", - "wasmer-emscripten 0.1.0", - "wasmer-runtime 0.1.4", - "wasmer-runtime-core 0.1.2", + "wasmer-clif-backend 0.2.0", + "wasmer-emscripten 0.2.1", + "wasmer-llvm-backend 0.1.0", + "wasmer-runtime 0.2.1", + "wasmer-runtime-core 0.2.1", ] [[package]] name = "wasmer-clif-backend" -version = "0.1.2" +version = "0.2.0" dependencies = [ "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "cranelift-codegen 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -856,15 +1349,18 @@ dependencies = [ "cranelift-native 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)", "cranelift-wasm 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)", "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde-bench 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_bytes 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_bytes 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "target-lexicon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-runtime-core 0.1.2", + "wasmer-runtime-core 0.2.1", + "wasmer-win-exception-handler 0.2.0", "wasmparser 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -872,67 +1368,119 @@ name = "wasmer-dynasm-backend" version = "0.1.0" dependencies = [ "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dynasm 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dynasm 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "dynasmrt 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-runtime-core 0.1.2", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-runtime-core 0.2.1", "wasmparser 0.28.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasmer-emscripten" -version = "0.1.0" +version = "0.2.1" dependencies = [ "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (git+https://github.com/rust-lang/libc)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-clif-backend 0.1.2", - "wasmer-runtime-core 0.1.2", + "wasmer-clif-backend 0.2.0", + "wasmer-llvm-backend 0.1.0", + "wasmer-runtime-core 0.2.1", +] + +[[package]] +name = "wasmer-llvm-backend" +version = "0.1.0" +dependencies = [ + "capstone 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", + "goblin 0.0.20 (registry+https://github.com/rust-lang/crates.io-index)", + "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "inkwell 0.1.0 (git+https://github.com/wasmerio/inkwell?branch=llvm7-0)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "nix 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", + "wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-runtime-core 0.2.1", + "wasmparser 0.28.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasmer-runtime" -version = "0.1.4" +version = "0.2.1" dependencies = [ - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-clif-backend 0.1.2", + "criterion 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-clif-backend 0.2.0", "wasmer-dynasm-backend 0.1.0", - "wasmer-runtime-core 0.1.2", + "wasmer-llvm-backend 0.1.0", + "wasmer-runtime-core 0.2.1", +] + +[[package]] +name = "wasmer-runtime-c-api" +version = "0.2.1" +dependencies = [ + "cbindgen 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-runtime 0.2.1", + "wasmer-runtime-core 0.2.1", ] [[package]] name = "wasmer-runtime-core" -version = "0.1.2" +version = "0.2.1" dependencies = [ + "blake2b_simd 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "field-offset 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", - "memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "page_size 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde-bench 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_bytes 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-clif-backend 0.1.2", + "serde_bytes 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "wasmparser 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasmer-spectests" -version = "0.1.2" +version = "0.2.0" dependencies = [ "wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-clif-backend 0.1.2", - "wasmer-runtime-core 0.1.2", + "wasmer-clif-backend 0.2.0", + "wasmer-llvm-backend 0.1.0", + "wasmer-runtime-core 0.2.1", +] + +[[package]] +name = "wasmer-win-exception-handler" +version = "0.2.0" +dependencies = [ + "bindgen 0.46.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cmake 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-runtime-core 0.2.1", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -950,6 +1498,15 @@ name = "wasmparser" version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "which" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "winapi" version = "0.2.8" @@ -974,26 +1531,53 @@ name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "winapi-util" +version = "0.1.2" +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 = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "wincolor" +version = "1.0.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)", + "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [metadata] +"checksum aho-corasick 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "81ce3d38065e618af2d7b77e10c5ad9a069859b4be3c2250f674af3840d9c8a5" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" +"checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" "checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799" +"checksum backtrace 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "cd5a90e2b463010cd0e0ce9a11d4a9d5d58d9f41d4a6ba3dcaf9e68b466e88b4" +"checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" +"checksum bindgen 0.46.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8f7f7f0701772b17de73e4f5cbcb1dd6926f4706cba4c1ab62c5367f8bdc94e1" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" -"checksum block-buffer 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49665c62e0e700857531fa5d3763e91b539ff1abeebd56808d378b495870d60d" -"checksum block-padding 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d75255892aeb580d3c566f213a2b6fdc1c66667839f45719ee1d30ebf2aea591" -"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +"checksum blake2b_simd 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce2571a6cd634670daa2977cc894c1cc2ba57c563c498e5a82c35446f34d056e" "checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" +"checksum capstone 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "00be9d203fa0e078b93b24603633fb081851dfe0c1086364431f52587a47157e" +"checksum capstone-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2dc8d32bc5c1e6d0fcde10af411c98b07d93498d51654f678757f08fa2acd6a6" "checksum cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "926013f2860c46252efceabb19f4a6b308197505082c609025aa6706c011d427" -"checksum cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4a8b715cb4597106ea87c7c84b2f1d452c7492033765df7f32651e66fcf749" -"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4" +"checksum cbindgen 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f61c5411fe3ac196fae7ea397dd13959b1323edda046eec50d648a8e92015a53" +"checksum cc 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)" = "d01c69d08ff207f231f07196e30f84c70f1c815b04f980f8b7b01ff01f05eb92" +"checksum cexpr 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "644d693ecfa91955ed32dcc7eda4914e1be97a641fb6f0645a37348e20b230da" +"checksum cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11d43355396e872eefb45ce6342e4374ed7bc2b3a502d1b28e36d6e23c05d1f4" +"checksum clang-sys 0.26.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6ef0c1bcf2e99c649104bd7a7012d8f8802684400e03db0ec0af48583c6fa0e4" "checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum cmake 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "6ec65ee4f9c9d16f335091d23693457ed4928657ba4982289d7fafee03bc614a" +"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e" "checksum cranelift-bforest 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "40f8ff24e9a6c89b8a846b14df9a34d2cac17cea7bdb5c81ed6b4744ee0e38bf" "checksum cranelift-codegen 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "42f5b809bd885c368e01aeec8fe04f21dcb07569834b907d75b4a7bed8d067eb" "checksum cranelift-codegen-meta 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "014c23ed3ebdc8377d41540af638245207dd169f421df042dfccc867465734ed" @@ -1001,41 +1585,65 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum cranelift-frontend 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "789907218eeebebcea8122c2053d71affac91c96ce72cea35ebfdbbf547e82af" "checksum cranelift-native 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "474bee81d620a473bf43411a3d6f10ffbf7965141dc5e5b76d8d2151dde3285d" "checksum cranelift-wasm 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49723365dab9a48b354bdc24cb6d9d5719bc1d3b858ffd2ea179d0d7d885804a" +"checksum criterion 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "1c6e5ee5b9652d4f851418c448af105642e1f99e9a2741a8ff45c0d2c911b1e0" +"checksum criterion-plot 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4107e4a5abb94267e0149922b8ff49dc70a87cc202820fdbfc0d3e1edbdc4b16" +"checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" +"checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150" +"checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" +"checksum csv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "9fd1c44c58078cfbeaf11fbb3eac9ae5534c23004ed770cc4bfb48e658ae4f04" +"checksum csv-core 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa5cdef62f37e6ffe7d1f07a381bc0db32b7a3ff1cac0de56cb0d81e71f53d65" "checksum digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f47366984d3ad862010e22c7ce81a7dbcaebbdfb37241a620f8b6596ee135c" -"checksum dynasm 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6aa959a3e40b6c4292053b150062d2abd795b3285c58d4be9b9ad3ef2264a26f" +"checksum dynasm 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b77e128faecc4d16cff7cae96c0c9e809f687f748a0dbc4d017996e48240a991" "checksum dynasmrt 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a4c408a211e7f5762829f5e46bdff0c14bc3b1517a21a4bb781c716bf88b0c68" +"checksum either 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c67353c641dc847124ea1902d69bd753dee9bb3beff9aa3662ecf86c971d1fac" +"checksum enum-methods 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7798e7da2d4cb0d6d6fc467e8d6b5bf247e9e989f786dde1732d79899c32bb10" +"checksum env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b61fa891024a945da30a9581546e8cfaf5602c7b3f4c137a2805cf388f92075a" "checksum errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2a071601ed01b988f896ab14b95e67335d1eeb50190932a1320f7fe3cadc84e" "checksum errno-dragonfly 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "14ca354e36190500e1e1fb267c647932382b54053c50b14970856c0b00a35067" "checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" "checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" -"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" "checksum field-offset 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "64e9bc339e426139e02601fa69d101e96a92aee71b58bc01697ec2a63a5c9e68" -"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" -"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" "checksum gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)" = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" "checksum generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c0f28c2f5bfb5960175af447a2da7c18900693738343dc896ffbcabd9839592" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" +"checksum goblin 0.0.20 (registry+https://github.com/rust-lang/crates.io-index)" = "84473a5302fa5094d3d9911c2f312f522f9a37462a777f195f63fae1bf7faf4d" "checksum hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3bae29b6653b3412c2e71e9d486db9f9df5d701941d86683005efb9f2d28e3da" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" +"checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" +"checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" "checksum indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d" +"checksum inkwell 0.1.0 (git+https://github.com/wasmerio/inkwell?branch=llvm7-0)" = "" +"checksum inkwell_internal_macros 0.1.0 (git+https://github.com/wasmerio/inkwell?branch=llvm7-0)" = "" +"checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" "checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -"checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1" -"checksum libc 0.2.48 (git+https://github.com/rust-lang/libc)" = "" -"checksum libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)" = "e962c7641008ac010fa60a7dfdc1712449f29c44ef2d4702394aea943ee75047" +"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" +"checksum libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)" = "aab692d7759f5cd8c859e169db98ae5b52c924add2af5fbbca11d12fefb567c1" +"checksum libloading 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3ad660d7cb8c5822cd83d10897b0f1f1526792737a179e73896152f85b88c2" +"checksum llvm-sys 70.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60a9ee82fe0fa72ae6ef6d018b407296085863836451c7a97384f84ed7e26b9f" "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" +"checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39" "checksum memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2ffa2c986de11a9df78620c01eeaaf27d94d3ff02bf81bfcca953102dd0c6ff" "checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" +"checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" "checksum nix 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "921f61dc817b379d0834e45d5ec45beaacfae97082090a49c2cf30dcbc30206f" "checksum nix 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46f0f3210768d796e8fa79ec70ee6af172dacbe7147f5e69be5240a47778302b" -"checksum opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "93f5bb2e8e8dec81642920ccff6b61f1eb94fa3020c5a325c9851ff604152409" +"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" +"checksum nom 4.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "22293d25d3f33a8567cc8a1dc20f40c7eeb761ce83d0fcca059858580790cac3" +"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" +"checksum num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a23f0ed30a54abaa0c7e83b1d2d87ada7c3c23078d1d87815af3e3b6385fbba" "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" "checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13" "checksum page_size 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f89ef58b3d32420dbd1a43d2f38ae92f6239ef12bb556ab09ca55445f5a67242" "checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337" "checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" -"checksum proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)" = "38fddd23d98b2144d197c0eca5705632d4fe2667d14a6be5df8934f8d74f1978" +"checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +"checksum plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" +"checksum proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)" = "4d317f9caece796be1980837fd5cb3dfec5613ebdb04ad0956deea83ce168915" +"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" +"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "checksum quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cdd8e04bd9c52e0342b406469d494fcb033be4bdbe5c606016defbb1681411e1" "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" "checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" @@ -1043,51 +1651,74 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0" "checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" "checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -"checksum rand_jitter 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f47842851e13bd803b506bdd1345328e0a1394733ee58e627b5e39332b9afafe" -"checksum rand_os 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f46fbd5550acf75b0c2730f5dd1873751daf9beb8f11b44027778fae50d7feca" -"checksum rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "086bd09a33c7044e56bb44d5bdde5a60e7f119a9e95b0775f545de759a32fe05" +"checksum rand_jitter 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b9ea758282efe12823e0d952ddb269d2e1897227e464919a554f2a03ef1b832" +"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" "checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +"checksum rand_xoshiro 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "03b418169fb9c46533f326efd6eed2576699c44ca92d3052a066214a8d828929" "checksum raw-cpuid 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "30a9d219c32c9132f7be513c18be77c9881c7107d2ab5569d205a6a0f0e6dc7d" +"checksum rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "373814f27745b2686b350dd261bfd24576a6fb0e2c5919b3a2b6005f820b0473" +"checksum rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b055d1e92aba6877574d8fe604a63c8b5df60f60e5982bf7ccbb1338ea527356" "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" "checksum redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)" = "423e376fffca3dfa06c9e9790a9ccd282fafb3cc6e6397d01dbf64f9bacc6b85" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" +"checksum regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53ee8cfdddb2e0291adfb9f13d31d3bbe0a03c9a402c01b1e24188d86c35b24f" +"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" +"checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" +"checksum scroll 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f84d114ef17fd144153d608fba7c446b0145d038985e7a8cc5d08bb0ce20383" +"checksum scroll_derive 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8f1aa96c45e7f5a91cb7fabe7b279f02fea7126239fc40b732316e8b6a2d0fcb" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)" = "534b8b91a95e0f71bca3ed5824752d558da048d4248c91af873b63bd60519752" +"checksum serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)" = "92514fb95f900c9b5126e32d020f5c6d40564c27a5ea6d1d7d9f157a96623560" "checksum serde-bench 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "d733da87e79faaac25616e33d26299a41143fd4cd42746cbb0e91d8feea243fd" -"checksum serde_bytes 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)" = "adb6e51a6b3696b301bc221d785f898b4457c619b51d7ce195a6d20baecb37b3" -"checksum serde_derive 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)" = "a915306b0f1ac5607797697148c223bedeaa36bcc2e28a01441cd638cc6567b4" -"checksum serde_json 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "4b90a9fbe1211e57d3e1c15670f1cb00802988fb23a1a4aad7a2b63544f1920e" -"checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d" -"checksum smallvec 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "88aea073965ab29f6edb5493faf96ad662fb18aa9eeb186a3b7057951605ed15" +"checksum serde_bytes 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)" = "defbb8a83d7f34cc8380751eeb892b825944222888aff18996ea7901f24aec88" +"checksum serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6eabf4b5914e88e24eea240bb7c9f9a2cbc1bbbe8d961d381975ec3c6b806c" +"checksum serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)" = "5a23aa71d4a4d43fdbfaac00eff68ba8a06a51759a89ac3304323e800c4dd40d" +"checksum smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c4488ae950c49d403731982257768f48fada354a5203fe81f9bb6f43ca9002be" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" -"checksum structopt 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "670ad348dc73012fcf78c71f06f9d942232cdd4c859d4b6975e27836c3efc0c3" -"checksum structopt-derive 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "ef98172b1a00b0bec738508d3726540edcbd186d50dfd326f2b1febbb3559f04" -"checksum syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)" = "f92e629aa1d9c827b2bb8297046c1ccffc57c99b947a680d3ccff1f136a3bee9" +"checksum structopt 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "3d0760c312538987d363c36c42339b55f5ee176ea8808bbe4543d484a291c8d1" +"checksum structopt-derive 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "528aeb7351d042e6ffbc2a6fb76a86f9b622fdf7c25932798e7a82cb03bc94c6" +"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" +"checksum syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1825685f977249735d510a242a6727b46efe914bb67e38d30c071b1b72b1d5c2" +"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" "checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015" "checksum take_mut 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" "checksum target-lexicon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4af5e2227f0b887d591d3724b796a96eff04226104d872f5b3883fcd427d64b9" +"checksum tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b86c784c88d98c801132806dadd3819ed29d8600836c4088e855cdf3e178ed8a" +"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 tinytemplate 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7655088894274afb52b807bd3c87072daa1fedd155068b8705cabfd628956115" +"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" "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" +"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" -"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" +"checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737" "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" +"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "74e463a508e390cc7447e70f640fbf44ad52e1bd095314ace1fdf99516d32add" "checksum wabt-sys 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a6265b25719e82598d104b3717375e37661d41753e2c84cde3f51050c7ed7e3c" +"checksum walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d9d7ed3431229a144296213105a390676cc49c9b6a72bd19f3176c98e129fa1" "checksum wasmparser 0.22.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f46e666ecb4a406483a59a49f9d0c17f327e70da53a128eccddae2eadb95865c" "checksum wasmparser 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b5e01c420bc7d36e778bd242e1167b079562ba8b34087122cc9057187026d060" "checksum wasmparser 0.28.0 (registry+https://github.com/rust-lang/crates.io-index)" = "40f426b1929bd26517fb10702e2a8e520d1845c49567aa4d244f426f10b206c1" +"checksum which 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b57acb10231b9493c8472b20cb57317d0679a49e0bdbee44b3b803a6473af164" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +"checksum wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba" diff --git a/Cargo.toml b/Cargo.toml index fa0e68a00..b082502ad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer" -version = "0.1.4" +version = "0.2.1" authors = ["The Wasmer Engineering Team "] edition = "2018" repository = "https://github.com/wasmerio/wasmer" @@ -26,8 +26,11 @@ wasmer-runtime = { path = "lib/runtime" } wasmer-runtime-core = { path = "lib/runtime-core" } wasmer-emscripten = { path = "lib/emscripten" } +[target.'cfg(not(windows))'.dependencies] +wasmer-llvm-backend = { path = "lib/llvm-backend" } + [workspace] -members = ["lib/clif-backend", "lib/dynasm-backend", "lib/runtime", "lib/runtime-core", "lib/emscripten", "lib/spectests"] +members = ["lib/clif-backend", "lib/dynasm-backend", "lib/runtime", "lib/runtime-core", "lib/emscripten", "lib/spectests", "lib/win-exception-handler", "lib/runtime-c-api", "lib/llvm-backend"] [build-dependencies] wabt = "0.7.2" @@ -36,6 +39,6 @@ glob = "0.2.11" [features] default = ["fast-tests"] -debug = [] +debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"] # This feature will allow cargo test to run much faster fast-tests = [] 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..0c9a0692d 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ emtests: # rm -rf artifacts build: - cargo build + cargo build --features debug install: cargo install --path . @@ -34,15 +34,25 @@ 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 --exclude wasmer-emscripten --exclude wasmer-spectests -- $(runargs) # cargo test --all --exclude wasmer-emscripten -- --test-threads=1 $(runargs) - # cargo test -p wasmer-spectests -- --test-threads=1 $(runargs) + cargo test --manifest-path lib/spectests/Cargo.toml --features clif + cargo test --manifest-path lib/spectests/Cargo.toml --features llvm + cargo build -p wasmer-runtime-c-api + cargo test -p wasmer-runtime-c-api -- --nocapture + +test-emscripten: + cargo test --manifest-path lib/emscripten/Cargo.toml --features clif -- --test-threads=1 $(runargs) + cargo test --manifest-path lib/emscripten/Cargo.toml --features llvm -- --test-threads=1 $(runargs) release: # If you are in OS-X, you will need mingw-w64 for cross compiling to windows # brew install mingw-w64 cargo build --release +debug-release: + cargo build --release --features debug + debug-release: cargo build --release --features "debug" diff --git a/README.md b/README.md index 2aae6e086..53677bca9 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,18 +47,18 @@ 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 install on Windows, download and run [`rustup-init.exe`](https://win.rustup.rs/) +To build on Windows, download and run [`rustup-init.exe`](https://win.rustup.rs/) then follow the onscreen instructions. -To install on other systems, run: +To build on other systems, run: ```sh curl https://sh.rustup.rs -sSf | sh @@ -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,15 +94,23 @@ sudo apt install cmake #### Windows (MSVC) -Right now Windows support is _highly experimental_. -We are working on this so Wasmer can soon be released for Windows. +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 [Visual Studio](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&rel=15) -2. Install Git for Windows (https://git-scm.com/download/win). DO allow it to add git.exe to the PATH (default +2. Install [Rust for Windows](https://win.rustup.rs) + +3. 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. + +4. 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). +5. Install [CMake](https://cmake.org/download/). Ensure CMake is in your PATH. + +6. Install [LLVM 7.0](https://prereleases.llvm.org/win-snapshots/LLVM-7.0.0-r336178-win64.exe) + ## Building Wasmer is built with [Cargo](https://crates.io/), the Rust package manager. @@ -111,7 +127,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: @@ -119,7 +135,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 @@ -132,24 +148,32 @@ You can also run integration tests with: make integration-tests ``` +## Benchmarking + +Benchmarks can be run with: + +```sh +cargo bench --all +``` + ## Roadmap 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/bors.toml b/bors.toml new file mode 100644 index 000000000..c56141cd5 --- /dev/null +++ b/bors.toml @@ -0,0 +1,9 @@ +status = [ + "ci/circleci: lint", + "ci/circleci: test", + "ci/circleci: test-macos", + "continuous-integration/appveyor/branch" +] +required_approvals = 1 +timeout_sec = 900 +delete_merged_branches = true \ No newline at end of file diff --git a/examples/nginx/LICENSE b/examples/nginx/LICENSE new file mode 100644 index 000000000..c63e0ba4e --- /dev/null +++ b/examples/nginx/LICENSE @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2002-2019 Igor Sysoev + * Copyright (C) 2011-2019 Nginx, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ diff --git a/install.sh b/install.sh index 196fab7d5..0b7da4ca2 100755 --- a/install.sh +++ b/install.sh @@ -130,10 +130,10 @@ wasmer_link() { printf "$cyan> Adding to bash profile...$reset\n" WASMER_PROFILE="$(wasmer_detect_profile)" LOAD_STR="\n# Wasmer\nexport WASMER_DIR=\"\$HOME/.wasmer\"\n[ -s \"\$WASMER_DIR/wasmer.sh\" ] && source \"\$WASMER_DIR/wasmer.sh\" # This loads wasmer\n" - SOURCE_STR="# Wasmer config\nexport WASMER_DIR=\"\$HOME/.wasmer\"\nexport PATH=\"\$HOME/.wasmer/bin:\$PATH\"\n" + SOURCE_STR="# Wasmer config\nexport WASMER_DIR=\"\$HOME/.wasmer\"\nexport WASMER_CACHE_DIR=\"\$WASMER_DIR/cache\"\nexport PATH=\"\$HOME/.wasmer/bin:\$PATH\"\n" # We create the wasmer.sh file - echo "$SOURCE_STR" > "$HOME/.wasmer/wasmer.sh" + printf "$SOURCE_STR" > "$HOME/.wasmer/wasmer.sh" if [ -z "${WASMER_PROFILE-}" ] ; then printf "${red}Profile not found. Tried:\n* ${WASMER_PROFILE} (as defined in \$PROFILE)\n* ~/.bashrc\n* ~/.bash_profile\n* ~/.zshrc\n* ~/.profile.\n" 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/lua/test.sh b/integration_tests/lua/test.sh index b45752f0b..7c3cc9b6c 100755 --- a/integration_tests/lua/test.sh +++ b/integration_tests/lua/test.sh @@ -1,7 +1,6 @@ #! /bin/bash -nohup ./target/release/wasmer run examples/lua.wasm & -sleep 3s +nohup ./target/release/wasmer run examples/lua.wasm --disable-cache -- -v if grep "Lua 5.4.0 Copyright (C) 1994-2018 Lua.org, PUC-Rio" ./nohup.out then 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/integration_tests/nginx/test.sh b/integration_tests/nginx/test.sh index aeb790e28..a2cfb7159 100755 --- a/integration_tests/nginx/test.sh +++ b/integration_tests/nginx/test.sh @@ -1,22 +1,14 @@ #! /bin/bash -nohup ./target/release/wasmer run examples/nginx/nginx.wasm -- -p integration_tests/nginx/ -c nginx.conf & -sleep 3s +nohup ./target/release/wasmer run examples/nginx/nginx.wasm --disable-cache -- -v -curl localhost:8080 > ./nginx.out - - -if grep "wasmer" ./nginx.out +if grep "nginx version: nginx/1.15.3" ./nohup.out then echo "nginx integration test succeeded" rm ./nohup.out - rm ./nginx.out - rm -rf ./integration_tests/nginx/*_temp exit 0 else echo "nginx integration test failed" rm ./nohup.out - rm ./nginx.out - rm -rf ./integration_tests/nginx/*_temp exit -1 fi diff --git a/lib/README.md b/lib/README.md index 44b316865..47f3264e7 100644 --- a/lib/README.md +++ b/lib/README.md @@ -2,35 +2,36 @@ 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: +Currently, we support multiple backends for compiling WebAssembly to machine code: -- [clif-backend](./clif-backend/): The integration of Wasmer with Cranelift +- [clif-backend](./clif-backend/): Cranelift backend +- [llvm-backend](./llvm-backend/): LLVM backend diff --git a/lib/clif-backend/Cargo.toml b/lib/clif-backend/Cargo.toml index 5b565676b..3ac5105cf 100644 --- a/lib/clif-backend/Cargo.toml +++ b/lib/clif-backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-clif-backend" -version = "0.1.2" +version = "0.2.0" description = "Wasmer runtime Cranelift compiler backend" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -8,7 +8,7 @@ repository = "https://github.com/wasmerio/wasmer" edition = "2018" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.1.2" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.2.0" } cranelift-native = "0.26.0" cranelift-codegen = "0.26.0" cranelift-entity = "0.26.0" @@ -18,25 +18,22 @@ target-lexicon = "0.2.0" wasmparser = "0.23.0" byteorder = "1" nix = "0.13.0" -libc = "0.2.48" +libc = "0.2.49" +rayon = "1.0" # Dependencies for caching. [dependencies.serde] version = "1.0" -optional = true [dependencies.serde_derive] version = "1.0" -optional = true [dependencies.serde_bytes] version = "0.10" -optional = true -# [dependencies.bincode] -# version = "1.0.1" -# optional = true [dependencies.serde-bench] version = "0.0.7" -optional = true + +[target.'cfg(windows)'.dependencies] +winapi = { version = "0.3", features = ["errhandlingapi", "minwindef", "minwinbase", "winnt"] } +wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.2.0" } [features] -cache = ["serde", "serde_derive", "serde_bytes", "serde-bench", "wasmer-runtime-core/cache"] -debug = ["wasmer-runtime-core/debug"] \ No newline at end of file +debug = ["wasmer-runtime-core/debug"] diff --git a/lib/clif-backend/src/cache.rs b/lib/clif-backend/src/cache.rs index aef6adf4c..5af708703 100644 --- a/lib/clif-backend/src/cache.rs +++ b/lib/clif-backend/src/cache.rs @@ -1,16 +1,50 @@ use crate::relocation::{ExternalRelocation, TrapSink}; use hashbrown::HashMap; +use std::sync::Arc; use wasmer_runtime_core::{ - backend::sys::Memory, - cache::{Cache, Error}, - module::ModuleInfo, + backend::{sys::Memory, CacheGen}, + cache::{Artifact, Error}, + module::{ModuleInfo, ModuleInner}, structures::Map, types::{LocalFuncIndex, SigIndex}, }; use serde_bench::{deserialize, serialize}; +pub struct CacheGenerator { + backend_cache: BackendCache, + memory: Arc, +} + +impl CacheGenerator { + pub fn new(backend_cache: BackendCache, memory: Arc) -> Self { + Self { + backend_cache, + memory, + } + } +} + +impl CacheGen for CacheGenerator { + fn generate_cache( + &self, + module: &ModuleInner, + ) -> Result<(Box, Box<[u8]>, Memory), Error> { + let info = Box::new(module.info.clone()); + + // Clone the memory to a new location. This could take a long time, + // depending on the throughput of your memcpy implementation. + let compiled_code = (*self.memory).clone(); + + Ok(( + info, + self.backend_cache.into_backend_data()?.into_boxed_slice(), + compiled_code, + )) + } +} + #[derive(Serialize, Deserialize)] pub struct TrampolineCache { #[serde(with = "serde_bytes")] @@ -22,24 +56,24 @@ pub struct TrampolineCache { pub struct BackendCache { pub external_relocs: Map>, pub offsets: Map, - pub trap_sink: TrapSink, + pub trap_sink: Arc, pub trampolines: TrampolineCache, } impl BackendCache { - pub fn from_cache(cache: Cache) -> Result<(ModuleInfo, Memory, Self), Error> { + pub fn from_cache(cache: Artifact) -> Result<(ModuleInfo, Memory, Self), Error> { let (info, backend_data, compiled_code) = cache.consume(); - let backend_cache = deserialize(backend_data.as_slice()) - .map_err(|e| Error::DeserializeError(e.to_string()))?; + let backend_cache = + deserialize(&backend_data).map_err(|e| Error::DeserializeError(e.to_string()))?; Ok((info, compiled_code, backend_cache)) } - pub fn into_backend_data(self) -> Result, Error> { + pub fn into_backend_data(&self) -> Result, Error> { let mut buffer = Vec::new(); - serialize(&mut buffer, &self).map_err(|e| Error::SerializeError(e.to_string()))?; + serialize(&mut buffer, self).map_err(|e| Error::SerializeError(e.to_string()))?; Ok(buffer) } diff --git a/lib/clif-backend/src/func_env.rs b/lib/clif-backend/src/func_env.rs index 789195264..8a65444b7 100644 --- a/lib/clif-backend/src/func_env.rs +++ b/lib/clif-backend/src/func_env.rs @@ -32,10 +32,10 @@ impl<'env, 'module, 'isa> FuncEnv<'env, 'module, 'isa> { let mut signature = self.env.signatures[Converter(clif_sig_index).into()].clone(); // Add the vmctx parameter type to it - signature.params.push(ir::AbiParam::special( - self.pointer_type(), - ir::ArgumentPurpose::VMContext, - )); + signature.params.insert( + 0, + ir::AbiParam::special(self.pointer_type(), ir::ArgumentPurpose::VMContext), + ); // Return signature signature @@ -75,7 +75,7 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> { let vmctx = func.create_global_value(ir::GlobalValueData::VMContext); let ptr_type = self.pointer_type(); - let local_global_addr = match global_index.local_or_import(self.env.module) { + let local_global_addr = match global_index.local_or_import(&self.env.module.info) { LocalOrImport::Local(local_global_index) => { let globals_base_addr = func.create_global_value(ir::GlobalValueData::Load { base: vmctx, @@ -145,48 +145,49 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> { let vmctx = func.create_global_value(ir::GlobalValueData::VMContext); let ptr_type = self.pointer_type(); - let (local_memory_ptr_ptr, description) = match mem_index.local_or_import(self.env.module) { - LocalOrImport::Local(local_mem_index) => { - let memories_base_addr = func.create_global_value(ir::GlobalValueData::Load { - base: vmctx, - offset: (vm::Ctx::offset_memories() as i32).into(), - global_type: ptr_type, - readonly: true, - }); - - let local_memory_ptr_offset = - local_mem_index.index() * mem::size_of::<*mut vm::LocalMemory>(); - - ( - func.create_global_value(ir::GlobalValueData::IAddImm { - base: memories_base_addr, - offset: (local_memory_ptr_offset as i64).into(), + let (local_memory_ptr_ptr, description) = + match mem_index.local_or_import(&self.env.module.info) { + LocalOrImport::Local(local_mem_index) => { + let memories_base_addr = func.create_global_value(ir::GlobalValueData::Load { + base: vmctx, + offset: (vm::Ctx::offset_memories() as i32).into(), global_type: ptr_type, - }), - self.env.module.info.memories[local_mem_index], - ) - } - LocalOrImport::Import(import_mem_index) => { - let memories_base_addr = func.create_global_value(ir::GlobalValueData::Load { - base: vmctx, - offset: (vm::Ctx::offset_imported_memories() as i32).into(), - global_type: ptr_type, - readonly: true, - }); + readonly: true, + }); - let local_memory_ptr_offset = - import_mem_index.index() * mem::size_of::<*mut vm::LocalMemory>(); + let local_memory_ptr_offset = + local_mem_index.index() * mem::size_of::<*mut vm::LocalMemory>(); - ( - func.create_global_value(ir::GlobalValueData::IAddImm { - base: memories_base_addr, - offset: (local_memory_ptr_offset as i64).into(), + ( + func.create_global_value(ir::GlobalValueData::IAddImm { + base: memories_base_addr, + offset: (local_memory_ptr_offset as i64).into(), + global_type: ptr_type, + }), + self.env.module.info.memories[local_mem_index], + ) + } + LocalOrImport::Import(import_mem_index) => { + let memories_base_addr = func.create_global_value(ir::GlobalValueData::Load { + base: vmctx, + offset: (vm::Ctx::offset_imported_memories() as i32).into(), global_type: ptr_type, - }), - self.env.module.info.imported_memories[import_mem_index].1, - ) - } - }; + readonly: true, + }); + + let local_memory_ptr_offset = + import_mem_index.index() * mem::size_of::<*mut vm::LocalMemory>(); + + ( + func.create_global_value(ir::GlobalValueData::IAddImm { + base: memories_base_addr, + offset: (local_memory_ptr_offset as i64).into(), + global_type: ptr_type, + }), + self.env.module.info.imported_memories[import_mem_index].1, + ) + } + }; let (local_memory_ptr, local_memory_base) = { let local_memory_ptr = func.create_global_value(ir::GlobalValueData::Load { @@ -253,7 +254,8 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> { let vmctx = func.create_global_value(ir::GlobalValueData::VMContext); let ptr_type = self.pointer_type(); - let (table_struct_ptr_ptr, description) = match table_index.local_or_import(self.env.module) + let (table_struct_ptr_ptr, description) = match table_index + .local_or_import(&self.env.module.info) { LocalOrImport::Local(local_table_index) => { let tables_base = func.create_global_value(ir::GlobalValueData::Load { @@ -443,6 +445,12 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> { pos.ins().symbol_value(ir::types::I64, sig_index_global) + // let dynamic_sigindices_array_ptr = pos.ins().load( + // ptr_type, + // mflags, + + // ) + // let expected_sig = pos.ins().iconst(ir::types::I32, sig_index.index() as i64); // self.env.deduplicated[clif_sig_index] @@ -459,8 +467,8 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> { // Build a value list for the indirect call instruction containing the call_args // and the vmctx parameter. let mut args = Vec::with_capacity(call_args.len() + 1); - args.extend(call_args.iter().cloned()); args.push(vmctx_ptr); + args.extend(call_args.iter().cloned()); Ok(pos.ins().call_indirect(sig_ref, func_ptr, &args)) } @@ -475,9 +483,10 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> { call_args: &[ir::Value], ) -> cranelift_wasm::WasmResult { let callee_index: FuncIndex = Converter(clif_callee_index).into(); + let ptr_type = self.pointer_type(); - match callee_index.local_or_import(self.env.module) { - LocalOrImport::Local(_) => { + match callee_index.local_or_import(&self.env.module.info) { + LocalOrImport::Local(local_function_index) => { // this is an internal function let vmctx = pos .func @@ -485,13 +494,31 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> { .expect("missing vmctx parameter"); let mut args = Vec::with_capacity(call_args.len() + 1); - args.extend(call_args.iter().cloned()); args.push(vmctx); + args.extend(call_args.iter().cloned()); - Ok(pos.ins().call(callee, &args)) + let sig_ref = pos.func.dfg.ext_funcs[callee].signature; + let function_ptr = { + let mflags = ir::MemFlags::trusted(); + + let function_array_ptr = pos.ins().load( + ptr_type, + mflags, + vmctx, + vm::Ctx::offset_local_functions() as i32, + ); + + pos.ins().load( + ptr_type, + mflags, + function_array_ptr, + (local_function_index.index() as i32) * 8, + ) + }; + + Ok(pos.ins().call_indirect(sig_ref, function_ptr, &args)) } LocalOrImport::Import(imported_func_index) => { - let ptr_type = self.pointer_type(); // this is an imported function let vmctx = pos.func.create_global_value(ir::GlobalValueData::VMContext); @@ -532,8 +559,8 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> { let sig_ref = pos.func.dfg.ext_funcs[callee].signature; let mut args = Vec::with_capacity(call_args.len() + 1); - args.extend(call_args.iter().cloned()); args.push(imported_vmctx_addr); + args.extend(call_args.iter().cloned()); Ok(pos .ins() @@ -559,27 +586,28 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> { let signature = pos.func.import_signature(ir::Signature { call_conv: self.target_config().default_call_conv, params: vec![ - ir::AbiParam::new(ir::types::I32), - ir::AbiParam::new(ir::types::I32), ir::AbiParam::special(self.pointer_type(), ir::ArgumentPurpose::VMContext), + ir::AbiParam::new(ir::types::I32), + ir::AbiParam::new(ir::types::I32), ], returns: vec![ir::AbiParam::new(ir::types::I32)], }); let mem_index: MemoryIndex = Converter(clif_mem_index).into(); - let (namespace, mem_index, description) = match mem_index.local_or_import(self.env.module) { - LocalOrImport::Local(local_mem_index) => ( - call_names::LOCAL_NAMESPACE, - local_mem_index.index(), - self.env.module.info.memories[local_mem_index], - ), - LocalOrImport::Import(import_mem_index) => ( - call_names::IMPORT_NAMESPACE, - import_mem_index.index(), - self.env.module.info.imported_memories[import_mem_index].1, - ), - }; + let (namespace, mem_index, description) = + match mem_index.local_or_import(&self.env.module.info) { + LocalOrImport::Local(local_mem_index) => ( + call_names::LOCAL_NAMESPACE, + local_mem_index.index(), + self.env.module.info.memories[local_mem_index], + ), + LocalOrImport::Import(import_mem_index) => ( + call_names::IMPORT_NAMESPACE, + import_mem_index.index(), + self.env.module.info.imported_memories[import_mem_index].1, + ), + }; let name_index = match description.memory_type() { MemoryType::Dynamic => call_names::DYNAMIC_MEM_GROW, @@ -604,7 +632,7 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> { let call_inst = pos .ins() - .call(mem_grow_func, &[const_mem_index, by_value, vmctx]); + .call(mem_grow_func, &[vmctx, const_mem_index, by_value]); Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap()) } @@ -623,26 +651,27 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> { let signature = pos.func.import_signature(ir::Signature { call_conv: self.target_config().default_call_conv, params: vec![ - ir::AbiParam::new(ir::types::I32), ir::AbiParam::special(self.pointer_type(), ir::ArgumentPurpose::VMContext), + ir::AbiParam::new(ir::types::I32), ], returns: vec![ir::AbiParam::new(ir::types::I32)], }); let mem_index: MemoryIndex = Converter(clif_mem_index).into(); - let (namespace, mem_index, description) = match mem_index.local_or_import(self.env.module) { - LocalOrImport::Local(local_mem_index) => ( - call_names::LOCAL_NAMESPACE, - local_mem_index.index(), - self.env.module.info.memories[local_mem_index], - ), - LocalOrImport::Import(import_mem_index) => ( - call_names::IMPORT_NAMESPACE, - import_mem_index.index(), - self.env.module.info.imported_memories[import_mem_index].1, - ), - }; + let (namespace, mem_index, description) = + match mem_index.local_or_import(&self.env.module.info) { + LocalOrImport::Local(local_mem_index) => ( + call_names::LOCAL_NAMESPACE, + local_mem_index.index(), + self.env.module.info.memories[local_mem_index], + ), + LocalOrImport::Import(import_mem_index) => ( + call_names::IMPORT_NAMESPACE, + import_mem_index.index(), + self.env.module.info.imported_memories[import_mem_index].1, + ), + }; let name_index = match description.memory_type() { MemoryType::Dynamic => call_names::DYNAMIC_MEM_SIZE, @@ -664,7 +693,7 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> { .special_param(ir::ArgumentPurpose::VMContext) .expect("missing vmctx parameter"); - let call_inst = pos.ins().call(mem_grow_func, &[const_mem_index, vmctx]); + let call_inst = pos.ins().call(mem_grow_func, &[vmctx, const_mem_index]); Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap()) } diff --git a/lib/clif-backend/src/lib.rs b/lib/clif-backend/src/lib.rs index 07bae97c6..88de51e12 100644 --- a/lib/clif-backend/src/lib.rs +++ b/lib/clif-backend/src/lib.rs @@ -1,4 +1,3 @@ -#[cfg(feature = "cache")] mod cache; mod func_env; mod libcalls; @@ -14,21 +13,18 @@ use cranelift_codegen::{ settings::{self, Configurable}, }; use target_lexicon::Triple; -#[cfg(feature = "cache")] -use wasmer_runtime_core::{ - backend::sys::Memory, - cache::{Cache, Error as CacheError}, - module::ModuleInfo, -}; + +use wasmer_runtime_core::cache::{Artifact, Error as CacheError}; use wasmer_runtime_core::{ backend::{Compiler, Token}, error::{CompileError, CompileResult}, module::ModuleInner, }; -#[cfg(feature = "cache")] + #[macro_use] extern crate serde_derive; -#[cfg(feature = "cache")] + +extern crate rayon; extern crate serde; use wasmparser::{self, WasmDecoder}; @@ -48,7 +44,7 @@ impl Compiler for CraneliftCompiler { let isa = get_isa(); - let mut module = module::Module::empty(); + let mut module = module::Module::new(); let module_env = module_env::ModuleEnv::new(&mut module, &*isa); let func_bodies = module_env.translate(wasm)?; @@ -57,41 +53,41 @@ impl Compiler for CraneliftCompiler { } /// Create a wasmer Module from an already-compiled cache. - #[cfg(feature = "cache")] - unsafe fn from_cache(&self, cache: Cache, _: Token) -> Result { + + unsafe fn from_cache(&self, cache: Artifact, _: Token) -> Result { module::Module::from_cache(cache) } - #[cfg(feature = "cache")] - fn compile_to_backend_cache_data( - &self, - wasm: &[u8], - _: Token, - ) -> CompileResult<(Box, Vec, Memory)> { - validate(wasm)?; + // + // fn compile_to_backend_cache_data( + // &self, + // wasm: &[u8], + // _: Token, + // ) -> CompileResult<(Box, Vec, Memory)> { + // validate(wasm)?; - let isa = get_isa(); + // let isa = get_isa(); - let mut module = module::Module::empty(); - let module_env = module_env::ModuleEnv::new(&mut module, &*isa); + // let mut module = module::Module::new(wasm); + // let module_env = module_env::ModuleEnv::new(&mut module, &*isa); - let func_bodies = module_env.translate(wasm)?; + // let func_bodies = module_env.translate(wasm)?; - let (info, backend_cache, compiled_code) = module - .compile_to_backend_cache(&*isa, func_bodies) - .map_err(|e| CompileError::InternalError { - msg: format!("{:?}", e), - })?; + // let (info, backend_cache, compiled_code) = module + // .compile_to_backend_cache(&*isa, func_bodies) + // .map_err(|e| CompileError::InternalError { + // msg: format!("{:?}", e), + // })?; - let buffer = - backend_cache - .into_backend_data() - .map_err(|e| CompileError::InternalError { - msg: format!("{:?}", e), - })?; + // let buffer = + // backend_cache + // .into_backend_data() + // .map_err(|e| CompileError::InternalError { + // msg: format!("{:?}", e), + // })?; - Ok((Box::new(info), buffer, compiled_code)) - } + // Ok((Box::new(info), buffer, compiled_code)) + // } } fn get_isa() -> Box { diff --git a/lib/clif-backend/src/module.rs b/lib/clif-backend/src/module.rs index c48072ac8..9e934ee0a 100644 --- a/lib/clif-backend/src/module.rs +++ b/lib/clif-backend/src/module.rs @@ -1,178 +1,122 @@ -#[cfg(feature = "cache")] -use crate::cache::BackendCache; +use crate::cache::{BackendCache, CacheGenerator}; use crate::{resolver::FuncResolverBuilder, signal::Caller, trampoline::Trampolines}; use cranelift_codegen::{ir, isa}; use cranelift_entity::EntityRef; use cranelift_wasm; use hashbrown::HashMap; -use std::{ - ops::{Deref, DerefMut}, - ptr::NonNull, -}; -#[cfg(feature = "cache")] +use std::sync::Arc; + +use wasmer_runtime_core::cache::{Artifact, Error as CacheError}; + use wasmer_runtime_core::{ - backend::sys::Memory, - cache::{Cache, Error as CacheError}, -}; -use wasmer_runtime_core::{ - backend::{Backend, FuncResolver, ProtectedCaller, Token, UserTrapper}, - error::{CompileResult, RuntimeResult}, + backend::Backend, + error::CompileResult, module::{ModuleInfo, ModuleInner, StringTable}, structures::{Map, TypedIndex}, types::{ FuncIndex, FuncSig, GlobalIndex, LocalFuncIndex, MemoryIndex, SigIndex, TableIndex, Type, - Value, }, - vm::{self, ImportBacking}, }; -struct Placeholder; - -impl FuncResolver for Placeholder { - fn get( - &self, - _module: &ModuleInner, - _local_func_index: LocalFuncIndex, - ) -> Option> { - None - } -} - -impl ProtectedCaller for Placeholder { - fn call( - &self, - _module: &ModuleInner, - _func_index: FuncIndex, - _params: &[Value], - _import_backing: &ImportBacking, - _vmctx: *mut vm::Ctx, - _: Token, - ) -> RuntimeResult> { - Ok(vec![]) - } - - fn get_early_trapper(&self) -> Box { - unimplemented!() - } -} - /// This contains all of the items in a `ModuleInner` except the `func_resolver`. pub struct Module { - pub module: ModuleInner, + pub info: ModuleInfo, } impl Module { - pub fn empty() -> Self { + pub fn new() -> Self { Self { - module: ModuleInner { - // this is a placeholder - func_resolver: Box::new(Placeholder), - protected_caller: Box::new(Placeholder), + info: ModuleInfo { + memories: Map::new(), + globals: Map::new(), + tables: Map::new(), - info: ModuleInfo { - memories: Map::new(), - globals: Map::new(), - tables: Map::new(), + imported_functions: Map::new(), + imported_memories: Map::new(), + imported_tables: Map::new(), + imported_globals: Map::new(), - imported_functions: Map::new(), - imported_memories: Map::new(), - imported_tables: Map::new(), - imported_globals: Map::new(), + exports: HashMap::new(), - exports: HashMap::new(), + data_initializers: Vec::new(), + elem_initializers: Vec::new(), - data_initializers: Vec::new(), - elem_initializers: Vec::new(), + start_func: None, - start_func: None, + func_assoc: Map::new(), + signatures: Map::new(), + backend: Backend::Cranelift, - func_assoc: Map::new(), - signatures: Map::new(), - backend: Backend::Cranelift, - - namespace_table: StringTable::new(), - name_table: StringTable::new(), - }, + namespace_table: StringTable::new(), + name_table: StringTable::new(), }, } } pub fn compile( - mut self, + self, isa: &isa::TargetIsa, functions: Map, ) -> CompileResult { let (func_resolver_builder, handler_data) = - FuncResolverBuilder::new(isa, functions, &self.module.info)?; + FuncResolverBuilder::new(isa, functions, &self.info)?; - self.module.func_resolver = - Box::new(func_resolver_builder.finalize(&self.module.info.signatures)?); + let trampolines = Arc::new(Trampolines::new(isa, &self.info)); - let trampolines = Trampolines::new(isa, &self.module.info); + let (func_resolver, backend_cache) = func_resolver_builder.finalize( + &self.info.signatures, + Arc::clone(&trampolines), + handler_data.clone(), + )?; - self.module.protected_caller = - Box::new(Caller::new(&self.module.info, handler_data, trampolines)); + let protected_caller = Caller::new(&self.info, handler_data, trampolines); - Ok(self.module) + let cache_gen = Box::new(CacheGenerator::new( + backend_cache, + Arc::clone(&func_resolver.memory), + )); + + Ok(ModuleInner { + func_resolver: Box::new(func_resolver), + protected_caller: Box::new(protected_caller), + cache_gen, + + info: self.info, + }) } - #[cfg(feature = "cache")] - pub fn compile_to_backend_cache( - self, - isa: &isa::TargetIsa, - functions: Map, - ) -> CompileResult<(ModuleInfo, BackendCache, Memory)> { - let (func_resolver_builder, handler_data) = - FuncResolverBuilder::new(isa, functions, &self.module.info)?; - - let trampolines = Trampolines::new(isa, &self.module.info); - - let trampoline_cache = trampolines.to_trampoline_cache(); - - let (backend_cache, compiled_code) = - func_resolver_builder.to_backend_cache(trampoline_cache, handler_data); - - Ok((self.module.info, backend_cache, compiled_code)) - } - - #[cfg(feature = "cache")] - pub fn from_cache(cache: Cache) -> Result { + pub fn from_cache(cache: Artifact) -> Result { let (info, compiled_code, backend_cache) = BackendCache::from_cache(cache)?; let (func_resolver_builder, trampolines, handler_data) = FuncResolverBuilder::new_from_backend_cache(backend_cache, compiled_code, &info)?; - let func_resolver = Box::new( - func_resolver_builder - .finalize(&info.signatures) - .map_err(|e| CacheError::Unknown(format!("{:?}", e)))?, - ); + let (func_resolver, backend_cache) = func_resolver_builder + .finalize( + &info.signatures, + Arc::clone(&trampolines), + handler_data.clone(), + ) + .map_err(|e| CacheError::Unknown(format!("{:?}", e)))?; - let protected_caller = Box::new(Caller::new(&info, handler_data, trampolines)); + let protected_caller = Caller::new(&info, handler_data, trampolines); + + let cache_gen = Box::new(CacheGenerator::new( + backend_cache, + Arc::clone(&func_resolver.memory), + )); Ok(ModuleInner { - func_resolver, - protected_caller, + func_resolver: Box::new(func_resolver), + protected_caller: Box::new(protected_caller), + cache_gen, + info, }) } } -impl Deref for Module { - type Target = ModuleInner; - - fn deref(&self) -> &ModuleInner { - &self.module - } -} - -impl DerefMut for Module { - fn deref_mut(&mut self) -> &mut ModuleInner { - &mut self.module - } -} - pub struct Converter(pub T); macro_rules! convert_clif_to_runtime_index { diff --git a/lib/clif-backend/src/module_env.rs b/lib/clif-backend/src/module_env.rs index 8a26b2731..7c6cd6731 100644 --- a/lib/clif-backend/src/module_env.rs +++ b/lib/clif-backend/src/module_env.rs @@ -4,7 +4,6 @@ use crate::{ }; use cranelift_codegen::{ir, isa}; use cranelift_wasm::{self, translate_module, FuncTranslator, ModuleEnvironment}; -use std::sync::Arc; use wasmer_runtime_core::{ error::{CompileError, CompileResult}, module::{ @@ -62,10 +61,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa> /// Declares a function signature to the environment. fn declare_signature(&mut self, sig: &ir::Signature) { self.signatures.push(sig.clone()); - self.module - .info - .signatures - .push(Arc::new(Converter(sig).into())); + self.module.info.signatures.push(Converter(sig).into()); } /// Return the signature with the given index. @@ -139,7 +135,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa> // assert!(!desc.mutable); let global_index: GlobalIndex = Converter(global_index).into(); let imported_global_index = global_index - .local_or_import(self.module) + .local_or_import(&self.module.info) .import() .expect("invalid global initializer when declaring an imported global"); Initializer::GetGlobal(imported_global_index) @@ -249,7 +245,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa> let base = match base { Some(global_index) => { let global_index: GlobalIndex = Converter(global_index).into(); - Initializer::GetGlobal(match global_index.local_or_import(self.module) { + Initializer::GetGlobal(match global_index.local_or_import(&self.module.info) { LocalOrImport::Import(imported_global_index) => imported_global_index, LocalOrImport::Local(_) => { panic!("invalid global initializer when declaring an imported global") @@ -319,7 +315,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa> let base = match base { Some(global_index) => { let global_index: GlobalIndex = Converter(global_index).into(); - Initializer::GetGlobal(match global_index.local_or_import(self.module) { + Initializer::GetGlobal(match global_index.local_or_import(&self.module.info) { LocalOrImport::Import(imported_global_index) => imported_global_index, LocalOrImport::Local(_) => { panic!("invalid global initializer when declaring an imported global") @@ -389,7 +385,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa> let name = ir::ExternalName::user(0, func_index.index() as u32); let sig = func_env.generate_signature( - self.get_func_type(Converter(func_index.convert_up(self.module)).into()), + self.get_func_type(Converter(func_index.convert_up(&self.module.info)).into()), ); let mut func = ir::Function::with_name_signature(name, sig); @@ -419,8 +415,8 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa> let signature = pos.func.import_signature(ir::Signature { call_conv: self.target_config().default_call_conv, params: vec![ - ir::AbiParam::new(ir::types::I32), ir::AbiParam::special(ir::types::I64, ir::ArgumentPurpose::VMContext), + ir::AbiParam::new(ir::types::I32), ], returns: vec![], }); @@ -457,8 +453,8 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa> let signature = pos.func.import_signature(ir::Signature { call_conv: self.target_config().default_call_conv, params: vec![ - ir::AbiParam::new(ir::types::I32), ir::AbiParam::special(ir::types::I64, ir::ArgumentPurpose::VMContext), + ir::AbiParam::new(ir::types::I32), ], returns: vec![], }); @@ -476,8 +472,8 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa> let signature = pos.func.import_signature(ir::Signature { call_conv: self.target_config().default_call_conv, params: vec![ - ir::AbiParam::new(ir::types::I64), ir::AbiParam::special(ir::types::I64, ir::ArgumentPurpose::VMContext), + ir::AbiParam::new(ir::types::I64), ], returns: vec![], }); @@ -495,8 +491,8 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa> let signature = pos.func.import_signature(ir::Signature { call_conv: self.target_config().default_call_conv, params: vec![ - ir::AbiParam::new(ir::types::F32), ir::AbiParam::special(ir::types::I64, ir::ArgumentPurpose::VMContext), + ir::AbiParam::new(ir::types::F32), ], returns: vec![], }); @@ -514,8 +510,8 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa> let signature = pos.func.import_signature(ir::Signature { call_conv: self.target_config().default_call_conv, params: vec![ - ir::AbiParam::new(ir::types::F64), ir::AbiParam::special(ir::types::I64, ir::ArgumentPurpose::VMContext), + ir::AbiParam::new(ir::types::F64), ], returns: vec![], }); @@ -536,14 +532,14 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa> let func_index = pos.ins().iconst(ir::types::I32, func_index.index() as i64); - pos.ins().call(start_debug, &[func_index, vmctx]); + pos.ins().call(start_debug, &[vmctx, func_index]); for param in new_ebb_params.iter().cloned() { match pos.func.dfg.value_type(param) { - ir::types::I32 => pos.ins().call(i32_print, &[param, vmctx]), - ir::types::I64 => pos.ins().call(i64_print, &[param, vmctx]), - ir::types::F32 => pos.ins().call(f32_print, &[param, vmctx]), - ir::types::F64 => pos.ins().call(f64_print, &[param, vmctx]), + ir::types::I32 => pos.ins().call(i32_print, &[vmctx, param]), + ir::types::I64 => pos.ins().call(i64_print, &[vmctx, param]), + ir::types::F32 => pos.ins().call(f32_print, &[vmctx, param]), + ir::types::F64 => pos.ins().call(f64_print, &[vmctx, param]), _ => unimplemented!(), }; } diff --git a/lib/clif-backend/src/relocation.rs b/lib/clif-backend/src/relocation.rs index 840255e61..92ef0485a 100644 --- a/lib/clif-backend/src/relocation.rs +++ b/lib/clif-backend/src/relocation.rs @@ -22,16 +22,14 @@ pub mod call_names { pub const DYNAMIC_MEM_SIZE: u32 = 5; } -#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)] pub enum Reloc { Abs8, X86PCRel4, X86CallPCRel4, } -#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] -#[derive(Debug, Copy, Clone)] +#[derive(Serialize, Deserialize, Debug, Copy, Clone)] pub enum LibCall { Probestack, CeilF32, @@ -44,8 +42,7 @@ pub enum LibCall { NearestF64, } -#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] -#[derive(Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct ExternalRelocation { /// The relocation code. pub reloc: Reloc, @@ -66,8 +63,7 @@ pub struct LocalRelocation { pub target: FuncIndex, } -#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, Copy)] +#[derive(Serialize, Deserialize, Debug, Clone, Copy)] pub enum VmCallKind { StaticMemoryGrow, StaticMemorySize, @@ -79,16 +75,14 @@ pub enum VmCallKind { DynamicMemorySize, } -#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, Copy)] +#[derive(Serialize, Deserialize, Debug, Clone, Copy)] pub enum VmCall { Local(VmCallKind), Import(VmCallKind), } /// Specify the type of relocation -#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] -#[derive(Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub enum RelocationType { Intrinsic(String), LibCall(LibCall), @@ -218,8 +212,7 @@ impl binemit::RelocSink for RelocSink { } } -#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, Copy)] +#[derive(Serialize, Deserialize, Debug, Clone, Copy)] pub enum TrapCode { StackOverflow, HeapOutOfBounds, @@ -244,8 +237,7 @@ impl RelocSink { } } -#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, Copy)] +#[derive(Serialize, Deserialize, Debug, Clone, Copy)] pub struct TrapData { pub trapcode: TrapCode, pub srcloc: u32, @@ -253,7 +245,7 @@ pub struct TrapData { /// Simple implementation of a TrapSink /// that saves the info for later. -#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] +#[derive(Serialize, Deserialize)] pub struct TrapSink { trap_datas: Vec<(usize, TrapData)>, } diff --git a/lib/clif-backend/src/resolver.rs b/lib/clif-backend/src/resolver.rs index 1e62c0c20..94713e0b2 100644 --- a/lib/clif-backend/src/resolver.rs +++ b/lib/clif-backend/src/resolver.rs @@ -1,8 +1,4 @@ -#[cfg(feature = "cache")] -use crate::{ - cache::{BackendCache, TrampolineCache}, - trampoline::Trampolines, -}; +use crate::{cache::BackendCache, trampoline::Trampolines}; use crate::{ libcalls, relocation::{ @@ -11,6 +7,7 @@ use crate::{ }, signal::HandlerData, }; +use rayon::prelude::*; use byteorder::{ByteOrder, LittleEndian}; use cranelift_codegen::{ir, isa, Context}; @@ -19,7 +16,7 @@ use std::{ ptr::{write_unaligned, NonNull}, sync::Arc, }; -#[cfg(feature = "cache")] + use wasmer_runtime_core::cache::Error as CacheError; use wasmer_runtime_core::{ self, @@ -42,21 +39,32 @@ extern "C" { pub fn __chkstk(); } +fn lookup_func( + map: &SliceMap, + memory: &Memory, + local_func_index: LocalFuncIndex, +) -> Option> { + let offset = *map.get(local_func_index)?; + let ptr = unsafe { memory.as_ptr().add(offset) }; + + NonNull::new(ptr).map(|nonnull| nonnull.cast()) +} + #[allow(dead_code)] pub struct FuncResolverBuilder { - resolver: FuncResolver, + map: Map, + memory: Memory, local_relocs: Map>, external_relocs: Map>, import_len: usize, } impl FuncResolverBuilder { - #[cfg(feature = "cache")] pub fn new_from_backend_cache( backend_cache: BackendCache, mut code: Memory, info: &ModuleInfo, - ) -> Result<(Self, Trampolines, HandlerData), CacheError> { + ) -> Result<(Self, Arc, HandlerData), CacheError> { unsafe { code.protect(.., Protect::ReadWrite) .map_err(|e| CacheError::Unknown(e.to_string()))?; @@ -67,61 +75,62 @@ impl FuncResolverBuilder { Ok(( Self { - resolver: FuncResolver { - map: backend_cache.offsets, - memory: code, - }, + map: backend_cache.offsets, + memory: code, local_relocs: Map::new(), external_relocs: backend_cache.external_relocs, import_len: info.imported_functions.len(), }, - Trampolines::from_trampoline_cache(backend_cache.trampolines), + Arc::new(Trampolines::from_trampoline_cache( + backend_cache.trampolines, + )), handler_data, )) } - #[cfg(feature = "cache")] - pub fn to_backend_cache( - mut self, - trampolines: TrampolineCache, - handler_data: HandlerData, - ) -> (BackendCache, Memory) { - self.relocate_locals(); - ( - BackendCache { - external_relocs: self.external_relocs, - offsets: self.resolver.map, - trap_sink: handler_data.trap_data, - trampolines, - }, - self.resolver.memory, - ) - } - pub fn new( isa: &isa::TargetIsa, function_bodies: Map, info: &ModuleInfo, ) -> CompileResult<(Self, HandlerData)> { - let mut compiled_functions: Vec> = Vec::with_capacity(function_bodies.len()); - let mut local_relocs = Map::with_capacity(function_bodies.len()); - let mut external_relocs = Map::new(); + let num_func_bodies = function_bodies.len(); + let mut local_relocs = Map::with_capacity(num_func_bodies); + let mut external_relocs = Map::with_capacity(num_func_bodies); let mut trap_sink = TrapSink::new(); - let mut local_trap_sink = LocalTrapSink::new(); - let mut ctx = Context::new(); + let compiled_functions: Result, (RelocSink, LocalTrapSink))>, CompileError> = + function_bodies + .into_vec() + .par_iter() + .map_init( + || Context::new(), + |ctx, func| { + let mut code_buf = Vec::new(); + ctx.func = func.to_owned(); + let mut reloc_sink = RelocSink::new(); + let mut local_trap_sink = LocalTrapSink::new(); + + ctx.compile_and_emit( + isa, + &mut code_buf, + &mut reloc_sink, + &mut local_trap_sink, + ) + .map_err(|e| CompileError::InternalError { msg: e.to_string() })?; + ctx.clear(); + Ok((code_buf, (reloc_sink, local_trap_sink))) + }, + ) + .collect(); + + let compiled_functions = compiled_functions?; let mut total_size = 0; - - for (_, func) in function_bodies { - ctx.func = func; - let mut code_buf = Vec::new(); - let mut reloc_sink = RelocSink::new(); - - ctx.compile_and_emit(isa, &mut code_buf, &mut reloc_sink, &mut local_trap_sink) - .map_err(|e| CompileError::InternalError { msg: e.to_string() })?; - ctx.clear(); - + // We separate into two iterators, one iterable and one into iterable + let (code_bufs, sinks): (Vec>, Vec<(RelocSink, LocalTrapSink)>) = + compiled_functions.into_iter().unzip(); + for (code_buf, (reloc_sink, mut local_trap_sink)) in code_bufs.iter().zip(sinks.into_iter()) + { // Clear the local trap sink and consolidate all trap info // into a single location. trap_sink.drain_local(total_size, &mut local_trap_sink); @@ -129,7 +138,6 @@ impl FuncResolverBuilder { // Round up each function's size to pointer alignment. total_size += round_up(code_buf.len(), mem::size_of::()); - compiled_functions.push(code_buf); local_relocs.push(reloc_sink.local_relocs.into_boxed_slice()); external_relocs.push(reloc_sink.external_relocs.into_boxed_slice()); } @@ -156,10 +164,10 @@ impl FuncResolverBuilder { *i = 0xCC; } - let mut map = Map::with_capacity(compiled_functions.len()); + let mut map = Map::with_capacity(num_func_bodies); let mut previous_end = 0; - for compiled in compiled_functions.iter() { + for compiled in code_bufs.iter() { let new_end = previous_end + round_up(compiled.len(), mem::size_of::()); unsafe { memory.as_slice_mut()[previous_end..previous_end + compiled.len()] @@ -169,10 +177,12 @@ impl FuncResolverBuilder { previous_end = new_end; } - let handler_data = HandlerData::new(trap_sink, memory.as_ptr() as _, memory.size()); + let handler_data = + HandlerData::new(Arc::new(trap_sink), memory.as_ptr() as _, memory.size()); let mut func_resolver_builder = Self { - resolver: FuncResolver { map, memory }, + map, + memory, local_relocs, external_relocs, import_len: info.imported_functions.len(), @@ -187,12 +197,15 @@ impl FuncResolverBuilder { for (index, relocs) in self.local_relocs.iter() { for ref reloc in relocs.iter() { let local_func_index = LocalFuncIndex::new(reloc.target.index() - self.import_len); - let target_func_address = - self.resolver.lookup(local_func_index).unwrap().as_ptr() as usize; + let target_func_address = lookup_func(&self.map, &self.memory, local_func_index) + .unwrap() + .as_ptr() as usize; // We need the address of the current function // because these calls are relative. - let func_addr = self.resolver.lookup(index).unwrap().as_ptr() as usize; + let func_addr = lookup_func(&self.map, &self.memory, index) + .unwrap() + .as_ptr() as usize; unsafe { let reloc_address = func_addr + reloc.offset as usize; @@ -208,8 +221,10 @@ impl FuncResolverBuilder { pub fn finalize( mut self, - signatures: &SliceMap>, - ) -> CompileResult { + signatures: &SliceMap, + trampolines: Arc, + handler_data: HandlerData, + ) -> CompileResult<(FuncResolver, BackendCache)> { for (index, relocs) in self.external_relocs.iter() { for ref reloc in relocs.iter() { let target_func_address: isize = match reloc.target { @@ -223,9 +238,9 @@ impl FuncResolverBuilder { LibCall::TruncF64 => libcalls::truncf64 as isize, LibCall::NearestF64 => libcalls::nearbyintf64 as isize, #[cfg(all(target_pointer_width = "64", target_os = "windows"))] - Probestack => __chkstk as isize, + LibCall::Probestack => __chkstk as isize, #[cfg(not(target_os = "windows"))] - Probestack => __rust_probestack as isize, + LibCall::Probestack => __rust_probestack as isize, }, RelocationType::Intrinsic(ref name) => match name.as_str() { "i32print" => i32_print as isize, @@ -273,15 +288,17 @@ impl FuncResolverBuilder { }, }, RelocationType::Signature(sig_index) => { - let sig_index = - SigRegistry.lookup_sig_index(Arc::clone(&signatures[sig_index])); + let signature = SigRegistry.lookup_signature_ref(&signatures[sig_index]); + let sig_index = SigRegistry.lookup_sig_index(signature); sig_index.index() as _ } }; // We need the address of the current function // because some of these calls are relative. - let func_addr = self.resolver.lookup(index).unwrap().as_ptr(); + let func_addr = lookup_func(&self.map, &self.memory, index) + .unwrap() + .as_ptr() as usize; // Determine relocation type and apply relocation. match reloc.reloc { @@ -289,9 +306,9 @@ impl FuncResolverBuilder { let ptr_to_write = (target_func_address as u64) .checked_add(reloc.addend as u64) .unwrap(); - let empty_space_offset = self.resolver.map[index] + reloc.offset as usize; + let empty_space_offset = self.map[index] + reloc.offset as usize; let ptr_slice = unsafe { - &mut self.resolver.memory.as_slice_mut() + &mut self.memory.as_slice_mut() [empty_space_offset..empty_space_offset + 8] }; LittleEndian::write_u64(ptr_slice, ptr_to_write); @@ -309,29 +326,35 @@ impl FuncResolverBuilder { } unsafe { - self.resolver - .memory + self.memory .protect(.., Protect::ReadExec) .map_err(|e| CompileError::InternalError { msg: e.to_string() })?; } - Ok(self.resolver) + let backend_cache = BackendCache { + external_relocs: self.external_relocs.clone(), + offsets: self.map.clone(), + trap_sink: handler_data.trap_data, + trampolines: trampolines.to_trampoline_cache(), + }; + + Ok(( + FuncResolver { + map: self.map, + memory: Arc::new(self.memory), + }, + backend_cache, + )) } } +unsafe impl Sync for FuncResolver {} +unsafe impl Send for FuncResolver {} + /// Resolves a function index to a function address. pub struct FuncResolver { map: Map, - memory: Memory, -} - -impl FuncResolver { - fn lookup(&self, local_func_index: LocalFuncIndex) -> Option> { - let offset = *self.map.get(local_func_index)?; - let ptr = unsafe { self.memory.as_ptr().add(offset) }; - - NonNull::new(ptr).map(|nonnull| nonnull.cast()) - } + pub(crate) memory: Arc, } // Implements FuncResolver trait. @@ -341,7 +364,7 @@ impl backend::FuncResolver for FuncResolver { _module: &wasmer_runtime_core::module::ModuleInner, index: LocalFuncIndex, ) -> Option> { - self.lookup(index) + lookup_func(&self.map, &self.memory, index) } } @@ -350,21 +373,21 @@ fn round_up(n: usize, multiple: usize) -> usize { (n + multiple - 1) & !(multiple - 1) } -extern "C" fn i32_print(n: i32) { +extern "C" fn i32_print(_ctx: &mut vm::Ctx, n: i32) { print!(" i32: {},", n); } -extern "C" fn i64_print(n: i64) { +extern "C" fn i64_print(_ctx: &mut vm::Ctx, n: i64) { print!(" i64: {},", n); } -extern "C" fn f32_print(n: f32) { +extern "C" fn f32_print(_ctx: &mut vm::Ctx, n: f32) { print!(" f32: {},", n); } -extern "C" fn f64_print(n: f64) { +extern "C" fn f64_print(_ctx: &mut vm::Ctx, n: f64) { print!(" f64: {},", n); } -extern "C" fn start_debug(func_index: u32) { +extern "C" fn start_debug(_ctx: &mut vm::Ctx, func_index: u32) { print!("func ({}), args: [", func_index); } -extern "C" fn end_debug() { +extern "C" fn end_debug(_ctx: &mut vm::Ctx) { println!(" ]"); } diff --git a/lib/clif-backend/src/signal/mod.rs b/lib/clif-backend/src/signal/mod.rs index eab9e62e7..04b97e832 100644 --- a/lib/clif-backend/src/signal/mod.rs +++ b/lib/clif-backend/src/signal/mod.rs @@ -2,7 +2,7 @@ use crate::relocation::{TrapData, TrapSink}; use crate::trampoline::Trampolines; use hashbrown::HashSet; use libc::c_void; -use std::{cell::Cell, sync::Arc}; +use std::{any::Any, cell::Cell, sync::Arc}; use wasmer_runtime_core::{ backend::{ProtectedCaller, Token, UserTrapper}, error::RuntimeResult, @@ -25,14 +25,14 @@ pub use self::unix::*; pub use self::windows::*; thread_local! { - pub static TRAP_EARLY_DATA: Cell> = Cell::new(None); + pub static TRAP_EARLY_DATA: Cell>> = Cell::new(None); } pub struct Trapper; impl UserTrapper for Trapper { - unsafe fn do_early_trap(&self, msg: String) -> ! { - TRAP_EARLY_DATA.with(|cell| cell.set(Some(msg))); + unsafe fn do_early_trap(&self, data: Box) -> ! { + TRAP_EARLY_DATA.with(|cell| cell.set(Some(data))); trigger_trap() } } @@ -40,11 +40,15 @@ impl UserTrapper for Trapper { pub struct Caller { func_export_set: HashSet, handler_data: HandlerData, - trampolines: Trampolines, + trampolines: Arc, } impl Caller { - pub fn new(module: &ModuleInfo, handler_data: HandlerData, trampolines: Trampolines) -> Self { + pub fn new( + module: &ModuleInfo, + handler_data: HandlerData, + trampolines: Arc, + ) -> Self { let mut func_export_set = HashSet::new(); for export_index in module.exports.values() { if let ExportIndex::Func(func_index) = export_index { @@ -110,6 +114,7 @@ impl ProtectedCaller for Caller { .lookup(sig_index) .expect("that trampoline doesn't exist"); + #[cfg(not(target_os = "windows"))] call_protected(&self.handler_data, || unsafe { // Leap of faith. trampoline( @@ -120,6 +125,17 @@ impl ProtectedCaller for Caller { ); })?; + // the trampoline is called from C on windows + #[cfg(target_os = "windows")] + call_protected( + &self.handler_data, + trampoline, + vmctx_ptr, + func_ptr, + param_vec.as_ptr(), + return_vec.as_mut_ptr(), + )?; + Ok(return_vec .iter() .zip(signature.returns().iter()) @@ -137,18 +153,18 @@ impl ProtectedCaller for Caller { } } -fn get_func_from_index( - module: &ModuleInner, +fn get_func_from_index<'a>( + module: &'a ModuleInner, import_backing: &ImportBacking, func_index: FuncIndex, -) -> (*const vm::Func, Context, Arc, SigIndex) { +) -> (*const vm::Func, Context, &'a FuncSig, SigIndex) { let sig_index = *module .info .func_assoc .get(func_index) .expect("broken invariant, incorrect func index"); - let (func_ptr, ctx) = match func_index.local_or_import(module) { + let (func_ptr, ctx) = match func_index.local_or_import(&module.info) { LocalOrImport::Local(local_func_index) => ( module .func_resolver @@ -167,7 +183,7 @@ fn get_func_from_index( } }; - let signature = Arc::clone(&module.info.signatures[sig_index]); + let signature = &module.info.signatures[sig_index]; (func_ptr, ctx, signature, sig_index) } @@ -175,15 +191,16 @@ fn get_func_from_index( unsafe impl Send for HandlerData {} unsafe impl Sync for HandlerData {} +#[derive(Clone)] pub struct HandlerData { - pub trap_data: TrapSink, + pub trap_data: Arc, exec_buffer_ptr: *const c_void, exec_buffer_size: usize, } impl HandlerData { pub fn new( - trap_data: TrapSink, + trap_data: Arc, exec_buffer_ptr: *const c_void, exec_buffer_size: usize, ) -> Self { diff --git a/lib/clif-backend/src/signal/unix.rs b/lib/clif-backend/src/signal/unix.rs index 9d2f80eca..feaf4e2e2 100644 --- a/lib/clif-backend/src/signal/unix.rs +++ b/lib/clif-backend/src/signal/unix.rs @@ -18,11 +18,7 @@ use nix::sys::signal::{ use std::cell::{Cell, UnsafeCell}; use std::ptr; use std::sync::Once; -use wasmer_runtime_core::{ - error::{RuntimeError, RuntimeResult}, - structures::TypedIndex, - types::{MemoryIndex, TableIndex}, -}; +use wasmer_runtime_core::error::{RuntimeError, RuntimeResult}; extern "C" fn signal_trap_handler( signum: ::nix::libc::c_int, @@ -79,8 +75,8 @@ pub fn call_protected(handler_data: &HandlerData, f: impl FnOnce() -> T) -> R if signum != 0 { *jmp_buf = prev_jmp_buf; - if let Some(msg) = super::TRAP_EARLY_DATA.with(|cell| cell.replace(None)) { - Err(RuntimeError::User { msg }) + if let Some(data) = super::TRAP_EARLY_DATA.with(|cell| cell.replace(None)) { + Err(RuntimeError::Panic { data }) } else { let (faulting_addr, inst_ptr) = CAUGHT_ADDRESSES.with(|cell| cell.get()); @@ -91,28 +87,28 @@ pub fn call_protected(handler_data: &HandlerData, f: impl FnOnce() -> T) -> R { Err(match Signal::from_c_int(signum) { Ok(SIGILL) => match trapcode { - TrapCode::BadSignature => RuntimeError::IndirectCallSignature { - table: TableIndex::new(0), + TrapCode::BadSignature => RuntimeError::Trap { + msg: "incorrect call_indirect signature".into(), }, - TrapCode::IndirectCallToNull => RuntimeError::IndirectCallToNull { - table: TableIndex::new(0), + TrapCode::IndirectCallToNull => RuntimeError::Trap { + msg: "indirect call to null".into(), }, - TrapCode::HeapOutOfBounds => RuntimeError::OutOfBoundsAccess { - memory: MemoryIndex::new(0), - addr: None, + TrapCode::HeapOutOfBounds => RuntimeError::Trap { + msg: "memory out-of-bounds access".into(), }, - TrapCode::TableOutOfBounds => RuntimeError::TableOutOfBounds { - table: TableIndex::new(0), + TrapCode::TableOutOfBounds => RuntimeError::Trap { + msg: "table out-of-bounds access".into(), }, - _ => RuntimeError::Unknown { - msg: "unknown trap".to_string(), + _ => RuntimeError::Trap { + msg: "unknown trap".into(), }, }, - Ok(SIGSEGV) | Ok(SIGBUS) => RuntimeError::OutOfBoundsAccess { - memory: MemoryIndex::new(0), - addr: None, + Ok(SIGSEGV) | Ok(SIGBUS) => RuntimeError::Trap { + msg: "memory out-of-bounds access".into(), + }, + Ok(SIGFPE) => RuntimeError::Trap { + msg: "illegal arithmetic operation".into(), }, - Ok(SIGFPE) => RuntimeError::IllegalArithmeticOperation, _ => unimplemented!(), } .into()) @@ -126,8 +122,8 @@ pub fn call_protected(handler_data: &HandlerData, f: impl FnOnce() -> T) -> R _ => "unkown trapped signal", }; // When the trap-handler is fully implemented, this will return more information. - Err(RuntimeError::Unknown { - msg: format!("trap at {:p} - {}", faulting_addr, signal), + Err(RuntimeError::Trap { + msg: format!("unknown trap at {:p} - {}", faulting_addr, signal).into(), } .into()) } diff --git a/lib/clif-backend/src/signal/windows.rs b/lib/clif-backend/src/signal/windows.rs index b715c616d..e5bd41994 100644 --- a/lib/clif-backend/src/signal/windows.rs +++ b/lib/clif-backend/src/signal/windows.rs @@ -1,10 +1,115 @@ +use crate::relocation::{TrapCode, TrapData}; use crate::signal::HandlerData; -use wasmer_runtime_core::error::RuntimeResult; +use crate::trampoline::Trampoline; +use std::cell::Cell; +use std::ffi::c_void; +use std::ptr; +use wasmer_runtime_core::vm::Ctx; +use wasmer_runtime_core::vm::Func; +use wasmer_runtime_core::{ + error::{RuntimeError, RuntimeResult}, + structures::TypedIndex, + types::{MemoryIndex, TableIndex}, +}; +use wasmer_win_exception_handler::CallProtectedData; +pub use wasmer_win_exception_handler::_call_protected; +use winapi::shared::minwindef::DWORD; +use winapi::um::minwinbase::{ + EXCEPTION_ACCESS_VIOLATION, EXCEPTION_FLT_DENORMAL_OPERAND, EXCEPTION_FLT_DIVIDE_BY_ZERO, + EXCEPTION_FLT_INEXACT_RESULT, EXCEPTION_FLT_INVALID_OPERATION, EXCEPTION_FLT_OVERFLOW, + EXCEPTION_FLT_STACK_CHECK, EXCEPTION_FLT_UNDERFLOW, EXCEPTION_ILLEGAL_INSTRUCTION, + EXCEPTION_INT_DIVIDE_BY_ZERO, EXCEPTION_INT_OVERFLOW, EXCEPTION_STACK_OVERFLOW, +}; -pub fn call_protected(handler_data: &HandlerData, f: impl FnOnce() -> T) -> RuntimeResult { - unimplemented!("TODO"); +thread_local! { + pub static CURRENT_EXECUTABLE_BUFFER: Cell<*const c_void> = Cell::new(ptr::null()); +} + +pub fn call_protected( + handler_data: &HandlerData, + trampoline: Trampoline, + ctx: *mut Ctx, + func: *const Func, + param_vec: *const u64, + return_vec: *mut u64, +) -> RuntimeResult<()> { + // TODO: trap early + // user code error + // if let Some(msg) = super::TRAP_EARLY_DATA.with(|cell| cell.replace(None)) { + // return Err(RuntimeError::User { msg }); + // } + + let result = _call_protected(trampoline, ctx, func, param_vec, return_vec); + + if let Ok(_) = result { + return Ok(()); + } + + let CallProtectedData { + code: signum, + exceptionAddress: exception_address, + instructionPointer: instruction_pointer, + } = result.unwrap_err(); + + if let Some(TrapData { + trapcode, + srcloc: _, + }) = handler_data.lookup(instruction_pointer as _) + { + Err(match signum as DWORD { + EXCEPTION_ACCESS_VIOLATION => RuntimeError::Trap { + msg: "memory out-of-bounds access".into(), + }, + EXCEPTION_ILLEGAL_INSTRUCTION => match trapcode { + TrapCode::BadSignature => RuntimeError::Trap { + msg: "incorrect call_indirect signature".into(), + }, + TrapCode::IndirectCallToNull => RuntimeError::Trap { + msg: "indirect call to null".into(), + }, + TrapCode::HeapOutOfBounds => RuntimeError::Trap { + msg: "memory out-of-bounds access".into(), + }, + TrapCode::TableOutOfBounds => RuntimeError::Trap { + msg: "table out-of-bounds access".into(), + }, + _ => RuntimeError::Trap { + msg: "unknown trap".into(), + }, + }, + EXCEPTION_STACK_OVERFLOW => RuntimeError::Trap { + msg: "stack overflow trap".into(), + }, + EXCEPTION_INT_DIVIDE_BY_ZERO | EXCEPTION_INT_OVERFLOW => RuntimeError::Trap { + msg: "illegal arithmetic operation".into(), + }, + _ => RuntimeError::Trap { + msg: "unknown trap".into(), + }, + } + .into()) + } else { + let signal = match signum as DWORD { + EXCEPTION_FLT_DENORMAL_OPERAND + | EXCEPTION_FLT_DIVIDE_BY_ZERO + | EXCEPTION_FLT_INEXACT_RESULT + | EXCEPTION_FLT_INVALID_OPERATION + | EXCEPTION_FLT_OVERFLOW + | EXCEPTION_FLT_STACK_CHECK + | EXCEPTION_FLT_UNDERFLOW => "floating-point exception", + EXCEPTION_ILLEGAL_INSTRUCTION => "illegal instruction", + EXCEPTION_ACCESS_VIOLATION => "segmentation violation", + _ => "unkown trapped signal", + }; + + Err(RuntimeError::Trap { + msg: format!("unknown trap at {} - {}", exception_address, signal).into(), + } + .into()) + } } pub unsafe fn trigger_trap() -> ! { - unimplemented!("TODO"); + // TODO + unimplemented!(); } diff --git a/lib/clif-backend/src/trampoline.rs b/lib/clif-backend/src/trampoline.rs index 0751cea14..b20109ef0 100644 --- a/lib/clif-backend/src/trampoline.rs +++ b/lib/clif-backend/src/trampoline.rs @@ -1,4 +1,3 @@ -#[cfg(feature = "cache")] use crate::cache::TrampolineCache; use cranelift_codegen::{ binemit::{NullTrapSink, Reloc, RelocSink}, @@ -7,6 +6,7 @@ use cranelift_codegen::{ isa, Context, }; use hashbrown::HashMap; +use std::ffi::c_void; use std::{iter, mem}; use wasmer_runtime_core::{ backend::sys::{Memory, Protect}, @@ -23,13 +23,15 @@ impl RelocSink for NullRelocSink { fn reloc_jt(&mut self, _: u32, _: Reloc, _: ir::JumpTable) {} } +pub type Trampoline = + unsafe extern "C" fn(*mut vm::Ctx, *const vm::Func, *const u64, *mut u64) -> c_void; + pub struct Trampolines { memory: Memory, offsets: HashMap, } impl Trampolines { - #[cfg(feature = "cache")] pub fn from_trampoline_cache(cache: TrampolineCache) -> Self { // pub struct TrampolineCache { // #[serde(with = "serde_bytes")] @@ -53,8 +55,7 @@ impl Trampolines { } } - #[cfg(feature = "cache")] - pub fn to_trampoline_cache(self) -> TrampolineCache { + pub fn to_trampoline_cache(&self) -> TrampolineCache { let mut code = vec![0; self.memory.size()]; unsafe { @@ -63,7 +64,7 @@ impl Trampolines { TrampolineCache { code, - offsets: self.offsets, + offsets: self.offsets.clone(), } } @@ -138,10 +139,7 @@ impl Trampolines { } } - pub fn lookup( - &self, - sig_index: SigIndex, - ) -> Option { + pub fn lookup(&self, sig_index: SigIndex) -> Option { let offset = *self.offsets.get(&sig_index)?; let ptr = unsafe { self.memory.as_ptr().add(offset) }; @@ -169,6 +167,7 @@ fn generate_func(func_sig: &FuncSig) -> ir::Function { let mut pos = FuncCursor::new(&mut func).at_first_insertion_point(entry_ebb); let mut args_vec = Vec::with_capacity(func_sig.params().len() + 1); + args_vec.push(vmctx_ptr); for (index, wasm_ty) in func_sig.params().iter().enumerate() { let mem_flags = ir::MemFlags::trusted(); @@ -180,7 +179,6 @@ fn generate_func(func_sig: &FuncSig) -> ir::Function { ); args_vec.push(val); } - args_vec.push(vmctx_ptr); let call_inst = pos.ins().call_indirect(export_sig_ref, func_ptr, &args_vec); @@ -212,7 +210,9 @@ fn wasm_ty_to_clif(ty: Type) -> ir::types::Type { } fn generate_trampoline_signature() -> ir::Signature { - let mut sig = ir::Signature::new(isa::CallConv::SystemV); + let isa = super::get_isa(); + let call_convention = isa.default_call_conv(); + let mut sig = ir::Signature::new(call_convention); let ptr_param = ir::AbiParam { value_type: ir::types::I64, @@ -227,24 +227,25 @@ fn generate_trampoline_signature() -> ir::Signature { } fn generate_export_signature(func_sig: &FuncSig) -> ir::Signature { - let mut export_clif_sig = ir::Signature::new(isa::CallConv::SystemV); + let isa = super::get_isa(); + let call_convention = isa.default_call_conv(); + let mut export_clif_sig = ir::Signature::new(call_convention); - export_clif_sig.params = func_sig - .params() - .iter() - .map(|wasm_ty| ir::AbiParam { - value_type: wasm_ty_to_clif(*wasm_ty), - purpose: ir::ArgumentPurpose::Normal, - extension: ir::ArgumentExtension::None, - location: ir::ArgumentLoc::Unassigned, - }) - .chain(iter::once(ir::AbiParam { - value_type: ir::types::I64, - purpose: ir::ArgumentPurpose::VMContext, - extension: ir::ArgumentExtension::None, - location: ir::ArgumentLoc::Unassigned, - })) - .collect(); + let func_sig_iter = func_sig.params().iter().map(|wasm_ty| ir::AbiParam { + value_type: wasm_ty_to_clif(*wasm_ty), + purpose: ir::ArgumentPurpose::Normal, + extension: ir::ArgumentExtension::None, + location: ir::ArgumentLoc::Unassigned, + }); + + export_clif_sig.params = iter::once(ir::AbiParam { + value_type: ir::types::I64, + purpose: ir::ArgumentPurpose::VMContext, + extension: ir::ArgumentExtension::None, + location: ir::ArgumentLoc::Unassigned, + }) + .chain(func_sig_iter) + .collect(); export_clif_sig.returns = func_sig .returns() diff --git a/lib/emscripten/Cargo.toml b/lib/emscripten/Cargo.toml index 36831f87d..cd691e566 100644 --- a/lib/emscripten/Cargo.toml +++ b/lib/emscripten/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-emscripten" -version = "0.1.0" +version = "0.2.1" description = "Wasmer runtime emscripten implementation library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -9,14 +9,25 @@ edition = "2018" build = "build/mod.rs" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.1.0" } -libc = { git = "https://github.com/rust-lang/libc" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.2.1" } +lazy_static = "1.2.0" +libc = "0.2.49" byteorder = "1" time = "0.1.41" +[target.'cfg(windows)'.dependencies] +rand = "0.6" + [dev-dependencies] -wasmer-clif-backend = { path = "../clif-backend", version = "0.1.0" } +wasmer-clif-backend = { path = "../clif-backend", version = "0.2.0" } wabt = "0.7.2" +[target.'cfg(not(windows))'.dev-dependencies] +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.1.0" } + [build-dependencies] glob = "0.2.11" + +[features] +clif = [] +llvm = [] \ No newline at end of file diff --git a/lib/emscripten/emtests/FS_exports.wasm b/lib/emscripten/emtests/FS_exports.wasm index 5c65988e0..255839e4d 100644 Binary files a/lib/emscripten/emtests/FS_exports.wasm and b/lib/emscripten/emtests/FS_exports.wasm differ diff --git a/lib/emscripten/emtests/clock_gettime.wasm b/lib/emscripten/emtests/clock_gettime.wasm index 1a855a75a..fd5ddd398 100644 Binary files a/lib/emscripten/emtests/clock_gettime.wasm and b/lib/emscripten/emtests/clock_gettime.wasm differ diff --git a/lib/emscripten/emtests/closebitcasts.wasm b/lib/emscripten/emtests/closebitcasts.wasm index 89ed015d5..82d90c281 100644 Binary files a/lib/emscripten/emtests/closebitcasts.wasm and b/lib/emscripten/emtests/closebitcasts.wasm differ diff --git a/lib/emscripten/emtests/dyncall.wasm b/lib/emscripten/emtests/dyncall.wasm index de0f05445..13ffc9617 100644 Binary files a/lib/emscripten/emtests/dyncall.wasm and b/lib/emscripten/emtests/dyncall.wasm differ diff --git a/lib/emscripten/emtests/dyncall_specific.wasm b/lib/emscripten/emtests/dyncall_specific.wasm index d7781255b..d08d0a861 100644 Binary files a/lib/emscripten/emtests/dyncall_specific.wasm and b/lib/emscripten/emtests/dyncall_specific.wasm differ diff --git a/lib/emscripten/emtests/emscripten_get_compiler_setting.wasm b/lib/emscripten/emtests/emscripten_get_compiler_setting.wasm index 3af77a618..5151cc3a8 100644 Binary files a/lib/emscripten/emtests/emscripten_get_compiler_setting.wasm and b/lib/emscripten/emtests/emscripten_get_compiler_setting.wasm differ diff --git a/lib/emscripten/emtests/env.wasm b/lib/emscripten/emtests/env.wasm index 0df9e53a5..fba068d5d 100644 Binary files a/lib/emscripten/emtests/env.wasm and b/lib/emscripten/emtests/env.wasm differ diff --git a/lib/emscripten/emtests/getValue_setValue.wasm b/lib/emscripten/emtests/getValue_setValue.wasm index cab7b6cb6..a826f008c 100644 Binary files a/lib/emscripten/emtests/getValue_setValue.wasm and b/lib/emscripten/emtests/getValue_setValue.wasm differ diff --git a/lib/emscripten/emtests/legacy_exported_runtime_numbers.wasm b/lib/emscripten/emtests/legacy_exported_runtime_numbers.wasm index e0f1ee78c..98426ab01 100644 Binary files a/lib/emscripten/emtests/legacy_exported_runtime_numbers.wasm and b/lib/emscripten/emtests/legacy_exported_runtime_numbers.wasm differ diff --git a/lib/emscripten/emtests/localtime.wasm b/lib/emscripten/emtests/localtime.wasm index 699d0d96b..083d8bd58 100644 Binary files a/lib/emscripten/emtests/localtime.wasm and b/lib/emscripten/emtests/localtime.wasm differ diff --git a/lib/emscripten/emtests/modularize_closure_pre.wasm b/lib/emscripten/emtests/modularize_closure_pre.wasm index 2eae20cd9..a2f6a5507 100644 Binary files a/lib/emscripten/emtests/modularize_closure_pre.wasm and b/lib/emscripten/emtests/modularize_closure_pre.wasm differ diff --git a/lib/emscripten/emtests/printf.wasm b/lib/emscripten/emtests/printf.wasm index c3f19ab96..b466aa9e7 100644 Binary files a/lib/emscripten/emtests/printf.wasm and b/lib/emscripten/emtests/printf.wasm differ diff --git a/lib/emscripten/emtests/puts.wasm b/lib/emscripten/emtests/puts.wasm index c01c5d8b7..6c7e1d91e 100644 Binary files a/lib/emscripten/emtests/puts.wasm and b/lib/emscripten/emtests/puts.wasm differ diff --git a/lib/emscripten/emtests/stackAlloc.wasm b/lib/emscripten/emtests/stackAlloc.wasm index 0787b9ecc..1b8910786 100644 Binary files a/lib/emscripten/emtests/stackAlloc.wasm and b/lib/emscripten/emtests/stackAlloc.wasm differ diff --git a/lib/emscripten/emtests/stack_overflow.wasm b/lib/emscripten/emtests/stack_overflow.wasm index 515a51e0d..5cc7a4791 100644 Binary files a/lib/emscripten/emtests/stack_overflow.wasm and b/lib/emscripten/emtests/stack_overflow.wasm differ diff --git a/lib/emscripten/emtests/test_addr_of_stacked.wasm b/lib/emscripten/emtests/test_addr_of_stacked.wasm index fce82c17a..12c763c03 100644 Binary files a/lib/emscripten/emtests/test_addr_of_stacked.wasm and b/lib/emscripten/emtests/test_addr_of_stacked.wasm differ diff --git a/lib/emscripten/emtests/test_alloca.wasm b/lib/emscripten/emtests/test_alloca.wasm index f623c9f2d..acbc0b180 100644 Binary files a/lib/emscripten/emtests/test_alloca.wasm and b/lib/emscripten/emtests/test_alloca.wasm differ diff --git a/lib/emscripten/emtests/test_alloca_stack.wasm b/lib/emscripten/emtests/test_alloca_stack.wasm index 98985f34a..d50dc3802 100644 Binary files a/lib/emscripten/emtests/test_alloca_stack.wasm and b/lib/emscripten/emtests/test_alloca_stack.wasm differ diff --git a/lib/emscripten/emtests/test_array2.wasm b/lib/emscripten/emtests/test_array2.wasm index 6d3f8434f..320c7f100 100644 Binary files a/lib/emscripten/emtests/test_array2.wasm and b/lib/emscripten/emtests/test_array2.wasm differ diff --git a/lib/emscripten/emtests/test_array2b.wasm b/lib/emscripten/emtests/test_array2b.wasm index 6511dbd2c..c11076457 100644 Binary files a/lib/emscripten/emtests/test_array2b.wasm and b/lib/emscripten/emtests/test_array2b.wasm differ diff --git a/lib/emscripten/emtests/test_atoX.wasm b/lib/emscripten/emtests/test_atoX.wasm index 5588c91a0..a36b2a59f 100644 Binary files a/lib/emscripten/emtests/test_atoX.wasm and b/lib/emscripten/emtests/test_atoX.wasm differ diff --git a/lib/emscripten/emtests/test_atomic.wasm b/lib/emscripten/emtests/test_atomic.wasm index 3b2c89e52..e38529b62 100644 Binary files a/lib/emscripten/emtests/test_atomic.wasm and b/lib/emscripten/emtests/test_atomic.wasm differ diff --git a/lib/emscripten/emtests/test_atomic_cxx.wasm b/lib/emscripten/emtests/test_atomic_cxx.wasm index 3f12b1663..182df2ce4 100644 Binary files a/lib/emscripten/emtests/test_atomic_cxx.wasm and b/lib/emscripten/emtests/test_atomic_cxx.wasm differ diff --git a/lib/emscripten/emtests/test_bsearch.wasm b/lib/emscripten/emtests/test_bsearch.wasm index cefea5571..3d0e3d784 100644 Binary files a/lib/emscripten/emtests/test_bsearch.wasm and b/lib/emscripten/emtests/test_bsearch.wasm differ diff --git a/lib/emscripten/emtests/test_ccall.wasm b/lib/emscripten/emtests/test_ccall.wasm index 014883f0d..b69fc46c5 100644 Binary files a/lib/emscripten/emtests/test_ccall.wasm and b/lib/emscripten/emtests/test_ccall.wasm differ diff --git a/lib/emscripten/emtests/test_complex.wasm b/lib/emscripten/emtests/test_complex.wasm index d8996ad84..bddd1b919 100644 Binary files a/lib/emscripten/emtests/test_complex.wasm and b/lib/emscripten/emtests/test_complex.wasm differ diff --git a/lib/emscripten/emtests/test_demangle_stacks.wasm b/lib/emscripten/emtests/test_demangle_stacks.wasm index e040a3a44..dd91254a4 100644 Binary files a/lib/emscripten/emtests/test_demangle_stacks.wasm and b/lib/emscripten/emtests/test_demangle_stacks.wasm differ diff --git a/lib/emscripten/emtests/test_demangle_stacks_noassert.wasm b/lib/emscripten/emtests/test_demangle_stacks_noassert.wasm index 0ddaf5d43..3a85b778c 100644 Binary files a/lib/emscripten/emtests/test_demangle_stacks_noassert.wasm and b/lib/emscripten/emtests/test_demangle_stacks_noassert.wasm differ diff --git a/lib/emscripten/emtests/test_dlmalloc_partial_2.wasm b/lib/emscripten/emtests/test_dlmalloc_partial_2.wasm index 4791dc760..5a4dbcc3c 100644 Binary files a/lib/emscripten/emtests/test_dlmalloc_partial_2.wasm and b/lib/emscripten/emtests/test_dlmalloc_partial_2.wasm differ diff --git a/lib/emscripten/emtests/test_double_varargs.wasm b/lib/emscripten/emtests/test_double_varargs.wasm index b5fd21587..1b33500ee 100644 Binary files a/lib/emscripten/emtests/test_double_varargs.wasm and b/lib/emscripten/emtests/test_double_varargs.wasm differ diff --git a/lib/emscripten/emtests/test_em_asm.wasm b/lib/emscripten/emtests/test_em_asm.wasm index e0c86abd6..464795c7b 100644 Binary files a/lib/emscripten/emtests/test_em_asm.wasm and b/lib/emscripten/emtests/test_em_asm.wasm differ diff --git a/lib/emscripten/emtests/test_em_asm_2.wasm b/lib/emscripten/emtests/test_em_asm_2.wasm index 6325ba5c8..677cf8703 100644 Binary files a/lib/emscripten/emtests/test_em_asm_2.wasm and b/lib/emscripten/emtests/test_em_asm_2.wasm differ diff --git a/lib/emscripten/emtests/test_em_asm_parameter_pack.wasm b/lib/emscripten/emtests/test_em_asm_parameter_pack.wasm index 872d966a3..5a028d06b 100644 Binary files a/lib/emscripten/emtests/test_em_asm_parameter_pack.wasm and b/lib/emscripten/emtests/test_em_asm_parameter_pack.wasm differ diff --git a/lib/emscripten/emtests/test_em_asm_signatures.wasm b/lib/emscripten/emtests/test_em_asm_signatures.wasm index e3909729c..29bf86083 100644 Binary files a/lib/emscripten/emtests/test_em_asm_signatures.wasm and b/lib/emscripten/emtests/test_em_asm_signatures.wasm differ diff --git a/lib/emscripten/emtests/test_em_asm_unicode.wasm b/lib/emscripten/emtests/test_em_asm_unicode.wasm index bf21149de..6313244ca 100644 Binary files a/lib/emscripten/emtests/test_em_asm_unicode.wasm and b/lib/emscripten/emtests/test_em_asm_unicode.wasm differ diff --git a/lib/emscripten/emtests/test_em_asm_unused_arguments.wasm b/lib/emscripten/emtests/test_em_asm_unused_arguments.wasm index bc217bf2e..fcdf08821 100644 Binary files a/lib/emscripten/emtests/test_em_asm_unused_arguments.wasm and b/lib/emscripten/emtests/test_em_asm_unused_arguments.wasm differ diff --git a/lib/emscripten/emtests/test_em_js.wasm b/lib/emscripten/emtests/test_em_js.wasm index 0f8b21682..ce044115e 100644 Binary files a/lib/emscripten/emtests/test_em_js.wasm and b/lib/emscripten/emtests/test_em_js.wasm differ diff --git a/lib/emscripten/emtests/test_emscripten_api.wasm b/lib/emscripten/emtests/test_emscripten_api.wasm index 0424dd889..b69ca687e 100644 Binary files a/lib/emscripten/emtests/test_emscripten_api.wasm and b/lib/emscripten/emtests/test_emscripten_api.wasm differ diff --git a/lib/emscripten/emtests/test_emulate_function_pointer_casts.wasm b/lib/emscripten/emtests/test_emulate_function_pointer_casts.wasm index 52a1a83b6..e115ef77f 100644 Binary files a/lib/emscripten/emtests/test_emulate_function_pointer_casts.wasm and b/lib/emscripten/emtests/test_emulate_function_pointer_casts.wasm differ diff --git a/lib/emscripten/emtests/test_erf.wasm b/lib/emscripten/emtests/test_erf.wasm index 124f3af3b..fe67b717f 100644 Binary files a/lib/emscripten/emtests/test_erf.wasm and b/lib/emscripten/emtests/test_erf.wasm differ diff --git a/lib/emscripten/emtests/test_errar.wasm b/lib/emscripten/emtests/test_errar.wasm index 65429609b..c512c803c 100644 Binary files a/lib/emscripten/emtests/test_errar.wasm and b/lib/emscripten/emtests/test_errar.wasm differ diff --git a/lib/emscripten/emtests/test_exceptions_2.wasm b/lib/emscripten/emtests/test_exceptions_2.wasm index 6add03b75..52cf48e5f 100644 Binary files a/lib/emscripten/emtests/test_exceptions_2.wasm and b/lib/emscripten/emtests/test_exceptions_2.wasm differ diff --git a/lib/emscripten/emtests/test_exceptions_convert.wasm b/lib/emscripten/emtests/test_exceptions_convert.wasm index f57ab9f75..216a54fe2 100644 Binary files a/lib/emscripten/emtests/test_exceptions_convert.wasm and b/lib/emscripten/emtests/test_exceptions_convert.wasm differ diff --git a/lib/emscripten/emtests/test_exceptions_destroy_virtual.wasm b/lib/emscripten/emtests/test_exceptions_destroy_virtual.wasm index f4794ad14..2f8f4e813 100644 Binary files a/lib/emscripten/emtests/test_exceptions_destroy_virtual.wasm and b/lib/emscripten/emtests/test_exceptions_destroy_virtual.wasm differ diff --git a/lib/emscripten/emtests/test_exceptions_libcxx.wasm b/lib/emscripten/emtests/test_exceptions_libcxx.wasm index c7019ed8f..3215a4146 100644 Binary files a/lib/emscripten/emtests/test_exceptions_libcxx.wasm and b/lib/emscripten/emtests/test_exceptions_libcxx.wasm differ diff --git a/lib/emscripten/emtests/test_exceptions_multi.wasm b/lib/emscripten/emtests/test_exceptions_multi.wasm index 2a225775a..d2261445d 100644 Binary files a/lib/emscripten/emtests/test_exceptions_multi.wasm and b/lib/emscripten/emtests/test_exceptions_multi.wasm differ diff --git a/lib/emscripten/emtests/test_exceptions_multiple_inherit.wasm b/lib/emscripten/emtests/test_exceptions_multiple_inherit.wasm index 8ee5a6350..e608942a2 100644 Binary files a/lib/emscripten/emtests/test_exceptions_multiple_inherit.wasm and b/lib/emscripten/emtests/test_exceptions_multiple_inherit.wasm differ diff --git a/lib/emscripten/emtests/test_exceptions_multiple_inherit_rethrow.wasm b/lib/emscripten/emtests/test_exceptions_multiple_inherit_rethrow.wasm index 5ca16790c..6330d0e58 100644 Binary files a/lib/emscripten/emtests/test_exceptions_multiple_inherit_rethrow.wasm and b/lib/emscripten/emtests/test_exceptions_multiple_inherit_rethrow.wasm differ diff --git a/lib/emscripten/emtests/test_exceptions_primary.wasm b/lib/emscripten/emtests/test_exceptions_primary.wasm index ba76a05b7..a072880f4 100644 Binary files a/lib/emscripten/emtests/test_exceptions_primary.wasm and b/lib/emscripten/emtests/test_exceptions_primary.wasm differ diff --git a/lib/emscripten/emtests/test_exceptions_refcount.wasm b/lib/emscripten/emtests/test_exceptions_refcount.wasm index e3876ae6a..2fd86c1d5 100644 Binary files a/lib/emscripten/emtests/test_exceptions_refcount.wasm and b/lib/emscripten/emtests/test_exceptions_refcount.wasm differ diff --git a/lib/emscripten/emtests/test_exceptions_resume.wasm b/lib/emscripten/emtests/test_exceptions_resume.wasm index 7fe8366d1..c0564c6ea 100644 Binary files a/lib/emscripten/emtests/test_exceptions_resume.wasm and b/lib/emscripten/emtests/test_exceptions_resume.wasm differ diff --git a/lib/emscripten/emtests/test_exceptions_rethrow.wasm b/lib/emscripten/emtests/test_exceptions_rethrow.wasm index b528620c4..ff3a1025f 100644 Binary files a/lib/emscripten/emtests/test_exceptions_rethrow.wasm and b/lib/emscripten/emtests/test_exceptions_rethrow.wasm differ diff --git a/lib/emscripten/emtests/test_exceptions_simplify_cfg.wasm b/lib/emscripten/emtests/test_exceptions_simplify_cfg.wasm index 72b2ce843..39122af42 100644 Binary files a/lib/emscripten/emtests/test_exceptions_simplify_cfg.wasm and b/lib/emscripten/emtests/test_exceptions_simplify_cfg.wasm differ diff --git a/lib/emscripten/emtests/test_exceptions_std.wasm b/lib/emscripten/emtests/test_exceptions_std.wasm index 4d1de92dc..2415458b0 100644 Binary files a/lib/emscripten/emtests/test_exceptions_std.wasm and b/lib/emscripten/emtests/test_exceptions_std.wasm differ diff --git a/lib/emscripten/emtests/test_exceptions_virtual_inheritance.wasm b/lib/emscripten/emtests/test_exceptions_virtual_inheritance.wasm index d984fc859..6d376d598 100644 Binary files a/lib/emscripten/emtests/test_exceptions_virtual_inheritance.wasm and b/lib/emscripten/emtests/test_exceptions_virtual_inheritance.wasm differ diff --git a/lib/emscripten/emtests/test_exceptions_white_list.wasm b/lib/emscripten/emtests/test_exceptions_white_list.wasm index a376143f8..346bd9d69 100644 Binary files a/lib/emscripten/emtests/test_exceptions_white_list.wasm and b/lib/emscripten/emtests/test_exceptions_white_list.wasm differ diff --git a/lib/emscripten/emtests/test_execvp.c b/lib/emscripten/emtests/test_execvp.c new file mode 100644 index 000000000..402654721 --- /dev/null +++ b/lib/emscripten/emtests/test_execvp.c @@ -0,0 +1,17 @@ +#include +#include + +int main() { + char command[] = "touch"; + char arg1[] = "foo.txt"; + char* argv[3]; + argv[0] = command; + argv[1] = arg1; + argv[2] = 0; + + printf("_execvp\n"); + int result = execvp(command, argv); + // should not return, and not print this message + printf("error"); + return 0; +} diff --git a/lib/emscripten/emtests/test_execvp.out b/lib/emscripten/emtests/test_execvp.out new file mode 100644 index 000000000..5048f1e2c --- /dev/null +++ b/lib/emscripten/emtests/test_execvp.out @@ -0,0 +1 @@ +_execvp diff --git a/lib/emscripten/emtests/test_execvp.wasm b/lib/emscripten/emtests/test_execvp.wasm new file mode 100644 index 000000000..a0959cc56 Binary files /dev/null and b/lib/emscripten/emtests/test_execvp.wasm differ diff --git a/lib/emscripten/emtests/test_execvp_windows.c b/lib/emscripten/emtests/test_execvp_windows.c new file mode 100644 index 000000000..c3920a4fa --- /dev/null +++ b/lib/emscripten/emtests/test_execvp_windows.c @@ -0,0 +1,18 @@ +#include +#include + +int main() { + char command[] = "C:\\Windows\\System32\\cmd.exe"; + char arg1[] = "echo"; + char arg2[] = "foo"; + char* argv[4]; + argv[0] = command; + argv[1] = arg1; + argv[2] = arg2; + argv[3] = 0; + printf("_execvp\n"); + int result = execvp(command, argv); + // should not return, and not print this message + printf("error"); + return 0; +} diff --git a/lib/emscripten/emtests/test_execvp_windows.wasm b/lib/emscripten/emtests/test_execvp_windows.wasm new file mode 100644 index 000000000..ec3a210c5 Binary files /dev/null and b/lib/emscripten/emtests/test_execvp_windows.wasm differ diff --git a/lib/emscripten/emtests/test_fast_math.wasm b/lib/emscripten/emtests/test_fast_math.wasm index d44785824..efbae458c 100644 Binary files a/lib/emscripten/emtests/test_fast_math.wasm and b/lib/emscripten/emtests/test_fast_math.wasm differ diff --git a/lib/emscripten/emtests/test_flexarray_struct.wasm b/lib/emscripten/emtests/test_flexarray_struct.wasm index c1ea8478d..dfaf36ac1 100644 Binary files a/lib/emscripten/emtests/test_flexarray_struct.wasm and b/lib/emscripten/emtests/test_flexarray_struct.wasm differ diff --git a/lib/emscripten/emtests/test_float32_precise.wasm b/lib/emscripten/emtests/test_float32_precise.wasm index 99654449c..7e5a512f4 100644 Binary files a/lib/emscripten/emtests/test_float32_precise.wasm and b/lib/emscripten/emtests/test_float32_precise.wasm differ diff --git a/lib/emscripten/emtests/test_float_builtins.wasm b/lib/emscripten/emtests/test_float_builtins.wasm index ce0eb52d2..8734671be 100644 Binary files a/lib/emscripten/emtests/test_float_builtins.wasm and b/lib/emscripten/emtests/test_float_builtins.wasm differ diff --git a/lib/emscripten/emtests/test_frexp.wasm b/lib/emscripten/emtests/test_frexp.wasm index 402260c57..44632cf1b 100644 Binary files a/lib/emscripten/emtests/test_frexp.wasm and b/lib/emscripten/emtests/test_frexp.wasm differ diff --git a/lib/emscripten/emtests/test_funcptr.wasm b/lib/emscripten/emtests/test_funcptr.wasm index 6ea033f1f..4b0ad5aaa 100644 Binary files a/lib/emscripten/emtests/test_funcptr.wasm and b/lib/emscripten/emtests/test_funcptr.wasm differ diff --git a/lib/emscripten/emtests/test_funcptr_namecollide.wasm b/lib/emscripten/emtests/test_funcptr_namecollide.wasm index cf4fe2ed6..b58125071 100644 Binary files a/lib/emscripten/emtests/test_funcptr_namecollide.wasm and b/lib/emscripten/emtests/test_funcptr_namecollide.wasm differ diff --git a/lib/emscripten/emtests/test_funcptrfunc.wasm b/lib/emscripten/emtests/test_funcptrfunc.wasm index a536d7c23..2c7a06a75 100644 Binary files a/lib/emscripten/emtests/test_funcptrfunc.wasm and b/lib/emscripten/emtests/test_funcptrfunc.wasm differ diff --git a/lib/emscripten/emtests/test_funcs.wasm b/lib/emscripten/emtests/test_funcs.wasm index 653106f3c..81f969a1f 100644 Binary files a/lib/emscripten/emtests/test_funcs.wasm and b/lib/emscripten/emtests/test_funcs.wasm differ diff --git a/lib/emscripten/emtests/test_functionpointer_libfunc_varargs.wasm b/lib/emscripten/emtests/test_functionpointer_libfunc_varargs.wasm index a90faac40..bf7ca7df1 100644 Binary files a/lib/emscripten/emtests/test_functionpointer_libfunc_varargs.wasm and b/lib/emscripten/emtests/test_functionpointer_libfunc_varargs.wasm differ diff --git a/lib/emscripten/emtests/test_fwrite_0.wasm b/lib/emscripten/emtests/test_fwrite_0.wasm index 743966f75..731796673 100644 Binary files a/lib/emscripten/emtests/test_fwrite_0.wasm and b/lib/emscripten/emtests/test_fwrite_0.wasm differ diff --git a/lib/emscripten/emtests/test_getcwd.c b/lib/emscripten/emtests/test_getcwd.c new file mode 100644 index 000000000..a3955d470 --- /dev/null +++ b/lib/emscripten/emtests/test_getcwd.c @@ -0,0 +1,10 @@ +#include +#include + +int main() { + const unsigned int size = 256; + char cwd[size] = {}; + char* buf = getcwd(cwd, size); + printf("getcwd\n"); + return 0; +} diff --git a/lib/emscripten/emtests/test_getcwd.out b/lib/emscripten/emtests/test_getcwd.out new file mode 100644 index 000000000..263db4c74 --- /dev/null +++ b/lib/emscripten/emtests/test_getcwd.out @@ -0,0 +1 @@ +getcwd diff --git a/lib/emscripten/emtests/test_getcwd.wasm b/lib/emscripten/emtests/test_getcwd.wasm new file mode 100644 index 000000000..0cf6f73a2 Binary files /dev/null and b/lib/emscripten/emtests/test_getcwd.wasm differ diff --git a/lib/emscripten/emtests/test_getgep.wasm b/lib/emscripten/emtests/test_getgep.wasm index 7fab765ec..ff3c899d3 100644 Binary files a/lib/emscripten/emtests/test_getgep.wasm and b/lib/emscripten/emtests/test_getgep.wasm differ diff --git a/lib/emscripten/emtests/test_getloadavg.wasm b/lib/emscripten/emtests/test_getloadavg.wasm index ddc4db07e..cce9f01f5 100644 Binary files a/lib/emscripten/emtests/test_getloadavg.wasm and b/lib/emscripten/emtests/test_getloadavg.wasm differ diff --git a/lib/emscripten/emtests/test_getopt.wasm b/lib/emscripten/emtests/test_getopt.wasm index 674f00335..025bd912f 100644 Binary files a/lib/emscripten/emtests/test_getopt.wasm and b/lib/emscripten/emtests/test_getopt.wasm differ diff --git a/lib/emscripten/emtests/test_getopt_long.wasm b/lib/emscripten/emtests/test_getopt_long.wasm index c30c89b36..3b7031251 100644 Binary files a/lib/emscripten/emtests/test_getopt_long.wasm and b/lib/emscripten/emtests/test_getopt_long.wasm differ diff --git a/lib/emscripten/emtests/test_globaldoubles.wasm b/lib/emscripten/emtests/test_globaldoubles.wasm index 45d14c5c7..0da31aed8 100644 Binary files a/lib/emscripten/emtests/test_globaldoubles.wasm and b/lib/emscripten/emtests/test_globaldoubles.wasm differ diff --git a/lib/emscripten/emtests/test_globals.wasm b/lib/emscripten/emtests/test_globals.wasm index a1d0fe010..e36fa0516 100644 Binary files a/lib/emscripten/emtests/test_globals.wasm and b/lib/emscripten/emtests/test_globals.wasm differ diff --git a/lib/emscripten/emtests/test_gmtime.wasm b/lib/emscripten/emtests/test_gmtime.wasm index 5c8736f9c..93032ac35 100644 Binary files a/lib/emscripten/emtests/test_gmtime.wasm and b/lib/emscripten/emtests/test_gmtime.wasm differ diff --git a/lib/emscripten/emtests/test_hello_world.wasm b/lib/emscripten/emtests/test_hello_world.wasm index f0254d743..2898d1d8e 100644 Binary files a/lib/emscripten/emtests/test_hello_world.wasm and b/lib/emscripten/emtests/test_hello_world.wasm differ diff --git a/lib/emscripten/emtests/test_i16_emcc_intrinsic.wasm b/lib/emscripten/emtests/test_i16_emcc_intrinsic.wasm index da7927000..ddc191ccf 100644 Binary files a/lib/emscripten/emtests/test_i16_emcc_intrinsic.wasm and b/lib/emscripten/emtests/test_i16_emcc_intrinsic.wasm differ diff --git a/lib/emscripten/emtests/test_i32_mul_precise.wasm b/lib/emscripten/emtests/test_i32_mul_precise.wasm index 8b23cf534..ab7d9a5d3 100644 Binary files a/lib/emscripten/emtests/test_i32_mul_precise.wasm and b/lib/emscripten/emtests/test_i32_mul_precise.wasm differ diff --git a/lib/emscripten/emtests/test_i64.wasm b/lib/emscripten/emtests/test_i64.wasm index 4efffd068..5a0ced60d 100644 Binary files a/lib/emscripten/emtests/test_i64.wasm and b/lib/emscripten/emtests/test_i64.wasm differ diff --git a/lib/emscripten/emtests/test_i64_4.wasm b/lib/emscripten/emtests/test_i64_4.wasm index c87b72def..4622fb24b 100644 Binary files a/lib/emscripten/emtests/test_i64_4.wasm and b/lib/emscripten/emtests/test_i64_4.wasm differ diff --git a/lib/emscripten/emtests/test_i64_7z.wasm b/lib/emscripten/emtests/test_i64_7z.wasm index 6685749b2..60498708b 100644 Binary files a/lib/emscripten/emtests/test_i64_7z.wasm and b/lib/emscripten/emtests/test_i64_7z.wasm differ diff --git a/lib/emscripten/emtests/test_i64_cmp2.wasm b/lib/emscripten/emtests/test_i64_cmp2.wasm index 80f8591b6..c15965948 100644 Binary files a/lib/emscripten/emtests/test_i64_cmp2.wasm and b/lib/emscripten/emtests/test_i64_cmp2.wasm differ diff --git a/lib/emscripten/emtests/test_i64_i16.wasm b/lib/emscripten/emtests/test_i64_i16.wasm index f2b517968..7a627eb2f 100644 Binary files a/lib/emscripten/emtests/test_i64_i16.wasm and b/lib/emscripten/emtests/test_i64_i16.wasm differ diff --git a/lib/emscripten/emtests/test_i64_llabs.wasm b/lib/emscripten/emtests/test_i64_llabs.wasm index d97b3be74..bfeb4cd4d 100644 Binary files a/lib/emscripten/emtests/test_i64_llabs.wasm and b/lib/emscripten/emtests/test_i64_llabs.wasm differ diff --git a/lib/emscripten/emtests/test_i64_precise.wasm b/lib/emscripten/emtests/test_i64_precise.wasm index b13dd72e2..0b203d96a 100644 Binary files a/lib/emscripten/emtests/test_i64_precise.wasm and b/lib/emscripten/emtests/test_i64_precise.wasm differ diff --git a/lib/emscripten/emtests/test_i64_precise_needed.wasm b/lib/emscripten/emtests/test_i64_precise_needed.wasm index 74f4fd760..255aa6efd 100644 Binary files a/lib/emscripten/emtests/test_i64_precise_needed.wasm and b/lib/emscripten/emtests/test_i64_precise_needed.wasm differ diff --git a/lib/emscripten/emtests/test_i64_precise_unneeded.wasm b/lib/emscripten/emtests/test_i64_precise_unneeded.wasm index 97db9c75e..70d8a469f 100644 Binary files a/lib/emscripten/emtests/test_i64_precise_unneeded.wasm and b/lib/emscripten/emtests/test_i64_precise_unneeded.wasm differ diff --git a/lib/emscripten/emtests/test_i64_qdouble.wasm b/lib/emscripten/emtests/test_i64_qdouble.wasm index af01fdac7..2312bdfc6 100644 Binary files a/lib/emscripten/emtests/test_i64_qdouble.wasm and b/lib/emscripten/emtests/test_i64_qdouble.wasm differ diff --git a/lib/emscripten/emtests/test_i64_umul.wasm b/lib/emscripten/emtests/test_i64_umul.wasm index ed0dfd5f6..fc936deb1 100644 Binary files a/lib/emscripten/emtests/test_i64_umul.wasm and b/lib/emscripten/emtests/test_i64_umul.wasm differ diff --git a/lib/emscripten/emtests/test_i64_varargs.wasm b/lib/emscripten/emtests/test_i64_varargs.wasm index 2b1bf9567..5a937850a 100644 Binary files a/lib/emscripten/emtests/test_i64_varargs.wasm and b/lib/emscripten/emtests/test_i64_varargs.wasm differ diff --git a/lib/emscripten/emtests/test_i64_zextneg.wasm b/lib/emscripten/emtests/test_i64_zextneg.wasm index e7072ede9..0b322d33a 100644 Binary files a/lib/emscripten/emtests/test_i64_zextneg.wasm and b/lib/emscripten/emtests/test_i64_zextneg.wasm differ diff --git a/lib/emscripten/emtests/test_if.wasm b/lib/emscripten/emtests/test_if.wasm index 35c2dce9f..1363ac905 100644 Binary files a/lib/emscripten/emtests/test_if.wasm and b/lib/emscripten/emtests/test_if.wasm differ diff --git a/lib/emscripten/emtests/test_if_else.wasm b/lib/emscripten/emtests/test_if_else.wasm index 218dcfa1b..4cf4d816d 100644 Binary files a/lib/emscripten/emtests/test_if_else.wasm and b/lib/emscripten/emtests/test_if_else.wasm differ diff --git a/lib/emscripten/emtests/test_indirectbr.wasm b/lib/emscripten/emtests/test_indirectbr.wasm index 38a98b0d7..ead37257e 100644 Binary files a/lib/emscripten/emtests/test_indirectbr.wasm and b/lib/emscripten/emtests/test_indirectbr.wasm differ diff --git a/lib/emscripten/emtests/test_indirectbr_many.wasm b/lib/emscripten/emtests/test_indirectbr_many.wasm index 52d9bf1ba..0b45da62a 100644 Binary files a/lib/emscripten/emtests/test_indirectbr_many.wasm and b/lib/emscripten/emtests/test_indirectbr_many.wasm differ diff --git a/lib/emscripten/emtests/test_intentional_fault.wasm b/lib/emscripten/emtests/test_intentional_fault.wasm index 52100daa1..0847ebc06 100644 Binary files a/lib/emscripten/emtests/test_intentional_fault.wasm and b/lib/emscripten/emtests/test_intentional_fault.wasm differ diff --git a/lib/emscripten/emtests/test_isnan.wasm b/lib/emscripten/emtests/test_isnan.wasm index b2372094a..cd5817ebb 100644 Binary files a/lib/emscripten/emtests/test_isnan.wasm and b/lib/emscripten/emtests/test_isnan.wasm differ diff --git a/lib/emscripten/emtests/test_libcextra.wasm b/lib/emscripten/emtests/test_libcextra.wasm index 94e3bbd36..e130a879d 100644 Binary files a/lib/emscripten/emtests/test_libcextra.wasm and b/lib/emscripten/emtests/test_libcextra.wasm differ diff --git a/lib/emscripten/emtests/test_libgen.wasm b/lib/emscripten/emtests/test_libgen.wasm index f444d6f6b..547f51017 100644 Binary files a/lib/emscripten/emtests/test_libgen.wasm and b/lib/emscripten/emtests/test_libgen.wasm differ diff --git a/lib/emscripten/emtests/test_literal_negative_zero.wasm b/lib/emscripten/emtests/test_literal_negative_zero.wasm index 5729ab2bd..df965adf2 100644 Binary files a/lib/emscripten/emtests/test_literal_negative_zero.wasm and b/lib/emscripten/emtests/test_literal_negative_zero.wasm differ diff --git a/lib/emscripten/emtests/test_llrint.wasm b/lib/emscripten/emtests/test_llrint.wasm index 4784e2bd8..ea04c0e10 100644 Binary files a/lib/emscripten/emtests/test_llrint.wasm and b/lib/emscripten/emtests/test_llrint.wasm differ diff --git a/lib/emscripten/emtests/test_llvm_fabs.wasm b/lib/emscripten/emtests/test_llvm_fabs.wasm index a8fafb48c..6a656b360 100644 Binary files a/lib/emscripten/emtests/test_llvm_fabs.wasm and b/lib/emscripten/emtests/test_llvm_fabs.wasm differ diff --git a/lib/emscripten/emtests/test_llvm_intrinsics.wasm b/lib/emscripten/emtests/test_llvm_intrinsics.wasm index 1eb8441d8..0261df971 100644 Binary files a/lib/emscripten/emtests/test_llvm_intrinsics.wasm and b/lib/emscripten/emtests/test_llvm_intrinsics.wasm differ diff --git a/lib/emscripten/emtests/test_llvmswitch.wasm b/lib/emscripten/emtests/test_llvmswitch.wasm index 9966cf220..98a9135e9 100644 Binary files a/lib/emscripten/emtests/test_llvmswitch.wasm and b/lib/emscripten/emtests/test_llvmswitch.wasm differ diff --git a/lib/emscripten/emtests/test_longjmp.wasm b/lib/emscripten/emtests/test_longjmp.wasm index a34adcd2c..d616a89d0 100644 Binary files a/lib/emscripten/emtests/test_longjmp.wasm and b/lib/emscripten/emtests/test_longjmp.wasm differ diff --git a/lib/emscripten/emtests/test_longjmp2.wasm b/lib/emscripten/emtests/test_longjmp2.wasm index 78cd20e74..e7c3fcf98 100644 Binary files a/lib/emscripten/emtests/test_longjmp2.wasm and b/lib/emscripten/emtests/test_longjmp2.wasm differ diff --git a/lib/emscripten/emtests/test_longjmp3.wasm b/lib/emscripten/emtests/test_longjmp3.wasm index 519adc53b..160aa0cfd 100644 Binary files a/lib/emscripten/emtests/test_longjmp3.wasm and b/lib/emscripten/emtests/test_longjmp3.wasm differ diff --git a/lib/emscripten/emtests/test_longjmp4.wasm b/lib/emscripten/emtests/test_longjmp4.wasm index 87efd3a02..cd296ac01 100644 Binary files a/lib/emscripten/emtests/test_longjmp4.wasm and b/lib/emscripten/emtests/test_longjmp4.wasm differ diff --git a/lib/emscripten/emtests/test_longjmp_exc.wasm b/lib/emscripten/emtests/test_longjmp_exc.wasm index 6ee493a59..f901e34ca 100644 Binary files a/lib/emscripten/emtests/test_longjmp_exc.wasm and b/lib/emscripten/emtests/test_longjmp_exc.wasm differ diff --git a/lib/emscripten/emtests/test_longjmp_funcptr.wasm b/lib/emscripten/emtests/test_longjmp_funcptr.wasm index b76699879..8c9b1015c 100644 Binary files a/lib/emscripten/emtests/test_longjmp_funcptr.wasm and b/lib/emscripten/emtests/test_longjmp_funcptr.wasm differ diff --git a/lib/emscripten/emtests/test_longjmp_repeat.wasm b/lib/emscripten/emtests/test_longjmp_repeat.wasm index 27419d80e..2d92be64c 100644 Binary files a/lib/emscripten/emtests/test_longjmp_repeat.wasm and b/lib/emscripten/emtests/test_longjmp_repeat.wasm differ diff --git a/lib/emscripten/emtests/test_longjmp_stacked.wasm b/lib/emscripten/emtests/test_longjmp_stacked.wasm index 21645b31c..ed768c814 100644 Binary files a/lib/emscripten/emtests/test_longjmp_stacked.wasm and b/lib/emscripten/emtests/test_longjmp_stacked.wasm differ diff --git a/lib/emscripten/emtests/test_longjmp_throw.wasm b/lib/emscripten/emtests/test_longjmp_throw.wasm index 4cb6e05a6..4ba7ac0fb 100644 Binary files a/lib/emscripten/emtests/test_longjmp_throw.wasm and b/lib/emscripten/emtests/test_longjmp_throw.wasm differ diff --git a/lib/emscripten/emtests/test_longjmp_unwind.wasm b/lib/emscripten/emtests/test_longjmp_unwind.wasm index 219c20777..a65f578ce 100644 Binary files a/lib/emscripten/emtests/test_longjmp_unwind.wasm and b/lib/emscripten/emtests/test_longjmp_unwind.wasm differ diff --git a/lib/emscripten/emtests/test_loop.wasm b/lib/emscripten/emtests/test_loop.wasm index b1d7090cf..976f6c965 100644 Binary files a/lib/emscripten/emtests/test_loop.wasm and b/lib/emscripten/emtests/test_loop.wasm differ diff --git a/lib/emscripten/emtests/test_lower_intrinsics.wasm b/lib/emscripten/emtests/test_lower_intrinsics.wasm index d9f919b15..8cd046a46 100644 Binary files a/lib/emscripten/emtests/test_lower_intrinsics.wasm and b/lib/emscripten/emtests/test_lower_intrinsics.wasm differ diff --git a/lib/emscripten/emtests/test_main_module_static_align.wasm b/lib/emscripten/emtests/test_main_module_static_align.wasm index 09f107bbf..786b9c93a 100644 Binary files a/lib/emscripten/emtests/test_main_module_static_align.wasm and b/lib/emscripten/emtests/test_main_module_static_align.wasm differ diff --git a/lib/emscripten/emtests/test_main_thread_async_em_asm.wasm b/lib/emscripten/emtests/test_main_thread_async_em_asm.wasm index 9bd6baf28..68db09f1d 100644 Binary files a/lib/emscripten/emtests/test_main_thread_async_em_asm.wasm and b/lib/emscripten/emtests/test_main_thread_async_em_asm.wasm differ diff --git a/lib/emscripten/emtests/test_mainenv.wasm b/lib/emscripten/emtests/test_mainenv.wasm index 79d3de95a..9385f5ab9 100644 Binary files a/lib/emscripten/emtests/test_mainenv.wasm and b/lib/emscripten/emtests/test_mainenv.wasm differ diff --git a/lib/emscripten/emtests/test_mathfuncptr.wasm b/lib/emscripten/emtests/test_mathfuncptr.wasm index 0b589a7e6..6942507fb 100644 Binary files a/lib/emscripten/emtests/test_mathfuncptr.wasm and b/lib/emscripten/emtests/test_mathfuncptr.wasm differ diff --git a/lib/emscripten/emtests/test_memcpy2.wasm b/lib/emscripten/emtests/test_memcpy2.wasm index 38085d473..84eb5e87a 100644 Binary files a/lib/emscripten/emtests/test_memcpy2.wasm and b/lib/emscripten/emtests/test_memcpy2.wasm differ diff --git a/lib/emscripten/emtests/test_memcpy3.wasm b/lib/emscripten/emtests/test_memcpy3.wasm index 86019187f..f2d5212be 100644 Binary files a/lib/emscripten/emtests/test_memcpy3.wasm and b/lib/emscripten/emtests/test_memcpy3.wasm differ diff --git a/lib/emscripten/emtests/test_memcpy_memcmp.wasm b/lib/emscripten/emtests/test_memcpy_memcmp.wasm index 076fb1b02..ed5be2c51 100644 Binary files a/lib/emscripten/emtests/test_memcpy_memcmp.wasm and b/lib/emscripten/emtests/test_memcpy_memcmp.wasm differ diff --git a/lib/emscripten/emtests/test_memmove.wasm b/lib/emscripten/emtests/test_memmove.wasm index 6fd995f3d..a50bd5c8a 100644 Binary files a/lib/emscripten/emtests/test_memmove.wasm and b/lib/emscripten/emtests/test_memmove.wasm differ diff --git a/lib/emscripten/emtests/test_memmove2.wasm b/lib/emscripten/emtests/test_memmove2.wasm index ff460adeb..a7aabf867 100644 Binary files a/lib/emscripten/emtests/test_memmove2.wasm and b/lib/emscripten/emtests/test_memmove2.wasm differ diff --git a/lib/emscripten/emtests/test_memmove3.wasm b/lib/emscripten/emtests/test_memmove3.wasm index a50bc29d5..6da6c0928 100644 Binary files a/lib/emscripten/emtests/test_memmove3.wasm and b/lib/emscripten/emtests/test_memmove3.wasm differ diff --git a/lib/emscripten/emtests/test_memorygrowth.wasm b/lib/emscripten/emtests/test_memorygrowth.wasm index e9a9d5b3b..e0d5e9242 100644 Binary files a/lib/emscripten/emtests/test_memorygrowth.wasm and b/lib/emscripten/emtests/test_memorygrowth.wasm differ diff --git a/lib/emscripten/emtests/test_memorygrowth_2.wasm b/lib/emscripten/emtests/test_memorygrowth_2.wasm index 2d9955bf5..138953fc3 100644 Binary files a/lib/emscripten/emtests/test_memorygrowth_2.wasm and b/lib/emscripten/emtests/test_memorygrowth_2.wasm differ diff --git a/lib/emscripten/emtests/test_memorygrowth_3.wasm b/lib/emscripten/emtests/test_memorygrowth_3.wasm index b37ca7a26..d3571b2c6 100644 Binary files a/lib/emscripten/emtests/test_memorygrowth_3.wasm and b/lib/emscripten/emtests/test_memorygrowth_3.wasm differ diff --git a/lib/emscripten/emtests/test_memorygrowth_wasm_mem_max.wasm b/lib/emscripten/emtests/test_memorygrowth_wasm_mem_max.wasm index 9587238a2..2f62b2a4e 100644 Binary files a/lib/emscripten/emtests/test_memorygrowth_wasm_mem_max.wasm and b/lib/emscripten/emtests/test_memorygrowth_wasm_mem_max.wasm differ diff --git a/lib/emscripten/emtests/test_memset.wasm b/lib/emscripten/emtests/test_memset.wasm index f1a907bf6..5ef44eaea 100644 Binary files a/lib/emscripten/emtests/test_memset.wasm and b/lib/emscripten/emtests/test_memset.wasm differ diff --git a/lib/emscripten/emtests/test_mmap.wasm b/lib/emscripten/emtests/test_mmap.wasm index 7bb650376..4f9395d72 100644 Binary files a/lib/emscripten/emtests/test_mmap.wasm and b/lib/emscripten/emtests/test_mmap.wasm differ diff --git a/lib/emscripten/emtests/test_negative_zero.wasm b/lib/emscripten/emtests/test_negative_zero.wasm index 25b600150..bdd0e80d8 100644 Binary files a/lib/emscripten/emtests/test_negative_zero.wasm and b/lib/emscripten/emtests/test_negative_zero.wasm differ diff --git a/lib/emscripten/emtests/test_nested_struct_varargs.wasm b/lib/emscripten/emtests/test_nested_struct_varargs.wasm index ad58548f9..0dbf108ad 100644 Binary files a/lib/emscripten/emtests/test_nested_struct_varargs.wasm and b/lib/emscripten/emtests/test_nested_struct_varargs.wasm differ diff --git a/lib/emscripten/emtests/test_nl_types.wasm b/lib/emscripten/emtests/test_nl_types.wasm index 6754a6f09..669796b96 100644 Binary files a/lib/emscripten/emtests/test_nl_types.wasm and b/lib/emscripten/emtests/test_nl_types.wasm differ diff --git a/lib/emscripten/emtests/test_perrar.wasm b/lib/emscripten/emtests/test_perrar.wasm index 4df7353bc..d94df2313 100644 Binary files a/lib/emscripten/emtests/test_perrar.wasm and b/lib/emscripten/emtests/test_perrar.wasm differ diff --git a/lib/emscripten/emtests/test_phiundef.wasm b/lib/emscripten/emtests/test_phiundef.wasm index cdf2ceaf5..cad15db8f 100644 Binary files a/lib/emscripten/emtests/test_phiundef.wasm and b/lib/emscripten/emtests/test_phiundef.wasm differ diff --git a/lib/emscripten/emtests/test_pipe.c b/lib/emscripten/emtests/test_pipe.c new file mode 100644 index 000000000..c8f650310 --- /dev/null +++ b/lib/emscripten/emtests/test_pipe.c @@ -0,0 +1,13 @@ +#include +#include +#include +#include + +int main() { + int mypipe[2]; + printf("pipe\n"); + if (pipe(mypipe)) { + printf("error\n"); + } + return 0; +} diff --git a/lib/emscripten/emtests/test_pipe.out b/lib/emscripten/emtests/test_pipe.out new file mode 100644 index 000000000..6b14dd0e0 --- /dev/null +++ b/lib/emscripten/emtests/test_pipe.out @@ -0,0 +1 @@ +pipe diff --git a/lib/emscripten/emtests/test_pipe.wasm b/lib/emscripten/emtests/test_pipe.wasm new file mode 100644 index 000000000..1aa68a5c4 Binary files /dev/null and b/lib/emscripten/emtests/test_pipe.wasm differ diff --git a/lib/emscripten/emtests/test_poll.wasm b/lib/emscripten/emtests/test_poll.wasm index 480580065..8ffdc04e8 100644 Binary files a/lib/emscripten/emtests/test_poll.wasm and b/lib/emscripten/emtests/test_poll.wasm differ diff --git a/lib/emscripten/emtests/test_posixtime.wasm b/lib/emscripten/emtests/test_posixtime.wasm index df3392fb1..a7a455af3 100644 Binary files a/lib/emscripten/emtests/test_posixtime.wasm and b/lib/emscripten/emtests/test_posixtime.wasm differ diff --git a/lib/emscripten/emtests/test_printf_2.wasm b/lib/emscripten/emtests/test_printf_2.wasm index 000bd4489..839d8a3ef 100644 Binary files a/lib/emscripten/emtests/test_printf_2.wasm and b/lib/emscripten/emtests/test_printf_2.wasm differ diff --git a/lib/emscripten/emtests/test_printf_more.wasm b/lib/emscripten/emtests/test_printf_more.wasm index 5bc1b3d1d..8dec5a6f8 100644 Binary files a/lib/emscripten/emtests/test_printf_more.wasm and b/lib/emscripten/emtests/test_printf_more.wasm differ diff --git a/lib/emscripten/emtests/test_regex.wasm b/lib/emscripten/emtests/test_regex.wasm index 32735a4dc..3f9fa0901 100644 Binary files a/lib/emscripten/emtests/test_regex.wasm and b/lib/emscripten/emtests/test_regex.wasm differ diff --git a/lib/emscripten/emtests/test_relocatable_void_function.wasm b/lib/emscripten/emtests/test_relocatable_void_function.wasm index aab5206a1..db5e5eefc 100644 Binary files a/lib/emscripten/emtests/test_relocatable_void_function.wasm and b/lib/emscripten/emtests/test_relocatable_void_function.wasm differ diff --git a/lib/emscripten/emtests/test_rounding.wasm b/lib/emscripten/emtests/test_rounding.wasm index 050f2db67..20dd299ba 100644 Binary files a/lib/emscripten/emtests/test_rounding.wasm and b/lib/emscripten/emtests/test_rounding.wasm differ diff --git a/lib/emscripten/emtests/test_runtime_stacksave.wasm b/lib/emscripten/emtests/test_runtime_stacksave.wasm index 5afe815a4..8f7a42073 100644 Binary files a/lib/emscripten/emtests/test_runtime_stacksave.wasm and b/lib/emscripten/emtests/test_runtime_stacksave.wasm differ diff --git a/lib/emscripten/emtests/test_set_align.wasm b/lib/emscripten/emtests/test_set_align.wasm index 8cd2c3d7e..4255623cd 100644 Binary files a/lib/emscripten/emtests/test_set_align.wasm and b/lib/emscripten/emtests/test_set_align.wasm differ diff --git a/lib/emscripten/emtests/test_siglongjmp.wasm b/lib/emscripten/emtests/test_siglongjmp.wasm index 5085f747a..5b6e13c7b 100644 Binary files a/lib/emscripten/emtests/test_siglongjmp.wasm and b/lib/emscripten/emtests/test_siglongjmp.wasm differ diff --git a/lib/emscripten/emtests/test_sintvars.wasm b/lib/emscripten/emtests/test_sintvars.wasm index d95f735c0..85c55ec59 100644 Binary files a/lib/emscripten/emtests/test_sintvars.wasm and b/lib/emscripten/emtests/test_sintvars.wasm differ diff --git a/lib/emscripten/emtests/test_sizeof.wasm b/lib/emscripten/emtests/test_sizeof.wasm index 7826daca1..796e7053b 100644 Binary files a/lib/emscripten/emtests/test_sizeof.wasm and b/lib/emscripten/emtests/test_sizeof.wasm differ diff --git a/lib/emscripten/emtests/test_sscanf.wasm b/lib/emscripten/emtests/test_sscanf.wasm index 64bc72f9b..caab61961 100644 Binary files a/lib/emscripten/emtests/test_sscanf.wasm and b/lib/emscripten/emtests/test_sscanf.wasm differ diff --git a/lib/emscripten/emtests/test_sscanf_3.wasm b/lib/emscripten/emtests/test_sscanf_3.wasm index aaf6940c5..a2b079166 100644 Binary files a/lib/emscripten/emtests/test_sscanf_3.wasm and b/lib/emscripten/emtests/test_sscanf_3.wasm differ diff --git a/lib/emscripten/emtests/test_sscanf_4.wasm b/lib/emscripten/emtests/test_sscanf_4.wasm index e19b937d0..e01c5db97 100644 Binary files a/lib/emscripten/emtests/test_sscanf_4.wasm and b/lib/emscripten/emtests/test_sscanf_4.wasm differ diff --git a/lib/emscripten/emtests/test_sscanf_5.wasm b/lib/emscripten/emtests/test_sscanf_5.wasm index ff2ea0d6f..4e47c5ea4 100644 Binary files a/lib/emscripten/emtests/test_sscanf_5.wasm and b/lib/emscripten/emtests/test_sscanf_5.wasm differ diff --git a/lib/emscripten/emtests/test_sscanf_6.wasm b/lib/emscripten/emtests/test_sscanf_6.wasm index 37c7583fb..5adcee63e 100644 Binary files a/lib/emscripten/emtests/test_sscanf_6.wasm and b/lib/emscripten/emtests/test_sscanf_6.wasm differ diff --git a/lib/emscripten/emtests/test_sscanf_caps.wasm b/lib/emscripten/emtests/test_sscanf_caps.wasm index c909033f3..d107a18f8 100644 Binary files a/lib/emscripten/emtests/test_sscanf_caps.wasm and b/lib/emscripten/emtests/test_sscanf_caps.wasm differ diff --git a/lib/emscripten/emtests/test_sscanf_float.wasm b/lib/emscripten/emtests/test_sscanf_float.wasm index 5807d73d6..3bde6bce1 100644 Binary files a/lib/emscripten/emtests/test_sscanf_float.wasm and b/lib/emscripten/emtests/test_sscanf_float.wasm differ diff --git a/lib/emscripten/emtests/test_sscanf_hex.wasm b/lib/emscripten/emtests/test_sscanf_hex.wasm index 338c0ace3..a3c8fd7cb 100644 Binary files a/lib/emscripten/emtests/test_sscanf_hex.wasm and b/lib/emscripten/emtests/test_sscanf_hex.wasm differ diff --git a/lib/emscripten/emtests/test_sscanf_n.wasm b/lib/emscripten/emtests/test_sscanf_n.wasm index e5dc001d6..be7e6d8ee 100644 Binary files a/lib/emscripten/emtests/test_sscanf_n.wasm and b/lib/emscripten/emtests/test_sscanf_n.wasm differ diff --git a/lib/emscripten/emtests/test_sscanf_other_whitespace.wasm b/lib/emscripten/emtests/test_sscanf_other_whitespace.wasm index 31015310a..8c511d067 100644 Binary files a/lib/emscripten/emtests/test_sscanf_other_whitespace.wasm and b/lib/emscripten/emtests/test_sscanf_other_whitespace.wasm differ diff --git a/lib/emscripten/emtests/test_sscanf_skip.wasm b/lib/emscripten/emtests/test_sscanf_skip.wasm index 0fb1dac61..df88b1995 100644 Binary files a/lib/emscripten/emtests/test_sscanf_skip.wasm and b/lib/emscripten/emtests/test_sscanf_skip.wasm differ diff --git a/lib/emscripten/emtests/test_sscanf_whitespace.wasm b/lib/emscripten/emtests/test_sscanf_whitespace.wasm index 19118832c..cb1b462b2 100644 Binary files a/lib/emscripten/emtests/test_sscanf_whitespace.wasm and b/lib/emscripten/emtests/test_sscanf_whitespace.wasm differ diff --git a/lib/emscripten/emtests/test_stack_align.wasm b/lib/emscripten/emtests/test_stack_align.wasm index a120d6f11..10172e1a8 100644 Binary files a/lib/emscripten/emtests/test_stack_align.wasm and b/lib/emscripten/emtests/test_stack_align.wasm differ diff --git a/lib/emscripten/emtests/test_stack_varargs.wasm b/lib/emscripten/emtests/test_stack_varargs.wasm index 309f838f8..c726a6a3c 100644 Binary files a/lib/emscripten/emtests/test_stack_varargs.wasm and b/lib/emscripten/emtests/test_stack_varargs.wasm differ diff --git a/lib/emscripten/emtests/test_stack_void.wasm b/lib/emscripten/emtests/test_stack_void.wasm index 978b0607a..3efc25730 100644 Binary files a/lib/emscripten/emtests/test_stack_void.wasm and b/lib/emscripten/emtests/test_stack_void.wasm differ diff --git a/lib/emscripten/emtests/test_statvfs.wasm b/lib/emscripten/emtests/test_statvfs.wasm index ef002f4fd..175d0e145 100644 Binary files a/lib/emscripten/emtests/test_statvfs.wasm and b/lib/emscripten/emtests/test_statvfs.wasm differ diff --git a/lib/emscripten/emtests/test_std_cout_new.wasm b/lib/emscripten/emtests/test_std_cout_new.wasm index 8f322ee36..e05464c1d 100644 Binary files a/lib/emscripten/emtests/test_std_cout_new.wasm and b/lib/emscripten/emtests/test_std_cout_new.wasm differ diff --git a/lib/emscripten/emtests/test_strcasecmp.wasm b/lib/emscripten/emtests/test_strcasecmp.wasm index 8aca91e7a..c1d8d2dd7 100644 Binary files a/lib/emscripten/emtests/test_strcasecmp.wasm and b/lib/emscripten/emtests/test_strcasecmp.wasm differ diff --git a/lib/emscripten/emtests/test_strcmp_uni.wasm b/lib/emscripten/emtests/test_strcmp_uni.wasm index 0069da1a9..c070216d6 100644 Binary files a/lib/emscripten/emtests/test_strcmp_uni.wasm and b/lib/emscripten/emtests/test_strcmp_uni.wasm differ diff --git a/lib/emscripten/emtests/test_strftime.wasm b/lib/emscripten/emtests/test_strftime.wasm index 561f44349..ea2687c7c 100644 Binary files a/lib/emscripten/emtests/test_strftime.wasm and b/lib/emscripten/emtests/test_strftime.wasm differ diff --git a/lib/emscripten/emtests/test_strings.wasm b/lib/emscripten/emtests/test_strings.wasm index 27f160b0f..bc26ff0e4 100644 Binary files a/lib/emscripten/emtests/test_strings.wasm and b/lib/emscripten/emtests/test_strings.wasm differ diff --git a/lib/emscripten/emtests/test_strndup.wasm b/lib/emscripten/emtests/test_strndup.wasm index 3d032b197..2f7b39502 100644 Binary files a/lib/emscripten/emtests/test_strndup.wasm and b/lib/emscripten/emtests/test_strndup.wasm differ diff --git a/lib/emscripten/emtests/test_strptime_days.wasm b/lib/emscripten/emtests/test_strptime_days.wasm index 86c51f93e..988fa2641 100644 Binary files a/lib/emscripten/emtests/test_strptime_days.wasm and b/lib/emscripten/emtests/test_strptime_days.wasm differ diff --git a/lib/emscripten/emtests/test_strptime_reentrant.wasm b/lib/emscripten/emtests/test_strptime_reentrant.wasm index 49716b6fa..24d08d7cf 100644 Binary files a/lib/emscripten/emtests/test_strptime_reentrant.wasm and b/lib/emscripten/emtests/test_strptime_reentrant.wasm differ diff --git a/lib/emscripten/emtests/test_strstr.wasm b/lib/emscripten/emtests/test_strstr.wasm index 239a78b33..1f9a363ae 100644 Binary files a/lib/emscripten/emtests/test_strstr.wasm and b/lib/emscripten/emtests/test_strstr.wasm differ diff --git a/lib/emscripten/emtests/test_strtod.wasm b/lib/emscripten/emtests/test_strtod.wasm index 3e349c2c6..34243695a 100644 Binary files a/lib/emscripten/emtests/test_strtod.wasm and b/lib/emscripten/emtests/test_strtod.wasm differ diff --git a/lib/emscripten/emtests/test_strtok.wasm b/lib/emscripten/emtests/test_strtok.wasm index aa7bef692..30a7fb7a7 100644 Binary files a/lib/emscripten/emtests/test_strtok.wasm and b/lib/emscripten/emtests/test_strtok.wasm differ diff --git a/lib/emscripten/emtests/test_strtol_bin.wasm b/lib/emscripten/emtests/test_strtol_bin.wasm index a6d4248a0..e189c8d67 100644 Binary files a/lib/emscripten/emtests/test_strtol_bin.wasm and b/lib/emscripten/emtests/test_strtol_bin.wasm differ diff --git a/lib/emscripten/emtests/test_strtol_dec.wasm b/lib/emscripten/emtests/test_strtol_dec.wasm index 9219a0b6c..071fb4130 100644 Binary files a/lib/emscripten/emtests/test_strtol_dec.wasm and b/lib/emscripten/emtests/test_strtol_dec.wasm differ diff --git a/lib/emscripten/emtests/test_strtol_hex.wasm b/lib/emscripten/emtests/test_strtol_hex.wasm index be2d48a0f..546f42a9f 100644 Binary files a/lib/emscripten/emtests/test_strtol_hex.wasm and b/lib/emscripten/emtests/test_strtol_hex.wasm differ diff --git a/lib/emscripten/emtests/test_strtol_oct.wasm b/lib/emscripten/emtests/test_strtol_oct.wasm index 52c00a59d..4e5d3b97c 100644 Binary files a/lib/emscripten/emtests/test_strtol_oct.wasm and b/lib/emscripten/emtests/test_strtol_oct.wasm differ diff --git a/lib/emscripten/emtests/test_strtold.wasm b/lib/emscripten/emtests/test_strtold.wasm index 38a2819f9..2d9da6204 100644 Binary files a/lib/emscripten/emtests/test_strtold.wasm and b/lib/emscripten/emtests/test_strtold.wasm differ diff --git a/lib/emscripten/emtests/test_strtoll_bin.wasm b/lib/emscripten/emtests/test_strtoll_bin.wasm index be521b0cf..b2ee57309 100644 Binary files a/lib/emscripten/emtests/test_strtoll_bin.wasm and b/lib/emscripten/emtests/test_strtoll_bin.wasm differ diff --git a/lib/emscripten/emtests/test_strtoll_dec.wasm b/lib/emscripten/emtests/test_strtoll_dec.wasm index 36e22518a..8e0291c94 100644 Binary files a/lib/emscripten/emtests/test_strtoll_dec.wasm and b/lib/emscripten/emtests/test_strtoll_dec.wasm differ diff --git a/lib/emscripten/emtests/test_strtoll_hex.wasm b/lib/emscripten/emtests/test_strtoll_hex.wasm index d85c91409..010f15b42 100644 Binary files a/lib/emscripten/emtests/test_strtoll_hex.wasm and b/lib/emscripten/emtests/test_strtoll_hex.wasm differ diff --git a/lib/emscripten/emtests/test_strtoll_oct.wasm b/lib/emscripten/emtests/test_strtoll_oct.wasm index b577d59e3..f25d954e3 100644 Binary files a/lib/emscripten/emtests/test_strtoll_oct.wasm and b/lib/emscripten/emtests/test_strtoll_oct.wasm differ diff --git a/lib/emscripten/emtests/test_struct_varargs.wasm b/lib/emscripten/emtests/test_struct_varargs.wasm index 82667ab3e..607530d4e 100644 Binary files a/lib/emscripten/emtests/test_struct_varargs.wasm and b/lib/emscripten/emtests/test_struct_varargs.wasm differ diff --git a/lib/emscripten/emtests/test_time_c.wasm b/lib/emscripten/emtests/test_time_c.wasm index 8003cc401..263cedc61 100644 Binary files a/lib/emscripten/emtests/test_time_c.wasm and b/lib/emscripten/emtests/test_time_c.wasm differ diff --git a/lib/emscripten/emtests/test_tracing.wasm b/lib/emscripten/emtests/test_tracing.wasm index 212bcf7ff..35d45a0ca 100644 Binary files a/lib/emscripten/emtests/test_tracing.wasm and b/lib/emscripten/emtests/test_tracing.wasm differ diff --git a/lib/emscripten/emtests/test_transtrcase.wasm b/lib/emscripten/emtests/test_transtrcase.wasm index fc14fb98b..fc54b2a41 100644 Binary files a/lib/emscripten/emtests/test_transtrcase.wasm and b/lib/emscripten/emtests/test_transtrcase.wasm differ diff --git a/lib/emscripten/emtests/test_trickystring.wasm b/lib/emscripten/emtests/test_trickystring.wasm index def39a824..99c45e800 100644 Binary files a/lib/emscripten/emtests/test_trickystring.wasm and b/lib/emscripten/emtests/test_trickystring.wasm differ diff --git a/lib/emscripten/emtests/test_uname.wasm b/lib/emscripten/emtests/test_uname.wasm index 097a46649..c28801d39 100644 Binary files a/lib/emscripten/emtests/test_uname.wasm and b/lib/emscripten/emtests/test_uname.wasm differ diff --git a/lib/emscripten/emtests/test_unary_literal.wasm b/lib/emscripten/emtests/test_unary_literal.wasm index 14042277d..aa7695bfe 100644 Binary files a/lib/emscripten/emtests/test_unary_literal.wasm and b/lib/emscripten/emtests/test_unary_literal.wasm differ diff --git a/lib/emscripten/emtests/test_utf.wasm b/lib/emscripten/emtests/test_utf.wasm index 92e6da9ab..6e0f90770 100644 Binary files a/lib/emscripten/emtests/test_utf.wasm and b/lib/emscripten/emtests/test_utf.wasm differ diff --git a/lib/emscripten/emtests/test_varargs.wasm b/lib/emscripten/emtests/test_varargs.wasm index daf5c40aa..f672fe8dc 100644 Binary files a/lib/emscripten/emtests/test_varargs.wasm and b/lib/emscripten/emtests/test_varargs.wasm differ diff --git a/lib/emscripten/emtests/test_varargs_multi.wasm b/lib/emscripten/emtests/test_varargs_multi.wasm index 99e2b5b86..d81b0a176 100644 Binary files a/lib/emscripten/emtests/test_varargs_multi.wasm and b/lib/emscripten/emtests/test_varargs_multi.wasm differ diff --git a/lib/emscripten/emtests/test_vprintf.wasm b/lib/emscripten/emtests/test_vprintf.wasm index 0c2d4d462..34391ea23 100644 Binary files a/lib/emscripten/emtests/test_vprintf.wasm and b/lib/emscripten/emtests/test_vprintf.wasm differ diff --git a/lib/emscripten/emtests/test_vsnprintf.wasm b/lib/emscripten/emtests/test_vsnprintf.wasm index dca3d57e4..22e33b19a 100644 Binary files a/lib/emscripten/emtests/test_vsnprintf.wasm and b/lib/emscripten/emtests/test_vsnprintf.wasm differ diff --git a/lib/emscripten/emtests/test_wprintf.wasm b/lib/emscripten/emtests/test_wprintf.wasm index 5914aa25d..dbcd76f4c 100644 Binary files a/lib/emscripten/emtests/test_wprintf.wasm and b/lib/emscripten/emtests/test_wprintf.wasm differ diff --git a/lib/emscripten/emtests/test_write_stdout_fileno.wasm b/lib/emscripten/emtests/test_write_stdout_fileno.wasm index a12aab93f..18aafd632 100644 Binary files a/lib/emscripten/emtests/test_write_stdout_fileno.wasm and b/lib/emscripten/emtests/test_write_stdout_fileno.wasm differ diff --git a/lib/emscripten/emtests/test_zero_multiplication.wasm b/lib/emscripten/emtests/test_zero_multiplication.wasm index bf8da640a..664643eee 100644 Binary files a/lib/emscripten/emtests/test_zero_multiplication.wasm and b/lib/emscripten/emtests/test_zero_multiplication.wasm differ diff --git a/lib/emscripten/emtests/test_zerodiv.wasm b/lib/emscripten/emtests/test_zerodiv.wasm index 7af5932ca..42f047cda 100644 Binary files a/lib/emscripten/emtests/test_zerodiv.wasm and b/lib/emscripten/emtests/test_zerodiv.wasm differ diff --git a/lib/emscripten/src/README.md b/lib/emscripten/src/README.md index 128ef1783..54b9f9dc1 100644 --- a/lib/emscripten/src/README.md +++ b/lib/emscripten/src/README.md @@ -10,7 +10,7 @@ ``` - **abort** ✅ 🔥     [:top:](#host-apis) ```rust - fn abort(message: u32, ctx: &mut Ctx) + fn abort(ctx: &mut Ctx, message: u32, ) ``` - **abort_on_cannot_grow_memory** ✅     [:top:](#host-apis) ```rust @@ -28,11 +28,11 @@ - **\_getenv** ✅     [:top:](#host-apis) ```rust - fn _getenv(name: c_int, ctx: &mut Ctx) + fn _getenv(ctx: &mut Ctx, name: c_int, ) ``` - **\_putenv** ✅     [:top:](#host-apis) ```rust - fn _putenv(name: c_int, ctx: &mut Ctx) + fn _putenv(ctx: &mut Ctx, name: c_int, ) ``` - **\_setenv** ✅     [:top:](#host-apis) ```rust @@ -40,7 +40,7 @@ ``` - **\_unsetenv** ✅     [:top:](#host-apis) ```rust - fn _unsetenv(name: c_int, ctx: &mut Ctx) + fn _unsetenv(ctx: &mut Ctx, name: c_int, ) ``` ###### THREAD @@ -70,7 +70,7 @@ - **\_emscripten_memcpy_big** ✅ 🔥     [:top:](#host-apis) ```rust - fn _emscripten_memcpy_big(dest: u32, src: u32, len: u32, ctx: &mut Ctx) -> u32 + fn _emscripten_memcpy_big(ctx: &mut Ctx, dest: u32, src: u32, len: u32, ) -> u32 ``` - **enlarge_memory** ✅     [:top:](#host-apis) ```rust @@ -78,7 +78,7 @@ ``` - **get_total_memory** ✅     [:top:](#host-apis) ```rust - fn get_total_memory(ctx: &mut Ctx) -> u32 + fn get_total_memory(ctx: &mut Ctx, ) -> u32 ``` ###### TIMING @@ -337,7 +337,7 @@ ``` - **open** (\_\_\_syscall5) ✅ ❗️ 🔥     [:top:](#host-apis) ```rust - fn open(path: u32, flags: c_int, mode: c_int, ctx: &mut Ctx) -> c_int + fn open(ctx: &mut Ctx, path: u32, flags: c_int, mode: c_int, ) -> c_int ``` - **openat** (\_\_\_syscall295)     [:top:](#host-apis) ```rust @@ -385,7 +385,7 @@ ``` - **read** (\_\_\_syscall3) ✅ ❗️     [:top:](#host-apis) ```rust - fn read(fd: c_int, buf: u32, count: size_t, ctx: &mut Ctx) -> ssize_t + fn read(ctx: &mut Ctx, fd: c_int, buf: u32, count: size_t, ) -> ssize_t ``` - **readlink** (\_\_\_syscall85)     [:top:](#host-apis) ```rust diff --git a/lib/emscripten/src/emscripten_target.rs b/lib/emscripten/src/emscripten_target.rs new file mode 100644 index 000000000..4508f4f54 --- /dev/null +++ b/lib/emscripten/src/emscripten_target.rs @@ -0,0 +1,575 @@ +#![allow(non_snake_case)] + +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 +} +// round 2 +pub fn nullFunc_dii(_ctx: &mut Ctx, _index: i32) { + debug!("emscripten::nullFunc_dii"); +} +pub fn nullFunc_diiii(_ctx: &mut Ctx, _index: i32) { + debug!("emscripten::nullFunc_diiii"); +} +pub fn nullFunc_iiji(_ctx: &mut Ctx, _index: i32) { + debug!("emscripten::nullFunc_iiji"); +} +pub fn nullFunc_j(_ctx: &mut Ctx, _index: i32) { + debug!("emscripten::nullFunc_j"); +} +pub fn nullFunc_jij(_ctx: &mut Ctx, _index: i32) { + debug!("emscripten::nullFunc_jij"); +} +pub fn nullFunc_jjj(_ctx: &mut Ctx, _index: i32) { + debug!("emscripten::nullFunc_jjj"); +} +pub fn nullFunc_vd(_ctx: &mut Ctx, _index: i32) { + debug!("emscripten::nullFunc_vd"); +} +pub fn nullFunc_viiiiiii(_ctx: &mut Ctx, _index: i32) { + debug!("emscripten::nullFunc_viiiiiii"); +} +pub fn nullFunc_viiiiiiii(_ctx: &mut Ctx, _index: i32) { + debug!("emscripten::nullFunc_viiiiiiii"); +} +pub fn nullFunc_viiiiiiiii(_ctx: &mut Ctx, _index: i32) { + debug!("emscripten::nullFunc_viiiiiiiii"); +} +pub fn nullFunc_viiij(_ctx: &mut Ctx, _index: i32) { + debug!("emscripten::nullFunc_viiij"); +} +pub fn nullFunc_viiijiiii(_ctx: &mut Ctx, _index: i32) { + debug!("emscripten::nullFunc_viiijiiii"); +} +pub fn nullFunc_viiijiiiiii(_ctx: &mut Ctx, _index: i32) { + debug!("emscripten::nullFunc_viiijiiiiii"); +} +pub fn nullFunc_viij(_ctx: &mut Ctx, _index: i32) { + debug!("emscripten::nullFunc_viij"); +} +pub fn nullFunc_viiji(_ctx: &mut Ctx, _index: i32) { + debug!("emscripten::nullFunc_viiji"); +} +pub fn nullFunc_viijiii(_ctx: &mut Ctx, _index: i32) { + debug!("emscripten::nullFunc_viijiii"); +} +pub fn nullFunc_viijj(_ctx: &mut Ctx, _index: i32) { + debug!("emscripten::nullFunc_viijj"); +} +pub fn nullFunc_vij(_ctx: &mut Ctx, _index: i32) { + debug!("emscripten::nullFunc_vij"); +} +pub fn nullFunc_viji(_ctx: &mut Ctx, _index: i32) { + debug!("emscripten::nullFunc_viji"); +} +pub fn nullFunc_vijiii(_ctx: &mut Ctx, _index: i32) { + debug!("emscripten::nullFunc_vijiii"); +} +pub fn nullFunc_vijj(_ctx: &mut Ctx, _index: i32) { + debug!("emscripten::nullFunc_vijj"); +} +pub fn invoke_dii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32) -> f64 { + debug!("emscripten::invoke_dii"); + if let Some(dyn_call_dii) = &get_emscripten_data(ctx).dyn_call_dii { + dyn_call_dii.call(index, a1, a2).unwrap() + } else { + panic!("dyn_call_dii is set to None"); + } +} +pub fn invoke_diiii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32, a4: i32) -> f64 { + debug!("emscripten::invoke_diiii"); + if let Some(dyn_call_diiii) = &get_emscripten_data(ctx).dyn_call_diiii { + dyn_call_diiii.call(index, a1, a2, a3, a4).unwrap() + } else { + panic!("dyn_call_diiii is set to None"); + } +} +pub fn invoke_iiiii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32, a4: i32) -> i32 { + debug!("emscripten::invoke_iiiii"); + if let Some(dyn_call_iiiii) = &get_emscripten_data(ctx).dyn_call_iiiii { + dyn_call_iiiii.call(index, a1, a2, a3, a4).unwrap() + } else { + panic!("dyn_call_iiiii is set to None"); + } +} +pub fn invoke_iiiiii( + ctx: &mut Ctx, + index: i32, + a1: i32, + a2: i32, + a3: i32, + a4: i32, + a5: i32, +) -> i32 { + debug!("emscripten::invoke_iiiiii"); + if let Some(dyn_call_iiiiii) = &get_emscripten_data(ctx).dyn_call_iiiiii { + dyn_call_iiiiii.call(index, a1, a2, a3, a4, a5).unwrap() + } else { + panic!("dyn_call_iiiiii is set to None"); + } +} +pub fn invoke_vd(ctx: &mut Ctx, index: i32, a1: f64) { + debug!("emscripten::invoke_vd"); + if let Some(dyn_call_vd) = &get_emscripten_data(ctx).dyn_call_vd { + dyn_call_vd.call(index, a1).unwrap(); + } else { + panic!("dyn_call_vd is set to None"); + } +} +pub fn invoke_viiiii(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32, a4: i32, a5: i32) { + debug!("emscripten::invoke_viiiii"); + if let Some(dyn_call_viiiii) = &get_emscripten_data(ctx).dyn_call_viiiii { + dyn_call_viiiii.call(index, a1, a2, a3, a4, a5).unwrap(); + } else { + panic!("dyn_call_viiiii is set to None"); + } +} +pub fn invoke_viiiiii( + ctx: &mut Ctx, + index: i32, + a1: i32, + a2: i32, + a3: i32, + a4: i32, + a5: i32, + a6: i32, +) { + debug!("emscripten::invoke_viiiiii"); + if let Some(dyn_call_viiiiii) = &get_emscripten_data(ctx).dyn_call_viiiiii { + dyn_call_viiiiii + .call(index, a1, a2, a3, a4, a5, a6) + .unwrap(); + } else { + panic!("dyn_call_viiiiii is set to None"); + } +} +pub fn invoke_viiiiiii( + ctx: &mut Ctx, + index: i32, + a1: i32, + a2: i32, + a3: i32, + a4: i32, + a5: i32, + a6: i32, + a7: i32, +) { + debug!("emscripten::invoke_viiiiiii"); + if let Some(dyn_call_viiiiiii) = &get_emscripten_data(ctx).dyn_call_viiiiiii { + dyn_call_viiiiiii + .call(index, a1, a2, a3, a4, a5, a6, a7) + .unwrap(); + } else { + panic!("dyn_call_viiiiiii is set to None"); + } +} +pub fn invoke_viiiiiiii( + ctx: &mut Ctx, + index: i32, + a1: i32, + a2: i32, + a3: i32, + a4: i32, + a5: i32, + a6: i32, + a7: i32, + a8: i32, +) { + debug!("emscripten::invoke_viiiiiiii"); + if let Some(dyn_call_viiiiiiii) = &get_emscripten_data(ctx).dyn_call_viiiiiiii { + dyn_call_viiiiiiii + .call(index, a1, a2, a3, a4, a5, a6, a7, a8) + .unwrap(); + } else { + panic!("dyn_call_viiiiiiii is set to None"); + } +} +pub fn invoke_viiiiiiiii( + ctx: &mut Ctx, + index: i32, + a1: i32, + a2: i32, + a3: i32, + a4: i32, + a5: i32, + a6: i32, + a7: i32, + a8: i32, + a9: i32, +) { + debug!("emscripten::invoke_viiiiiiiii"); + if let Some(dyn_call_viiiiiiiii) = &get_emscripten_data(ctx).dyn_call_viiiiiiiii { + dyn_call_viiiiiiiii + .call(index, a1, a2, a3, a4, a5, a6, a7, a8, a9) + .unwrap(); + } else { + panic!("dyn_call_viiiiiiiii is set to None"); + } +} +pub fn invoke_iiji(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32, a4: i32) -> i32 { + debug!("emscripten::invoke_iiji"); + if let Some(dyn_call_iiji) = &get_emscripten_data(ctx).dyn_call_iiji { + dyn_call_iiji.call(index, a1, a2, a3, a4).unwrap() + } else { + panic!("dyn_call_iiji is set to None"); + } +} +pub fn invoke_j(ctx: &mut Ctx, index: i32) -> i32 { + debug!("emscripten::invoke_j"); + if let Some(dyn_call_j) = &get_emscripten_data(ctx).dyn_call_j { + dyn_call_j.call(index).unwrap() + } else { + panic!("dyn_call_j is set to None"); + } +} +pub fn invoke_ji(ctx: &mut Ctx, index: i32, a1: i32) -> i32 { + debug!("emscripten::invoke_ji"); + if let Some(dyn_call_ji) = &get_emscripten_data(ctx).dyn_call_ji { + dyn_call_ji.call(index, a1).unwrap() + } else { + panic!("dyn_call_ji is set to None"); + } +} +pub fn invoke_jij(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32) -> i32 { + debug!("emscripten::invoke_jij"); + if let Some(dyn_call_jij) = &get_emscripten_data(ctx).dyn_call_jij { + dyn_call_jij.call(index, a1, a2, a3).unwrap() + } else { + panic!("dyn_call_jij is set to None"); + } +} +pub fn invoke_jjj(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32, a4: i32) -> i32 { + debug!("emscripten::invoke_jjj"); + if let Some(dyn_call_jjj) = &get_emscripten_data(ctx).dyn_call_jjj { + dyn_call_jjj.call(index, a1, a2, a3, a4).unwrap() + } else { + panic!("dyn_call_jjj is set to None"); + } +} +pub fn invoke_viiij(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32, a4: i32, a5: i32) { + debug!("emscripten::invoke_viiij"); + if let Some(dyn_call_viiij) = &get_emscripten_data(ctx).dyn_call_viiij { + dyn_call_viiij.call(index, a1, a2, a3, a4, a5).unwrap(); + } else { + panic!("dyn_call_viiij is set to None"); + } +} +pub fn invoke_viiijiiii( + ctx: &mut Ctx, + index: i32, + a1: i32, + a2: i32, + a3: i32, + a4: i32, + a5: i32, + a6: i32, + a7: i32, + a8: i32, + a9: i32, +) { + debug!("emscripten::invoke_viiijiiii"); + if let Some(dyn_call_viiijiiii) = &get_emscripten_data(ctx).dyn_call_viiijiiii { + dyn_call_viiijiiii + .call(index, a1, a2, a3, a4, a5, a6, a7, a8, a9) + .unwrap(); + } else { + panic!("dyn_call_viiijiiii is set to None"); + } +} +pub fn invoke_viiijiiiiii( + ctx: &mut Ctx, + index: i32, + a1: i32, + a2: i32, + a3: i32, + a4: i32, + a5: i32, + a6: i32, + a7: i32, + a8: i32, + a9: i32, + a10: i32, + a11: i32, +) { + debug!("emscripten::invoke_viiijiiiiii"); + if let Some(dyn_call_viiijiiiiii) = &get_emscripten_data(ctx).dyn_call_viiijiiiiii { + dyn_call_viiijiiiiii + .call(index, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) + .unwrap(); + } else { + panic!("dyn_call_viiijiiiiii is set to None"); + } +} +pub fn invoke_viij(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32, a4: i32) { + debug!("emscripten::invoke_viij"); + if let Some(dyn_call_viij) = &get_emscripten_data(ctx).dyn_call_viij { + dyn_call_viij.call(index, a1, a2, a3, a4).unwrap(); + } else { + panic!("dyn_call_viij is set to None"); + } +} +pub fn invoke_viiji(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32, a4: i32, a5: i32) { + debug!("emscripten::invoke_viiji"); + if let Some(dyn_call_viiji) = &get_emscripten_data(ctx).dyn_call_viiji { + dyn_call_viiji.call(index, a1, a2, a3, a4, a5).unwrap(); + } else { + panic!("dyn_call_viiji is set to None"); + } +} +pub fn invoke_viijiii( + ctx: &mut Ctx, + index: i32, + a1: i32, + a2: i32, + a3: i32, + a4: i32, + a5: i32, + a6: i32, + a7: i32, +) { + debug!("emscripten::invoke_viijiii"); + if let Some(dyn_call_viijiii) = &get_emscripten_data(ctx).dyn_call_viijiii { + dyn_call_viijiii + .call(index, a1, a2, a3, a4, a5, a6, a7) + .unwrap(); + } else { + panic!("dyn_call_viijiii is set to None"); + } +} +pub fn invoke_viijj( + ctx: &mut Ctx, + index: i32, + a1: i32, + a2: i32, + a3: i32, + a4: i32, + a5: i32, + a6: i32, +) { + debug!("emscripten::invoke_viijj"); + if let Some(dyn_call_viijj) = &get_emscripten_data(ctx).dyn_call_viijj { + dyn_call_viijj.call(index, a1, a2, a3, a4, a5, a6).unwrap(); + } else { + panic!("dyn_call_viijj is set to None"); + } +} +pub fn invoke_vij(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32) { + debug!("emscripten::invoke_vij"); + if let Some(dyn_call_vij) = &get_emscripten_data(ctx).dyn_call_vij { + dyn_call_vij.call(index, a1, a2, a3).unwrap(); + } else { + panic!("dyn_call_vij is set to None"); + } +} +pub fn invoke_viji(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32, a4: i32) { + debug!("emscripten::invoke_viji"); + if let Some(dyn_call_viji) = &get_emscripten_data(ctx).dyn_call_viji { + dyn_call_viji.call(index, a1, a2, a3, a4).unwrap() + } else { + panic!("dyn_call_viji is set to None"); + } +} +pub fn invoke_vijiii( + ctx: &mut Ctx, + index: i32, + a1: i32, + a2: i32, + a3: i32, + a4: i32, + a5: i32, + a6: i32, +) { + debug!("emscripten::invoke_vijiii"); + if let Some(dyn_call_vijiii) = &get_emscripten_data(ctx).dyn_call_vijiii { + dyn_call_vijiii.call(index, a1, a2, a3, a4, a5, a6).unwrap() + } else { + panic!("dyn_call_vijiii is set to None"); + } +} +pub fn invoke_vijj(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32, a4: i32, a5: i32) { + debug!("emscripten::invoke_vijj"); + if let Some(dyn_call_vijj) = &get_emscripten_data(ctx).dyn_call_vijj { + dyn_call_vijj.call(index, a1, a2, a3, a4, a5).unwrap() + } else { + panic!("dyn_call_vijj is set to None"); + } +} diff --git a/lib/emscripten/src/env/mod.rs b/lib/emscripten/src/env/mod.rs index 50b7a48f9..662d4ba94 100644 --- a/lib/emscripten/src/env/mod.rs +++ b/lib/emscripten/src/env/mod.rs @@ -14,16 +14,16 @@ use crate::{allocate_on_stack, EmscriptenData}; use std::os::raw::c_int; use wasmer_runtime_core::vm::Ctx; -pub fn _getaddrinfo(_one: i32, _two: i32, _three: i32, _four: i32, _ctx: &mut Ctx) -> i32 { +pub fn _getaddrinfo(_ctx: &mut Ctx, _one: i32, _two: i32, _three: i32, _four: i32) -> i32 { debug!("emscripten::_getaddrinfo"); -1 } -pub fn call_malloc(size: u32, ctx: &mut Ctx) -> u32 { +pub fn call_malloc(ctx: &mut Ctx, size: u32) -> u32 { get_emscripten_data(ctx).malloc.call(size).unwrap() } -pub fn call_memalign(alignment: u32, size: u32, ctx: &mut Ctx) -> u32 { +pub fn call_memalign(ctx: &mut Ctx, alignment: u32, size: u32) -> u32 { if let Some(memalign) = &get_emscripten_data(ctx).memalign { memalign.call(alignment, size).unwrap() } else { @@ -31,7 +31,7 @@ pub fn call_memalign(alignment: u32, size: u32, ctx: &mut Ctx) -> u32 { } } -pub fn call_memset(pointer: u32, value: u32, size: u32, ctx: &mut Ctx) -> u32 { +pub fn call_memset(ctx: &mut Ctx, pointer: u32, value: u32, size: u32) -> u32 { get_emscripten_data(ctx) .memset .call(pointer, value, size) @@ -48,16 +48,16 @@ pub fn _getpagesize(_ctx: &mut Ctx) -> u32 { } #[allow(clippy::cast_ptr_alignment)] -pub fn ___build_environment(environ: c_int, ctx: &mut Ctx) { +pub fn ___build_environment(ctx: &mut Ctx, environ: c_int) { debug!("emscripten::___build_environment {}", environ); const MAX_ENV_VALUES: u32 = 64; const TOTAL_ENV_SIZE: u32 = 1024; let environment = emscripten_memory_pointer!(ctx.memory(0), environ) as *mut c_int; unsafe { let (pool_offset, _pool_slice): (u32, &mut [u8]) = - allocate_on_stack(TOTAL_ENV_SIZE as u32, ctx); + allocate_on_stack(ctx, TOTAL_ENV_SIZE as u32); let (env_offset, _env_slice): (u32, &mut [u8]) = - allocate_on_stack((MAX_ENV_VALUES * 4) as u32, ctx); + allocate_on_stack(ctx, (MAX_ENV_VALUES * 4) as u32); let env_ptr = emscripten_memory_pointer!(ctx.memory(0), env_offset) as *mut c_int; let mut _pool_ptr = emscripten_memory_pointer!(ctx.memory(0), pool_offset) as *mut c_int; *env_ptr = pool_offset as i32; @@ -70,8 +70,8 @@ pub fn ___build_environment(environ: c_int, ctx: &mut Ctx) { // }; } -pub fn ___assert_fail(a: c_int, b: c_int, c: c_int, d: c_int, _ctx: &mut Ctx) { - debug!("emscripten::___assert_fail {} {} {} {}", a, b, c, d); +pub fn ___assert_fail(_ctx: &mut Ctx, _a: c_int, _b: c_int, _c: c_int, _d: c_int) { + debug!("emscripten::___assert_fail {} {} {} {}", _a, _b, _c, _d); // TODO: Implement like emscripten expects regarding memory/page size // TODO raise an error } diff --git a/lib/emscripten/src/env/unix/mod.rs b/lib/emscripten/src/env/unix/mod.rs index d701bd6e8..cc7e01b03 100644 --- a/lib/emscripten/src/env/unix/mod.rs +++ b/lib/emscripten/src/env/unix/mod.rs @@ -13,7 +13,7 @@ use wasmer_runtime_core::vm::Ctx; // #[no_mangle] /// emscripten: _getenv // (name: *const char) -> *const c_char; -pub fn _getenv(name: i32, ctx: &mut Ctx) -> u32 { +pub fn _getenv(ctx: &mut Ctx, name: i32) -> u32 { debug!("emscripten::_getenv"); let name_addr = emscripten_memory_pointer!(ctx.memory(0), name) as *const c_char; @@ -29,7 +29,7 @@ pub fn _getenv(name: i32, ctx: &mut Ctx) -> u32 { } /// emscripten: _setenv // (name: *const char, name: *const value, overwrite: int); -pub fn _setenv(name: c_int, value: c_int, overwrite: c_int, ctx: &mut Ctx) -> c_int { +pub fn _setenv(ctx: &mut Ctx, name: c_int, value: c_int, overwrite: c_int) -> c_int { debug!("emscripten::_setenv"); let name_addr = emscripten_memory_pointer!(ctx.memory(0), name) as *const c_char; @@ -42,7 +42,7 @@ pub fn _setenv(name: c_int, value: c_int, overwrite: c_int, ctx: &mut Ctx) -> c_ } /// emscripten: _putenv // (name: *const char); -pub fn _putenv(name: c_int, ctx: &mut Ctx) -> c_int { +pub fn _putenv(ctx: &mut Ctx, name: c_int) -> c_int { debug!("emscripten::_putenv"); let name_addr = emscripten_memory_pointer!(ctx.memory(0), name) as *const c_char; @@ -53,7 +53,7 @@ pub fn _putenv(name: c_int, ctx: &mut Ctx) -> c_int { } /// emscripten: _unsetenv // (name: *const char); -pub fn _unsetenv(name: c_int, ctx: &mut Ctx) -> c_int { +pub fn _unsetenv(ctx: &mut Ctx, name: c_int) -> c_int { debug!("emscripten::_unsetenv"); let name_addr = emscripten_memory_pointer!(ctx.memory(0), name) as *const c_char; @@ -64,7 +64,7 @@ pub fn _unsetenv(name: c_int, ctx: &mut Ctx) -> c_int { } #[allow(clippy::cast_ptr_alignment)] -pub fn _getpwnam(name_ptr: c_int, ctx: &mut Ctx) -> c_int { +pub fn _getpwnam(ctx: &mut Ctx, name_ptr: c_int) -> c_int { debug!("emscripten::_getpwnam {}", name_ptr); #[repr(C)] @@ -85,7 +85,7 @@ pub fn _getpwnam(name_ptr: c_int, ctx: &mut Ctx) -> c_int { unsafe { let passwd = &*libc_getpwnam(name.as_ptr()); - let passwd_struct_offset = call_malloc(mem::size_of::() as _, ctx); + let passwd_struct_offset = call_malloc(ctx, mem::size_of::() as _); let passwd_struct_ptr = emscripten_memory_pointer!(ctx.memory(0), passwd_struct_offset) as *mut GuestPasswd; @@ -102,7 +102,7 @@ pub fn _getpwnam(name_ptr: c_int, ctx: &mut Ctx) -> c_int { } #[allow(clippy::cast_ptr_alignment)] -pub fn _getgrnam(name_ptr: c_int, ctx: &mut Ctx) -> c_int { +pub fn _getgrnam(ctx: &mut Ctx, name_ptr: c_int) -> c_int { debug!("emscripten::_getgrnam {}", name_ptr); #[repr(C)] @@ -120,7 +120,7 @@ pub fn _getgrnam(name_ptr: c_int, ctx: &mut Ctx) -> c_int { unsafe { let group = &*libc_getgrnam(name.as_ptr()); - let group_struct_offset = call_malloc(mem::size_of::() as _, ctx); + let group_struct_offset = call_malloc(ctx, mem::size_of::() as _); let group_struct_ptr = emscripten_memory_pointer!(ctx.memory(0), group_struct_offset) as *mut GuestGroup; @@ -133,7 +133,7 @@ pub fn _getgrnam(name_ptr: c_int, ctx: &mut Ctx) -> c_int { } } -pub fn _sysconf(name: c_int, _ctx: &mut Ctx) -> i32 { +pub fn _sysconf(_ctx: &mut Ctx, name: c_int) -> i32 { debug!("emscripten::_sysconf {}", name); // TODO: Implement like emscripten expects regarding memory/page size unsafe { sysconf(name) as i32 } // TODO review i64 diff --git a/lib/emscripten/src/env/windows/mod.rs b/lib/emscripten/src/env/windows/mod.rs index b4171e13c..e95a66ec2 100644 --- a/lib/emscripten/src/env/windows/mod.rs +++ b/lib/emscripten/src/env/windows/mod.rs @@ -7,6 +7,7 @@ use std::os::raw::c_char; use crate::env::call_malloc; use crate::utils::{copy_cstr_into_wasm, read_string_from_wasm}; +use std::ffi::CStr; use wasmer_runtime_core::vm::Ctx; extern "C" { @@ -16,7 +17,7 @@ extern "C" { // #[no_mangle] /// emscripten: _getenv // (name: *const char) -> *const c_char; -pub fn _getenv(name: u32, ctx: &mut Ctx) -> u32 { +pub fn _getenv(ctx: &mut Ctx, name: u32) -> u32 { debug!("emscripten::_getenv"); let name_string = read_string_from_wasm(ctx.memory(0), name); debug!("=> name({:?})", name_string); @@ -28,7 +29,7 @@ pub fn _getenv(name: u32, ctx: &mut Ctx) -> u32 { } /// emscripten: _setenv // (name: *const char, name: *const value, overwrite: int); -pub fn _setenv(name: u32, value: u32, overwrite: u32, ctx: &mut Ctx) -> c_int { +pub fn _setenv(ctx: &mut Ctx, name: u32, value: u32, overwrite: u32) -> c_int { debug!("emscripten::_setenv"); let name_addr = emscripten_memory_pointer!(ctx.memory(0), name); let value_addr = emscripten_memory_pointer!(ctx.memory(0), value); @@ -44,7 +45,7 @@ pub fn _setenv(name: u32, value: u32, overwrite: u32, ctx: &mut Ctx) -> c_int { } /// emscripten: _putenv // (name: *const char); -pub fn _putenv(name: c_int, ctx: &mut Ctx) -> c_int { +pub fn _putenv(ctx: &mut Ctx, name: c_int) -> c_int { debug!("emscripten::_putenv"); let name_addr = emscripten_memory_pointer!(ctx.memory(0), name) as *const c_char; @@ -54,7 +55,7 @@ pub fn _putenv(name: c_int, ctx: &mut Ctx) -> c_int { } /// emscripten: _unsetenv // (name: *const char); -pub fn _unsetenv(name: u32, ctx: &mut Ctx) -> c_int { +pub fn _unsetenv(ctx: &mut Ctx, name: u32) -> c_int { debug!("emscripten::_unsetenv"); let name_addr = emscripten_memory_pointer!(ctx.memory(0), name); let name = read_string_from_wasm(ctx.memory(0), name); @@ -67,7 +68,7 @@ pub fn _unsetenv(name: u32, ctx: &mut Ctx) -> c_int { } #[allow(clippy::cast_ptr_alignment)] -pub fn _getpwnam(name_ptr: c_int, ctx: &mut Ctx) -> c_int { +pub fn _getpwnam(ctx: &mut Ctx, name_ptr: c_int) -> c_int { debug!("emscripten::_getpwnam {}", name_ptr); #[repr(C)] @@ -83,7 +84,7 @@ pub fn _getpwnam(name_ptr: c_int, ctx: &mut Ctx) -> c_int { // stub this in windows as it is not valid unsafe { - let passwd_struct_offset = call_malloc(mem::size_of::() as _, ctx); + let passwd_struct_offset = call_malloc(ctx, mem::size_of::() as _); let passwd_struct_ptr = emscripten_memory_pointer!(ctx.memory(0), passwd_struct_offset) as *mut GuestPasswd; (*passwd_struct_ptr).pw_name = 0; @@ -99,7 +100,7 @@ pub fn _getpwnam(name_ptr: c_int, ctx: &mut Ctx) -> c_int { } #[allow(clippy::cast_ptr_alignment)] -pub fn _getgrnam(name_ptr: c_int, ctx: &mut Ctx) -> c_int { +pub fn _getgrnam(ctx: &mut Ctx, name_ptr: c_int) -> c_int { debug!("emscripten::_getgrnam {}", name_ptr); #[repr(C)] @@ -112,7 +113,7 @@ pub fn _getgrnam(name_ptr: c_int, ctx: &mut Ctx) -> c_int { // stub the group struct as it is not supported on windows unsafe { - let group_struct_offset = call_malloc(mem::size_of::() as _, ctx); + let group_struct_offset = call_malloc(ctx, mem::size_of::() as _); let group_struct_ptr = emscripten_memory_pointer!(ctx.memory(0), group_struct_offset) as *mut GuestGroup; (*group_struct_ptr).gr_name = 0; @@ -123,7 +124,7 @@ pub fn _getgrnam(name_ptr: c_int, ctx: &mut Ctx) -> c_int { } } -pub fn _sysconf(name: c_int, _ctx: &mut Ctx) -> c_long { +pub fn _sysconf(_ctx: &mut Ctx, name: c_int) -> c_long { debug!("emscripten::_sysconf {}", name); // stub because sysconf is not valid on windows 0 diff --git a/lib/emscripten/src/errno.rs b/lib/emscripten/src/errno.rs index 647932810..ff1d09b7f 100644 --- a/lib/emscripten/src/errno.rs +++ b/lib/emscripten/src/errno.rs @@ -1,8 +1,8 @@ // use std::collections::HashMap; use wasmer_runtime_core::vm::Ctx; -pub fn ___seterrno(value: i32, _ctx: &mut Ctx) { - debug!("emscripten::___seterrno {}", value); +pub fn ___seterrno(_ctx: &mut Ctx, _value: i32) { + debug!("emscripten::___seterrno {}", _value); // TODO: Incomplete impl eprintln!("failed to set errno!"); // value diff --git a/lib/emscripten/src/exception.rs b/lib/emscripten/src/exception.rs index b1814e107..b0aab78dd 100644 --- a/lib/emscripten/src/exception.rs +++ b/lib/emscripten/src/exception.rs @@ -3,14 +3,14 @@ use super::process::_abort; use wasmer_runtime_core::vm::Ctx; /// emscripten: ___cxa_allocate_exception -pub fn ___cxa_allocate_exception(size: u32, ctx: &mut Ctx) -> u32 { +pub fn ___cxa_allocate_exception(ctx: &mut Ctx, size: u32) -> u32 { debug!("emscripten::___cxa_allocate_exception"); - env::call_malloc(size as _, ctx) + env::call_malloc(ctx, size as _) } /// emscripten: ___cxa_throw /// TODO: We don't have support for exceptions yet -pub fn ___cxa_throw(_ptr: u32, _ty: u32, _destructor: u32, ctx: &mut Ctx) { +pub fn ___cxa_throw(ctx: &mut Ctx, _ptr: u32, _ty: u32, _destructor: u32) { debug!("emscripten::___cxa_throw"); _abort(ctx); } diff --git a/lib/emscripten/src/exec.rs b/lib/emscripten/src/exec.rs new file mode 100644 index 000000000..f47f09bf0 --- /dev/null +++ b/lib/emscripten/src/exec.rs @@ -0,0 +1,40 @@ +use libc::execvp as libc_execvp; +use std::cell::Cell; +use std::ffi::CString; +use wasmer_runtime_core::vm::Ctx; + +pub fn execvp(ctx: &mut Ctx, command_name_offset: u32, argv_offset: u32) -> i32 { + // a single reference to re-use + let emscripten_memory = ctx.memory(0); + + // read command name as string + let command_name_string_vec: Vec = emscripten_memory.view() + [(command_name_offset as usize)..] + .iter() + .map(|cell| cell.get()) + .take_while(|&byte| byte != 0) + .collect(); + let command_name_string = CString::new(command_name_string_vec).unwrap(); + + // get the array of args + let mut argv: Vec<*const i8> = emscripten_memory.view()[((argv_offset / 4) as usize)..] + .iter() + .map(|cell: &Cell| cell.get()) + .take_while(|&byte| byte != 0) + .map(|offset| { + let p: *const i8 = (emscripten_memory.view::()[(offset as usize)..]) + .iter() + .map(|cell| cell.as_ptr() as *const i8) + .collect::>()[0]; + p + }) + .collect(); + + // push a nullptr on to the end of the args array + argv.push(std::ptr::null()); + + // construct raw pointers and hand them to `execvp` + let command_pointer = command_name_string.as_ptr() as *const i8; + let args_pointer = argv.as_ptr(); + unsafe { libc_execvp(command_pointer, args_pointer) } +} diff --git a/lib/emscripten/src/exit.rs b/lib/emscripten/src/exit.rs new file mode 100644 index 000000000..d3f57a7a2 --- /dev/null +++ b/lib/emscripten/src/exit.rs @@ -0,0 +1,7 @@ +use wasmer_runtime_core::vm::Ctx; + +// __exit +pub fn exit(_ctx: &mut Ctx, value: i32) { + debug!("emscripten::exit {}", value); + ::std::process::exit(value); +} diff --git a/lib/emscripten/src/io/unix.rs b/lib/emscripten/src/io/unix.rs index a7c26a196..da3ddeff9 100644 --- a/lib/emscripten/src/io/unix.rs +++ b/lib/emscripten/src/io/unix.rs @@ -3,12 +3,12 @@ use libc::printf as _printf; use wasmer_runtime_core::vm::Ctx; /// putchar -pub fn putchar(chr: i32, ctx: &mut Ctx) { +pub fn putchar(_ctx: &mut Ctx, chr: i32) { unsafe { libc::putchar(chr) }; } /// printf -pub fn printf(memory_offset: i32, extra: i32, ctx: &mut Ctx) -> i32 { +pub fn printf(ctx: &mut Ctx, memory_offset: i32, extra: i32) -> i32 { debug!("emscripten::printf {}, {}", memory_offset, extra); unsafe { let addr = emscripten_memory_pointer!(ctx.memory(0), memory_offset) as _; diff --git a/lib/emscripten/src/io/windows.rs b/lib/emscripten/src/io/windows.rs index b85d9c54a..903d05900 100644 --- a/lib/emscripten/src/io/windows.rs +++ b/lib/emscripten/src/io/windows.rs @@ -15,12 +15,12 @@ use wasmer_runtime_core::vm::Ctx; //} /// putchar -pub fn putchar(chr: i32, ctx: &mut Ctx) { +pub fn putchar(ctx: &mut Ctx, chr: i32) { unsafe { libc::putchar(chr) }; } /// printf -pub fn printf(memory_offset: i32, extra: i32, ctx: &mut Ctx) -> i32 { +pub fn printf(ctx: &mut Ctx, memory_offset: i32, extra: i32) -> i32 { debug!("emscripten::printf {}, {}", memory_offset, extra); // unsafe { // let addr = emscripten_memory_pointer!(ctx.memory(0), memory_offset) as _; diff --git a/lib/emscripten/src/jmp.rs b/lib/emscripten/src/jmp.rs index 0c63593aa..c5b54f02b 100644 --- a/lib/emscripten/src/jmp.rs +++ b/lib/emscripten/src/jmp.rs @@ -4,7 +4,7 @@ use std::cell::UnsafeCell; use wasmer_runtime_core::vm::Ctx; /// setjmp -pub fn __setjmp(env_addr: u32, ctx: &mut Ctx) -> c_int { +pub fn __setjmp(ctx: &mut Ctx, env_addr: u32) -> c_int { debug!("emscripten::__setjmp (setjmp)"); unsafe { // Rather than using the env as the holder of the jump buffer pointer, @@ -15,7 +15,7 @@ pub fn __setjmp(env_addr: u32, ctx: &mut Ctx) -> c_int { let jump_buf: UnsafeCell<[u32; 27]> = UnsafeCell::new([0; 27]); let jumps = &mut get_emscripten_data(ctx).jumps; let result = setjmp(jump_buf.get() as _); - // We set the jump index to be the last value of jumps + // We set the jump index to be the last 3value of jumps *jump_index = jumps.len() as _; // We hold the reference of the jump buffer jumps.push(jump_buf); @@ -25,7 +25,7 @@ pub fn __setjmp(env_addr: u32, ctx: &mut Ctx) -> c_int { /// longjmp #[allow(unreachable_code)] -pub fn __longjmp(env_addr: u32, val: c_int, ctx: &mut Ctx) { +pub fn __longjmp(ctx: &mut Ctx, env_addr: u32, val: c_int) { debug!("emscripten::__longjmp (longmp)"); unsafe { // We retrieve the jump index from the env address diff --git a/lib/emscripten/src/lib.rs b/lib/emscripten/src/lib.rs index c8e98a985..1cde93620 100644 --- a/lib/emscripten/src/lib.rs +++ b/lib/emscripten/src/lib.rs @@ -1,6 +1,7 @@ #[macro_use] extern crate wasmer_runtime_core; +use lazy_static::lazy_static; use std::cell::UnsafeCell; use std::{f64, ffi::c_void}; use wasmer_runtime_core::{ @@ -11,11 +12,12 @@ use wasmer_runtime_core::{ import::ImportObject, imports, memory::Memory, + module::ImportName, table::Table, - types::{ElementType, MemoryDescriptor, TableDescriptor, Value}, + types::{ElementType, FuncSig, MemoryDescriptor, TableDescriptor, Type, Value}, units::Pages, vm::Ctx, - Func, Instance, Module, + Func, Instance, IsExport, Module, }; #[macro_use] @@ -25,9 +27,12 @@ mod file_descriptor; pub mod stdio; // EMSCRIPTEN APIS +mod emscripten_target; mod env; mod errno; mod exception; +mod exec; +mod exit; mod io; mod jmp; mod linking; @@ -51,11 +56,14 @@ pub use self::utils::{ // TODO: Magic number - how is this calculated? const TOTAL_STACK: u32 = 5_242_880; -// TODO: Magic number - how is this calculated? -const DYNAMICTOP_PTR_DIFF: u32 = 1088; // TODO: make this variable const STATIC_BUMP: u32 = 215_536; +lazy_static! { + static ref OLD_ABORT_ON_CANNOT_GROW_MEMORY_SIG: FuncSig = + { FuncSig::new(vec![], vec![Type::I32]) }; +} + // The address globals begin at. Very low in memory, for code size and optimization opportunities. // Above 0 is static memory, starting with globals. // Then the stack. @@ -63,30 +71,52 @@ const STATIC_BUMP: u32 = 215_536; const GLOBAL_BASE: u32 = 1024; const STATIC_BASE: u32 = GLOBAL_BASE; -fn stacktop(static_bump: u32) -> u32 { - align_memory(dynamictop_ptr(static_bump) + 4) -} - -fn stack_max(static_bump: u32) -> u32 { - stacktop(static_bump) + TOTAL_STACK -} - -fn dynamic_base(static_bump: u32) -> u32 { - align_memory(stack_max(static_bump)) -} - -fn dynamictop_ptr(static_bump: u32) -> u32 { - static_bump + DYNAMICTOP_PTR_DIFF -} - pub struct EmscriptenData<'a> { pub malloc: Func<'a, u32, u32>, pub free: Func<'a, u32>, 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>, + + // round 2 + pub dyn_call_dii: Option>, + pub dyn_call_diiii: Option>, + pub dyn_call_iiiii: Option>, + pub dyn_call_iiiiii: Option>, + pub dyn_call_vd: Option>, + pub dyn_call_viiiii: Option>, + pub dyn_call_viiiiii: Option>, + pub dyn_call_viiiiiii: Option>, + pub dyn_call_viiiiiiii: Option>, + pub dyn_call_viiiiiiiii: Option>, + pub dyn_call_iiji: Option>, + pub dyn_call_j: Option>, + pub dyn_call_ji: Option>, + pub dyn_call_jij: Option>, + pub dyn_call_jjj: Option>, + pub dyn_call_viiij: Option>, + pub dyn_call_viiijiiii: Option>, + pub dyn_call_viiijiiiiii: + Option>, + pub dyn_call_viij: Option>, + pub dyn_call_viiji: Option>, + pub dyn_call_viijiii: Option>, + pub dyn_call_viijj: Option>, + pub dyn_call_vij: Option>, + pub dyn_call_viji: Option>, + pub dyn_call_vijiii: Option>, + pub dyn_call_vijj: Option>, } impl<'a> EmscriptenData<'a> { @@ -101,6 +131,44 @@ 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(); + + // round 2 + let dyn_call_dii = instance.func("dynCall_dii").ok(); + let dyn_call_diiii = instance.func("dynCall_diiii").ok(); + let dyn_call_iiiii = instance.func("dynCall_iiiii").ok(); + let dyn_call_iiiiii = instance.func("dynCall_iiiiii").ok(); + let dyn_call_vd = instance.func("dynCall_vd").ok(); + let dyn_call_viiiii = instance.func("dynCall_viiiii").ok(); + let dyn_call_viiiiii = instance.func("dynCall_viiiiii").ok(); + let dyn_call_viiiiiii = instance.func("dynCall_viiiiiii").ok(); + let dyn_call_viiiiiiii = instance.func("dynCall_viiiiiiii").ok(); + let dyn_call_viiiiiiiii = instance.func("dynCall_viiiiiiiii").ok(); + let dyn_call_iiji = instance.func("dynCall_iiji").ok(); + let dyn_call_j = instance.func("dynCall_j").ok(); + let dyn_call_ji = instance.func("dynCall_ji").ok(); + let dyn_call_jij = instance.func("dynCall_jij").ok(); + let dyn_call_jjj = instance.func("dynCall_jjj").ok(); + let dyn_call_viiij = instance.func("dynCall_viiij").ok(); + let dyn_call_viiijiiii = instance.func("dynCall_viiijiiii").ok(); + let dyn_call_viiijiiiiii = instance.func("dynCall_viiijiiiiii").ok(); + let dyn_call_viij = instance.func("dynCall_viij").ok(); + let dyn_call_viiji = instance.func("dynCall_viiji").ok(); + let dyn_call_viijiii = instance.func("dynCall_viijiii").ok(); + let dyn_call_viijj = instance.func("dynCall_viijj").ok(); + let dyn_call_vij = instance.func("dynCall_vij").ok(); + let dyn_call_viji = instance.func("dynCall_viji").ok(); + let dyn_call_vijiii = instance.func("dynCall_vijiii").ok(); + let dyn_call_vijj = instance.func("dynCall_vijj").ok(); + EmscriptenData { malloc, free, @@ -108,6 +176,43 @@ 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, + + // round 2 + dyn_call_dii, + dyn_call_diiii, + dyn_call_iiiii, + dyn_call_iiiiii, + dyn_call_vd, + dyn_call_viiiii, + dyn_call_viiiiii, + dyn_call_viiiiiii, + dyn_call_viiiiiiii, + dyn_call_viiiiiiiii, + dyn_call_iiji, + dyn_call_j, + dyn_call_ji, + dyn_call_jij, + dyn_call_jjj, + dyn_call_viiij, + dyn_call_viiijiiii, + dyn_call_viiijiiiiii, + dyn_call_viij, + dyn_call_viiji, + dyn_call_viijiii, + dyn_call_viijj, + dyn_call_vij, + dyn_call_viji, + dyn_call_vijiii, + dyn_call_vijj, } } } @@ -132,7 +237,7 @@ pub fn run_emscripten_instance( let num_params = main_func.signature().params().len(); let _result = match num_params { 2 => { - let (argc, argv) = store_module_arguments(path, args, instance.context_mut()); + let (argc, argv) = store_module_arguments(instance.context_mut(), path, args); instance.call("_main", &[Value::I32(argc as i32), Value::I32(argv as i32)])?; } 0 => { @@ -149,17 +254,17 @@ pub fn run_emscripten_instance( Ok(()) } -fn store_module_arguments(path: &str, args: Vec<&str>, ctx: &mut Ctx) -> (u32, u32) { +fn store_module_arguments(ctx: &mut Ctx, path: &str, args: Vec<&str>) -> (u32, u32) { let argc = args.len() + 1; let mut args_slice = vec![0; argc]; - args_slice[0] = unsafe { allocate_cstr_on_stack(path, ctx).0 }; + args_slice[0] = unsafe { allocate_cstr_on_stack(ctx, path).0 }; for (slot, arg) in args_slice[1..argc].iter_mut().zip(args.iter()) { - *slot = unsafe { allocate_cstr_on_stack(&arg, ctx).0 }; + *slot = unsafe { allocate_cstr_on_stack(ctx, &arg).0 }; } let (argv_offset, argv_slice): (_, &mut [u32]) = - unsafe { allocate_on_stack(((argc + 1) * 4) as u32, ctx) }; + unsafe { allocate_on_stack(ctx, ((argc + 1) * 4) as u32) }; assert!(!argv_slice.is_empty()); for (slot, arg) in argv_slice[0..argc].iter_mut().zip(args_slice.iter()) { *slot = *arg @@ -187,10 +292,7 @@ pub struct EmscriptenGlobalsData { memory_base: u32, table_base: u32, temp_double_ptr: u32, - - // Global namespace - infinity: f64, - nan: f64, + use_old_abort_on_cannot_grow_memory: bool, } pub struct EmscriptenGlobals { @@ -205,6 +307,27 @@ pub struct EmscriptenGlobals { impl EmscriptenGlobals { pub fn new(module: &Module /*, static_bump: u32 */) -> Self { + let mut use_old_abort_on_cannot_grow_memory = false; + for ( + index, + ImportName { + namespace_index, + name_index, + }, + ) in &module.info().imported_functions + { + let namespace = module.info().namespace_table.get(*namespace_index); + let name = module.info().name_table.get(*name_index); + if name == "abortOnCannotGrowMemory" && namespace == "env" { + let sig_index = module.info().func_assoc[index.convert_up(module.info())]; + let expected_sig = &module.info().signatures[sig_index]; + if *expected_sig == *OLD_ABORT_ON_CANNOT_GROW_MEMORY_SIG { + use_old_abort_on_cannot_grow_memory = true; + } + break; + } + } + let (table_min, table_max) = get_emscripten_table_size(&module); let (memory_min, memory_max) = get_emscripten_memory_size(&module); @@ -221,22 +344,22 @@ impl EmscriptenGlobals { minimum: table_min, maximum: table_max, }; - let mut table = Table::new(table_type).unwrap(); + let table = Table::new(table_type).unwrap(); let data = { let static_bump = STATIC_BUMP; - let mut STATIC_TOP = STATIC_BASE + static_bump; + let mut static_top = STATIC_BASE + static_bump; let memory_base = STATIC_BASE; let table_base = 0; - let temp_double_ptr = STATIC_TOP; - STATIC_TOP += 16; + let temp_double_ptr = static_top; + static_top += 16; - let dynamictop_ptr = static_alloc(&mut STATIC_TOP, 4); + let dynamictop_ptr = static_alloc(&mut static_top, 4); - let stacktop = align_memory(STATIC_TOP); + let stacktop = align_memory(static_top); let stack_max = stacktop + TOTAL_STACK; EmscriptenGlobalsData { @@ -247,9 +370,7 @@ impl EmscriptenGlobals { memory_base, table_base, temp_double_ptr, - - infinity: std::f64::INFINITY, - nan: std::f64::NAN, + use_old_abort_on_cannot_grow_memory, } }; @@ -266,6 +387,12 @@ impl EmscriptenGlobals { } pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject { + let abort_on_cannot_grow_memory_export = if globals.data.use_old_abort_on_cannot_grow_memory { + func!(crate::memory::abort_on_cannot_grow_memory_old).to_export() + } else { + func!(crate::memory::abort_on_cannot_grow_memory).to_export() + }; + imports! { "env" => { "memory" => Export::Memory(globals.memory.clone()), @@ -289,6 +416,12 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject "___unlock" => func!(crate::lock::___unlock), "___wait" => func!(crate::lock::___wait), + // exec + "_execvp" => func!(crate::exec::execvp), + + // exit + "__exit" => func!(crate::exit::exit), + // Env "___assert_fail" => func!(crate::env::___assert_fail), "_getenv" => func!(crate::env::_getenv), @@ -331,6 +464,7 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject "___syscall39" => func!(crate::syscalls::___syscall39), "___syscall38" => func!(crate::syscalls::___syscall38), "___syscall40" => func!(crate::syscalls::___syscall40), + "___syscall42" => func!(crate::syscalls::___syscall42), "___syscall54" => func!(crate::syscalls::___syscall54), "___syscall57" => func!(crate::syscalls::___syscall57), "___syscall60" => func!(crate::syscalls::___syscall60), @@ -352,6 +486,7 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject "___syscall168" => func!(crate::syscalls::___syscall168), "___syscall180" => func!(crate::syscalls::___syscall180), "___syscall181" => func!(crate::syscalls::___syscall181), + "___syscall183" => func!(crate::syscalls::___syscall183), "___syscall191" => func!(crate::syscalls::___syscall191), "___syscall192" => func!(crate::syscalls::___syscall192), "___syscall194" => func!(crate::syscalls::___syscall194), @@ -409,8 +544,10 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject "_sigsuspend" => func!(crate::signal::_sigsuspend), // Memory - "abortOnCannotGrowMemory" => func!(crate::memory::abort_on_cannot_grow_memory), + "abortOnCannotGrowMemory" => abort_on_cannot_grow_memory_export, "_emscripten_memcpy_big" => func!(crate::memory::_emscripten_memcpy_big), + "_emscripten_get_heap_size" => func!(crate::memory::_emscripten_get_heap_size), + "_emscripten_resize_heap" => func!(crate::memory::_emscripten_resize_heap), "enlargeMemory" => func!(crate::memory::enlarge_memory), "getTotalMemory" => func!(crate::memory::get_total_memory), "___map_file" => func!(crate::memory::___map_file), @@ -453,6 +590,90 @@ 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), + // round 2 + "nullFunc_dii" => func!(crate::emscripten_target::nullFunc_dii), + "nullFunc_diiii" => func!(crate::emscripten_target::nullFunc_diiii), + "nullFunc_iiji" => func!(crate::emscripten_target::nullFunc_iiji), + "nullFunc_j" => func!(crate::emscripten_target::nullFunc_j), + "nullFunc_jij" => func!(crate::emscripten_target::nullFunc_jij), + "nullFunc_jjj" => func!(crate::emscripten_target::nullFunc_jjj), + "nullFunc_vd" => func!(crate::emscripten_target::nullFunc_vd), + "nullFunc_viiiiiii" => func!(crate::emscripten_target::nullFunc_viiiiiii), + "nullFunc_viiiiiiii" => func!(crate::emscripten_target::nullFunc_viiiiiiii), + "nullFunc_viiiiiiiii" => func!(crate::emscripten_target::nullFunc_viiiiiiiii), + "nullFunc_viiij" => func!(crate::emscripten_target::nullFunc_viiij), + "nullFunc_viiijiiii" => func!(crate::emscripten_target::nullFunc_viiijiiii), + "nullFunc_viiijiiiiii" => func!(crate::emscripten_target::nullFunc_viiijiiiiii), + "nullFunc_viij" => func!(crate::emscripten_target::nullFunc_viij), + "nullFunc_viiji" => func!(crate::emscripten_target::nullFunc_viiji), + "nullFunc_viijiii" => func!(crate::emscripten_target::nullFunc_viijiii), + "nullFunc_viijj" => func!(crate::emscripten_target::nullFunc_viijj), + "nullFunc_vij" => func!(crate::emscripten_target::nullFunc_vij), + "nullFunc_viji" => func!(crate::emscripten_target::nullFunc_viji), + "nullFunc_vijiii" => func!(crate::emscripten_target::nullFunc_vijiii), + "nullFunc_vijj" => func!(crate::emscripten_target::nullFunc_vijj), + "invoke_dii" => func!(crate::emscripten_target::invoke_dii), + "invoke_diiii" => func!(crate::emscripten_target::invoke_diiii), + "invoke_iiiii" => func!(crate::emscripten_target::invoke_iiiii), + "invoke_iiiiii" => func!(crate::emscripten_target::invoke_iiiiii), + "invoke_vd" => func!(crate::emscripten_target::invoke_vd), + "invoke_viiiii" => func!(crate::emscripten_target::invoke_viiiii), + "invoke_viiiiii" => func!(crate::emscripten_target::invoke_viiiiii), + "invoke_viiiiiii" => func!(crate::emscripten_target::invoke_viiiiiii), + "invoke_viiiiiiii" => func!(crate::emscripten_target::invoke_viiiiiiii), + "invoke_viiiiiiiii" => func!(crate::emscripten_target::invoke_viiiiiiiii), + "invoke_iiji" => func!(crate::emscripten_target::invoke_iiji), + "invoke_j" => func!(crate::emscripten_target::invoke_j), + "invoke_ji" => func!(crate::emscripten_target::invoke_ji), + "invoke_jij" => func!(crate::emscripten_target::invoke_jij), + "invoke_jjj" => func!(crate::emscripten_target::invoke_jjj), + "invoke_viiij" => func!(crate::emscripten_target::invoke_viiij), + "invoke_viiijiiii" => func!(crate::emscripten_target::invoke_viiijiiii), + "invoke_viiijiiiiii" => func!(crate::emscripten_target::invoke_viiijiiiiii), + "invoke_viij" => func!(crate::emscripten_target::invoke_viij), + "invoke_viiji" => func!(crate::emscripten_target::invoke_viiji), + "invoke_viijiii" => func!(crate::emscripten_target::invoke_viijiii), + "invoke_viijj" => func!(crate::emscripten_target::invoke_viijj), + "invoke_vij" => func!(crate::emscripten_target::invoke_vij), + "invoke_viji" => func!(crate::emscripten_target::invoke_viji), + "invoke_vijiii" => func!(crate::emscripten_target::invoke_vijiii), + "invoke_vijj" => func!(crate::emscripten_target::invoke_vijj), }, "global" => { "NaN" => Global::new(Value::F64(f64::NAN)), diff --git a/lib/emscripten/src/linking.rs b/lib/emscripten/src/linking.rs index e61b9d1b1..831712e91 100644 --- a/lib/emscripten/src/linking.rs +++ b/lib/emscripten/src/linking.rs @@ -3,19 +3,19 @@ use wasmer_runtime_core::vm::Ctx; // TODO: Need to implement. /// emscripten: dlopen(filename: *const c_char, flag: c_int) -> *mut c_void -pub fn _dlopen(_filename: u32, _flag: u32, _ctx: &mut Ctx) -> i32 { +pub fn _dlopen(_ctx: &mut Ctx, _filename: u32, _flag: u32) -> i32 { debug!("emscripten::_dlopen"); -1 } /// emscripten: dlclose(handle: *mut c_void) -> c_int -pub fn _dlclose(_filename: u32, _ctx: &mut Ctx) -> i32 { +pub fn _dlclose(_ctx: &mut Ctx, _filename: u32) -> i32 { debug!("emscripten::_dlclose"); -1 } /// emscripten: dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void -pub fn _dlsym(_filepath: u32, _symbol: u32, _ctx: &mut Ctx) -> i32 { +pub fn _dlsym(_ctx: &mut Ctx, _filepath: u32, _symbol: u32) -> i32 { debug!("emscripten::_dlsym"); -1 } diff --git a/lib/emscripten/src/lock.rs b/lib/emscripten/src/lock.rs index 005a1baed..badb47a1b 100644 --- a/lib/emscripten/src/lock.rs +++ b/lib/emscripten/src/lock.rs @@ -2,16 +2,16 @@ use libc::c_int; use wasmer_runtime_core::vm::Ctx; // NOTE: Not implemented by Emscripten -pub fn ___lock(what: c_int, _ctx: &mut Ctx) { - debug!("emscripten::___lock {}", what); +pub fn ___lock(_ctx: &mut Ctx, _what: c_int) { + debug!("emscripten::___lock {}", _what); } // NOTE: Not implemented by Emscripten -pub fn ___unlock(what: c_int, _ctx: &mut Ctx) { - debug!("emscripten::___unlock {}", what); +pub fn ___unlock(_ctx: &mut Ctx, _what: c_int) { + debug!("emscripten::___unlock {}", _what); } // NOTE: Not implemented by Emscripten -pub fn ___wait(_which: u32, _varargs: u32, _three: u32, _four: u32, _ctx: &mut Ctx) { +pub fn ___wait(_ctx: &mut Ctx, _which: u32, _varargs: u32, _three: u32, _four: u32) { debug!("emscripten::___wait"); } diff --git a/lib/emscripten/src/math.rs b/lib/emscripten/src/math.rs index 16f3ed419..a60966a1c 100644 --- a/lib/emscripten/src/math.rs +++ b/lib/emscripten/src/math.rs @@ -1,23 +1,23 @@ use wasmer_runtime_core::vm::Ctx; /// emscripten: _llvm_log10_f64 -pub fn _llvm_log10_f64(value: f64, _ctx: &mut Ctx) -> f64 { +pub fn _llvm_log10_f64(_ctx: &mut Ctx, value: f64) -> f64 { debug!("emscripten::_llvm_log10_f64"); value.log10() } /// emscripten: _llvm_log2_f64 -pub fn _llvm_log2_f64(value: f64, _ctx: &mut Ctx) -> f64 { +pub fn _llvm_log2_f64(_ctx: &mut Ctx, value: f64) -> f64 { debug!("emscripten::_llvm_log2_f64"); value.log2() } -pub fn _llvm_log10_f32(_value: f64, _ctx: &mut Ctx) -> f64 { +pub fn _llvm_log10_f32(_ctx: &mut Ctx, _value: f64) -> f64 { debug!("emscripten::_llvm_log10_f32"); -1.0 } -pub fn _llvm_log2_f32(_value: f64, _ctx: &mut Ctx) -> f64 { +pub fn _llvm_log2_f32(_ctx: &mut Ctx, _value: f64) -> f64 { debug!("emscripten::_llvm_log10_f32"); -1.0 } @@ -28,12 +28,12 @@ pub fn _emscripten_random(_ctx: &mut Ctx) -> f64 { } // emscripten: f64-rem -pub fn f64_rem(x: f64, y: f64, _ctx: &mut Ctx) -> f64 { +pub fn f64_rem(_ctx: &mut Ctx, x: f64, y: f64) -> f64 { debug!("emscripten::f64-rem"); x % y } // emscripten: global.Math pow -pub fn pow(x: f64, y: f64, _ctx: &mut Ctx) -> f64 { +pub fn pow(_ctx: &mut Ctx, x: f64, y: f64) -> f64 { x.powf(y) } diff --git a/lib/emscripten/src/memory.rs b/lib/emscripten/src/memory.rs index e174e023d..02877a011 100644 --- a/lib/emscripten/src/memory.rs +++ b/lib/emscripten/src/memory.rs @@ -3,7 +3,7 @@ use libc::{c_int, c_void, memcpy, size_t}; use wasmer_runtime_core::vm::Ctx; /// emscripten: _emscripten_memcpy_big -pub fn _emscripten_memcpy_big(dest: u32, src: u32, len: u32, ctx: &mut Ctx) -> u32 { +pub fn _emscripten_memcpy_big(ctx: &mut Ctx, dest: u32, src: u32, len: u32) -> u32 { debug!( "emscripten::_emscripten_memcpy_big {}, {}, {}", dest, src, len @@ -16,6 +16,20 @@ pub fn _emscripten_memcpy_big(dest: u32, src: u32, len: u32, ctx: &mut Ctx) -> u dest } +/// emscripten: _emscripten_get_heap_size +pub fn _emscripten_get_heap_size(_ctx: &mut Ctx) -> u32 { + debug!("emscripten::_emscripten_get_heap_size",); + // TODO: Fix implementation + 16_777_216 +} + +/// emscripten: _emscripten_resize_heap +pub fn _emscripten_resize_heap(_ctx: &mut Ctx, _requested_size: u32) -> u32 { + debug!("emscripten::_emscripten_resize_heap {}", _requested_size); + // TODO: Fix implementation + 0 +} + /// emscripten: getTotalMemory pub fn get_total_memory(_ctx: &mut Ctx) -> u32 { debug!("emscripten::get_total_memory"); @@ -33,14 +47,24 @@ pub fn enlarge_memory(_ctx: &mut Ctx) -> u32 { } /// emscripten: abortOnCannotGrowMemory -pub fn abort_on_cannot_grow_memory(ctx: &mut Ctx) -> u32 { +pub fn abort_on_cannot_grow_memory(ctx: &mut Ctx, _requested_size: u32) -> u32 { + debug!( + "emscripten::abort_on_cannot_grow_memory {}", + _requested_size + ); + abort_with_message(ctx, "Cannot enlarge memory arrays!"); + 0 +} + +/// emscripten: abortOnCannotGrowMemory +pub fn abort_on_cannot_grow_memory_old(ctx: &mut Ctx) -> u32 { debug!("emscripten::abort_on_cannot_grow_memory"); - abort_with_message("Cannot enlarge memory arrays!", ctx); + abort_with_message(ctx, "Cannot enlarge memory arrays!"); 0 } /// emscripten: ___map_file -pub fn ___map_file(_one: u32, _two: u32, _ctx: &mut Ctx) -> c_int { +pub fn ___map_file(_ctx: &mut Ctx, _one: u32, _two: u32) -> c_int { debug!("emscripten::___map_file"); // NOTE: TODO: Em returns -1 here as well. May need to implement properly -1 diff --git a/lib/emscripten/src/nullfunc.rs b/lib/emscripten/src/nullfunc.rs index 253edf8f6..8f289599b 100644 --- a/lib/emscripten/src/nullfunc.rs +++ b/lib/emscripten/src/nullfunc.rs @@ -1,67 +1,67 @@ use super::process::abort_with_message; use wasmer_runtime_core::vm::Ctx; -pub fn nullfunc_i(x: u32, ctx: &mut Ctx) { - debug!("emscripten::nullfunc_i {}", x); - abort_with_message("Invalid function pointer called with signature 'i'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)", ctx); +pub fn nullfunc_i(ctx: &mut Ctx, _x: u32) { + debug!("emscripten::nullfunc_i {}", _x); + abort_with_message(ctx, "Invalid function pointer called with signature 'i'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)"); } -pub fn nullfunc_ii(x: u32, ctx: &mut Ctx) { - debug!("emscripten::nullfunc_ii {}", x); - abort_with_message("Invalid function pointer called with signature 'ii'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)", ctx); +pub fn nullfunc_ii(ctx: &mut Ctx, _x: u32) { + debug!("emscripten::nullfunc_ii {}", _x); + abort_with_message(ctx, "Invalid function pointer called with signature 'ii'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)"); } -pub fn nullfunc_iii(x: u32, ctx: &mut Ctx) { - debug!("emscripten::nullfunc_iii {}", x); - abort_with_message("Invalid function pointer called with signature 'iii'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)", ctx); +pub fn nullfunc_iii(ctx: &mut Ctx, _x: u32) { + debug!("emscripten::nullfunc_iii {}", _x); + abort_with_message(ctx, "Invalid function pointer called with signature 'iii'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)"); } -pub fn nullfunc_iiii(x: u32, ctx: &mut Ctx) { - debug!("emscripten::nullfunc_iiii {}", x); - abort_with_message("Invalid function pointer called with signature 'iiii'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)", ctx); +pub fn nullfunc_iiii(ctx: &mut Ctx, _x: u32) { + debug!("emscripten::nullfunc_iiii {}", _x); + abort_with_message(ctx, "Invalid function pointer called with signature 'iiii'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)"); } -pub fn nullfunc_iiiii(x: u32, ctx: &mut Ctx) { - debug!("emscripten::nullfunc_iiiii {}", x); - abort_with_message("Invalid function pointer called with signature 'iiiii'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)", ctx); +pub fn nullfunc_iiiii(ctx: &mut Ctx, _x: u32) { + debug!("emscripten::nullfunc_iiiii {}", _x); + abort_with_message(ctx, "Invalid function pointer called with signature 'iiiii'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)"); } -pub fn nullfunc_iiiiii(x: u32, ctx: &mut Ctx) { - debug!("emscripten::nullfunc_iiiiii {}", x); - abort_with_message("Invalid function pointer called with signature 'iiiiii'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)", ctx); +pub fn nullfunc_iiiiii(ctx: &mut Ctx, _x: u32) { + debug!("emscripten::nullfunc_iiiiii {}", _x); + abort_with_message(ctx, "Invalid function pointer called with signature 'iiiiii'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)"); } -pub fn nullfunc_v(x: u32, ctx: &mut Ctx) { - debug!("emscripten::nullfunc_v {}", x); - abort_with_message("Invalid function pointer called with signature 'v'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)", ctx); +pub fn nullfunc_v(ctx: &mut Ctx, _x: u32) { + debug!("emscripten::nullfunc_v {}", _x); + abort_with_message(ctx, "Invalid function pointer called with signature 'v'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)"); } -pub fn nullfunc_vi(x: u32, ctx: &mut Ctx) { - debug!("emscripten::nullfunc_vi {}", x); - abort_with_message("Invalid function pointer called with signature 'vi'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)", ctx); +pub fn nullfunc_vi(ctx: &mut Ctx, _x: u32) { + debug!("emscripten::nullfunc_vi {}", _x); + abort_with_message(ctx, "Invalid function pointer called with signature 'vi'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)"); } -pub fn nullfunc_vii(x: u32, ctx: &mut Ctx) { - debug!("emscripten::nullfunc_vii {}", x); - abort_with_message("Invalid function pointer called with signature 'vii'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)", ctx); +pub fn nullfunc_vii(ctx: &mut Ctx, _x: u32) { + debug!("emscripten::nullfunc_vii {}", _x); + abort_with_message(ctx, "Invalid function pointer called with signature 'vii'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)"); } -pub fn nullfunc_viii(x: u32, ctx: &mut Ctx) { - debug!("emscripten::nullfunc_viii {}", x); - abort_with_message("Invalid function pointer called with signature 'viii'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)", ctx); +pub fn nullfunc_viii(ctx: &mut Ctx, _x: u32) { + debug!("emscripten::nullfunc_viii {}", _x); + abort_with_message(ctx, "Invalid function pointer called with signature 'viii'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)"); } -pub fn nullfunc_viiii(x: u32, ctx: &mut Ctx) { - debug!("emscripten::nullfunc_viiii {}", x); - abort_with_message("Invalid function pointer called with signature 'viiii'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)", ctx); +pub fn nullfunc_viiii(ctx: &mut Ctx, _x: u32) { + debug!("emscripten::nullfunc_viiii {}", _x); + abort_with_message(ctx, "Invalid function pointer called with signature 'viiii'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)"); } -pub fn nullfunc_viiiii(_x: u32, ctx: &mut Ctx) { +pub fn nullfunc_viiiii(ctx: &mut Ctx, _x: u32) { debug!("emscripten::nullfunc_viiiii"); - abort_with_message("Invalid function pointer called with signature 'viiiii'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)", ctx); + abort_with_message(ctx, "Invalid function pointer called with signature 'viiiii'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)"); } -pub fn nullfunc_viiiiii(_x: u32, ctx: &mut Ctx) { +pub fn nullfunc_viiiiii(ctx: &mut Ctx, _x: u32) { debug!("emscripten::nullfunc_viiiiii"); - abort_with_message("Invalid function pointer called with signature 'viiiiii'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)", ctx); + abort_with_message(ctx, "Invalid function pointer called with signature 'viiiiii'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)"); } diff --git a/lib/emscripten/src/process.rs b/lib/emscripten/src/process.rs index d6be7a78b..2dbf90ca5 100644 --- a/lib/emscripten/src/process.rs +++ b/lib/emscripten/src/process.rs @@ -9,7 +9,7 @@ type pid_t = c_int; use std::ffi::CStr; use wasmer_runtime_core::vm::Ctx; -pub fn abort_with_message(message: &str, ctx: &mut Ctx) { +pub fn abort_with_message(ctx: &mut Ctx, message: &str) { debug!("emscripten::abort_with_message"); println!("{}", message); _abort(ctx); @@ -34,19 +34,19 @@ pub fn _endgrent(_ctx: &mut Ctx) { debug!("emscripten::_endgrent"); } -pub fn _execve(_one: i32, _two: i32, _three: i32, _ctx: &mut Ctx) -> i32 { +pub fn _execve(_ctx: &mut Ctx, _one: i32, _two: i32, _three: i32) -> i32 { debug!("emscripten::_execve"); -1 } #[allow(unreachable_code)] -pub fn _exit(status: c_int, _ctx: &mut Ctx) { +pub fn _exit(_ctx: &mut Ctx, status: c_int) { // -> ! debug!("emscripten::_exit {}", status); unsafe { exit(status) } } -pub fn em_abort(message: u32, ctx: &mut Ctx) { +pub fn em_abort(ctx: &mut Ctx, message: u32) { debug!("emscripten::em_abort {}", message); let message_addr = emscripten_memory_pointer!(ctx.memory(0), message) as *mut c_char; unsafe { @@ -54,11 +54,11 @@ pub fn em_abort(message: u32, ctx: &mut Ctx) { .to_str() .unwrap_or("Unexpected abort"); - abort_with_message(message, ctx); + abort_with_message(ctx, message); } } -pub fn _kill(_one: i32, _two: i32, _ctx: &mut Ctx) -> i32 { +pub fn _kill(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::_kill"); -1 } @@ -73,32 +73,32 @@ pub fn _llvm_stacksave(_ctx: &mut Ctx) -> i32 { -1 } -pub fn _llvm_stackrestore(_one: i32, _ctx: &mut Ctx) { +pub fn _llvm_stackrestore(_ctx: &mut Ctx, _one: i32) { debug!("emscripten::_llvm_stackrestore"); } -pub fn _raise(_one: i32, _ctx: &mut Ctx) -> i32 { +pub fn _raise(_ctx: &mut Ctx, _one: i32) -> i32 { debug!("emscripten::_raise"); -1 } -pub fn _sem_init(_one: i32, _two: i32, _three: i32, _ctx: &mut Ctx) -> i32 { +pub fn _sem_init(_ctx: &mut Ctx, _one: i32, _two: i32, _three: i32) -> i32 { debug!("emscripten::_sem_init"); -1 } -pub fn _sem_post(_one: i32, _ctx: &mut Ctx) -> i32 { +pub fn _sem_post(_ctx: &mut Ctx, _one: i32) -> i32 { debug!("emscripten::_sem_post"); -1 } -pub fn _sem_wait(_one: i32, _ctx: &mut Ctx) -> i32 { +pub fn _sem_wait(_ctx: &mut Ctx, _one: i32) -> i32 { debug!("emscripten::_sem_post"); -1 } #[allow(clippy::cast_ptr_alignment)] -pub fn _getgrent(ctx: &mut Ctx) -> c_int { +pub fn _getgrent(_ctx: &mut Ctx) -> c_int { debug!("emscripten::_getgrent"); -1 } @@ -107,53 +107,53 @@ pub fn _setgrent(_ctx: &mut Ctx) { debug!("emscripten::_setgrent"); } -pub fn _setgroups(_one: i32, _two: i32, _ctx: &mut Ctx) -> i32 { +pub fn _setgroups(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::_setgroups"); -1 } -pub fn _setitimer(_one: i32, _two: i32, _three: i32, _ctx: &mut Ctx) -> i32 { +pub fn _setitimer(_ctx: &mut Ctx, _one: i32, _two: i32, _three: i32) -> i32 { debug!("emscripten::_setitimer"); -1 } -pub fn _usleep(_one: i32, _ctx: &mut Ctx) -> i32 { +pub fn _usleep(_ctx: &mut Ctx, _one: i32) -> i32 { debug!("emscripten::_usleep"); -1 } -pub fn _utimes(_one: i32, _two: i32, _ctx: &mut Ctx) -> i32 { +pub fn _utimes(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::_utimes"); -1 } -pub fn _waitpid(_one: i32, _two: i32, _three: i32, _ctx: &mut Ctx) -> i32 { +pub fn _waitpid(_ctx: &mut Ctx, _one: i32, _two: i32, _three: i32) -> i32 { debug!("emscripten::_waitpid"); -1 } -pub fn abort_stack_overflow(_what: c_int, ctx: &mut Ctx) { +pub fn abort_stack_overflow(ctx: &mut Ctx, _what: c_int) { debug!("emscripten::abort_stack_overflow"); // TODO: Message incomplete. Need to finish em runtime data first abort_with_message( - "Stack overflow! Attempted to allocate some bytes on the stack", ctx, + "Stack overflow! Attempted to allocate some bytes on the stack", ); } pub fn _llvm_trap(ctx: &mut Ctx) { debug!("emscripten::_llvm_trap"); - abort_with_message("abort!", ctx); + abort_with_message(ctx, "abort!"); } -pub fn _system(_one: i32, _ctx: &mut Ctx) -> c_int { +pub fn _system(_ctx: &mut Ctx, _one: i32) -> c_int { debug!("emscripten::_system"); // TODO: May need to change this Em impl to a working version eprintln!("Can't call external programs"); return EAGAIN; } -pub fn _popen(_one: i32, _two: i32, _ctx: &mut Ctx) -> c_int { +pub fn _popen(_ctx: &mut Ctx, _one: i32, _two: i32) -> c_int { debug!("emscripten::_popen"); // TODO: May need to change this Em impl to a working version eprintln!("Missing function: popen"); diff --git a/lib/emscripten/src/signal.rs b/lib/emscripten/src/signal.rs index 1a6d43078..37c3ed8df 100644 --- a/lib/emscripten/src/signal.rs +++ b/lib/emscripten/src/signal.rs @@ -2,7 +2,7 @@ use wasmer_runtime_core::vm::Ctx; #[allow(clippy::cast_ptr_alignment)] -pub fn _sigemptyset(set: u32, ctx: &mut Ctx) -> i32 { +pub fn _sigemptyset(ctx: &mut Ctx, set: u32) -> i32 { debug!("emscripten::_sigemptyset"); let set_addr = emscripten_memory_pointer!(ctx.memory(0), set) as *mut u32; unsafe { @@ -11,13 +11,13 @@ pub fn _sigemptyset(set: u32, ctx: &mut Ctx) -> i32 { 0 } -pub fn _sigaction(signum: u32, act: u32, oldact: u32, _ctx: &mut Ctx) -> i32 { - debug!("emscripten::_sigaction {}, {}, {}", signum, act, oldact); +pub fn _sigaction(_ctx: &mut Ctx, _signum: u32, _act: u32, _oldact: u32) -> i32 { + debug!("emscripten::_sigaction {}, {}, {}", _signum, _act, _oldact); 0 } #[allow(clippy::cast_ptr_alignment)] -pub fn _sigaddset(set: u32, signum: u32, ctx: &mut Ctx) -> i32 { +pub fn _sigaddset(ctx: &mut Ctx, set: u32, signum: u32) -> i32 { debug!("emscripten::_sigaddset {}, {}", set, signum); let set_addr = emscripten_memory_pointer!(ctx.memory(0), set) as *mut u32; unsafe { @@ -26,17 +26,17 @@ pub fn _sigaddset(set: u32, signum: u32, ctx: &mut Ctx) -> i32 { 0 } -pub fn _sigsuspend(_one: i32, _ctx: &mut Ctx) -> i32 { +pub fn _sigsuspend(_ctx: &mut Ctx, _one: i32) -> i32 { debug!("emscripten::_sigsuspend"); -1 } -pub fn _sigprocmask(_one: i32, _two: i32, _three: i32, _ctx: &mut Ctx) -> i32 { +pub fn _sigprocmask(_ctx: &mut Ctx, _one: i32, _two: i32, _three: i32) -> i32 { debug!("emscripten::_sigprocmask"); 0 } -pub fn _signal(sig: u32, _two: i32, _ctx: &mut Ctx) -> i32 { - debug!("emscripten::_signal ({})", sig); +pub fn _signal(_ctx: &mut Ctx, _sig: u32, _two: i32) -> i32 { + debug!("emscripten::_signal ({})", _sig); 0 } diff --git a/lib/emscripten/src/syscalls/mod.rs b/lib/emscripten/src/syscalls/mod.rs index 9078af374..806ea8187 100644 --- a/lib/emscripten/src/syscalls/mod.rs +++ b/lib/emscripten/src/syscalls/mod.rs @@ -28,7 +28,7 @@ use libc::{ getpid, // iovec, lseek, - open, + // open, read, // readv, rmdir, @@ -40,20 +40,12 @@ use libc::{ use wasmer_runtime_core::vm::Ctx; use super::env; +use std::cell::Cell; use std::slice; -// use std::sys::fd::FileDesc; - -// Another conditional constant for name resolution: Macos et iOS use -// SO_NOSIGPIPE as a setsockopt flag to disable SIGPIPE emission on socket. -// Other platforms do otherwise. -#[cfg(target_os = "darwin")] -use libc::SO_NOSIGPIPE; -#[cfg(not(target_os = "darwin"))] -const SO_NOSIGPIPE: c_int = 0; /// exit -pub fn ___syscall1(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) { - debug!("emscripten::___syscall1 (exit) {}", which); +pub fn ___syscall1(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) { + debug!("emscripten::___syscall1 (exit) {}", _which); let status: i32 = varargs.get(ctx); unsafe { exit(status); @@ -61,104 +53,114 @@ pub fn ___syscall1(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) { } /// read -pub fn ___syscall3(which: i32, mut varargs: VarArgs, ctx: &mut Ctx) -> i32 { +pub fn ___syscall3(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 { // -> ssize_t - debug!("emscripten::___syscall3 (read) {}", which); + debug!("emscripten::___syscall3 (read) {}", _which); let fd: i32 = varargs.get(ctx); let buf: u32 = varargs.get(ctx); - let count = varargs.get(ctx); + let count: i32 = varargs.get(ctx); debug!("=> fd: {}, buf_offset: {}, count: {}", fd, buf, count); let buf_addr = emscripten_memory_pointer!(ctx.memory(0), buf) as *mut c_void; - let ret = unsafe { read(fd, buf_addr, count) }; + let ret = unsafe { read(fd, buf_addr, count as _) }; debug!("=> ret: {}", ret); ret as _ } /// write -pub fn ___syscall4(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int { - debug!("emscripten::___syscall4 (write) {}", which); +pub fn ___syscall4(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall4 (write) {}", _which); let fd: i32 = varargs.get(ctx); let buf: u32 = varargs.get(ctx); - let count = varargs.get(ctx); + let count: i32 = varargs.get(ctx); debug!("=> fd: {}, buf: {}, count: {}", fd, buf, count); let buf_addr = emscripten_memory_pointer!(ctx.memory(0), buf) as *const c_void; - unsafe { write(fd, buf_addr, count) as i32 } -} - -/// open -pub fn ___syscall5(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int { - debug!("emscripten::___syscall5 (open) {}", which); - let pathname: u32 = varargs.get(ctx); - let flags: i32 = varargs.get(ctx); - let mode: u32 = varargs.get(ctx); - let pathname_addr = emscripten_memory_pointer!(ctx.memory(0), pathname) as *const i8; - let path_str = unsafe { std::ffi::CStr::from_ptr(pathname_addr).to_str().unwrap() }; - let fd = unsafe { open(pathname_addr, flags, mode) }; - debug!( - "=> pathname: {}, flags: {}, mode: {} = fd: {}\npath: {}", - pathname, flags, mode, fd, path_str - ); - fd + unsafe { write(fd, buf_addr, count as _) as i32 } } /// close -pub fn ___syscall6(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int { - debug!("emscripten::___syscall6 (close) {}", which); +pub fn ___syscall6(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall6 (close) {}", _which); let fd: i32 = varargs.get(ctx); debug!("fd: {}", fd); unsafe { close(fd) } } // chdir -pub fn ___syscall12(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int { - debug!("emscripten::___syscall12 (chdir) {}", which); +pub fn ___syscall12(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall12 (chdir) {}", _which); let path_addr: i32 = varargs.get(ctx); unsafe { let path_ptr = emscripten_memory_pointer!(ctx.memory(0), path_addr) as *const i8; - let path = std::ffi::CStr::from_ptr(path_ptr); + let _path = std::ffi::CStr::from_ptr(path_ptr); let ret = chdir(path_ptr); - debug!("=> path: {:?}, ret: {}", path, ret); + debug!("=> path: {:?}, ret: {}", _path, ret); ret } } -pub fn ___syscall10(_one: i32, _two: i32, _ctx: &mut Ctx) -> i32 { +pub fn ___syscall10(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall10"); -1 } -pub fn ___syscall15(_one: i32, _two: i32, _ctx: &mut Ctx) -> i32 { +pub fn ___syscall15(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall15"); -1 } // getpid -pub fn ___syscall20(_one: i32, _two: i32, _ctx: &mut Ctx) -> i32 { +pub fn ___syscall20(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall20 (getpid)"); unsafe { getpid() } } -pub fn ___syscall38(_one: i32, _two: i32, _ctx: &mut Ctx) -> i32 { +pub fn ___syscall38(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall38"); -1 } // rmdir -pub fn ___syscall40(_which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int { +pub fn ___syscall40(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall40 (rmdir)"); let pathname: u32 = varargs.get(ctx); let pathname_addr = emscripten_memory_pointer!(ctx.memory(0), pathname) as *const i8; unsafe { rmdir(pathname_addr) } } -pub fn ___syscall60(_one: i32, _two: i32, _ctx: &mut Ctx) -> i32 { +// pipe +pub fn ___syscall42(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall42 (pipe)"); + // offset to a file descriptor, which contains a read end and write end, 2 integers + let fd_offset: u32 = varargs.get(ctx); + + let emscripten_memory = ctx.memory(0); + + // convert the file descriptor into a vec with two slots + let mut fd_vec: Vec = emscripten_memory.view()[((fd_offset / 4) as usize)..] + .iter() + .map(|pipe_end: &Cell| pipe_end.get()) + .take(2) + .collect(); + + // get it as a mutable pointer + let fd_ptr = fd_vec.as_mut_ptr(); + + // call pipe and store the pointers in this array + #[cfg(target_os = "windows")] + let result: c_int = unsafe { libc::pipe(fd_ptr, 2048, 0) }; + #[cfg(not(target_os = "windows"))] + let result: c_int = unsafe { libc::pipe(fd_ptr) }; + result +} + +pub fn ___syscall60(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall60"); -1 } // dup2 -pub fn ___syscall63(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int { - debug!("emscripten::___syscall63 (dup2) {}", which); +pub fn ___syscall63(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall63 (dup2) {}", _which); let src: i32 = varargs.get(ctx); let dst: i32 = varargs.get(ctx); @@ -167,61 +169,80 @@ pub fn ___syscall63(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int } // getppid -pub fn ___syscall64(_one: i32, _two: i32, _ctx: &mut Ctx) -> i32 { +pub fn ___syscall64(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall64 (getppid)"); unsafe { getpid() } } -pub fn ___syscall66(_one: i32, _two: i32, _ctx: &mut Ctx) -> i32 { +pub fn ___syscall66(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall66"); -1 } -pub fn ___syscall75(_one: i32, _two: i32, _ctx: &mut Ctx) -> i32 { +pub fn ___syscall75(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall75"); -1 } -pub fn ___syscall85(_one: i32, _two: i32, _ctx: &mut Ctx) -> i32 { +pub fn ___syscall85(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall85"); -1 } -pub fn ___syscall91(_one: i32, _two: i32, _ctx: &mut Ctx) -> i32 { +pub fn ___syscall91(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall91"); -1 } -pub fn ___syscall97(_one: i32, _two: i32, _ctx: &mut Ctx) -> i32 { +pub fn ___syscall97(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall97"); -1 } -pub fn ___syscall110(_one: i32, _two: i32, _ctx: &mut Ctx) -> i32 { +pub fn ___syscall110(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall110"); -1 } +// getcwd +pub fn ___syscall183(ctx: &mut Ctx, buf_offset: u32, _size: u32) -> u32 { + debug!("emscripten::___syscall183"); + use std::env; + let path = env::current_dir(); + let path_string = path.unwrap().display().to_string(); + let len = path_string.len(); + unsafe { + let pointer_to_buffer = + emscripten_memory_pointer!(ctx.memory(0), buf_offset) as *mut libc::c_char; + let slice = slice::from_raw_parts_mut(pointer_to_buffer, len.clone()); + for (byte, loc) in path_string.bytes().zip(slice.iter_mut()) { + *loc = byte as _; + } + *pointer_to_buffer.add(len.clone()) = 0; + } + buf_offset +} + // mmap2 -pub fn ___syscall192(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int { - debug!("emscripten::___syscall192 (mmap2) {}", which); - let addr: i32 = varargs.get(ctx); +pub fn ___syscall192(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall192 (mmap2) {}", _which); + let _addr: i32 = varargs.get(ctx); let len: u32 = varargs.get(ctx); - let prot: i32 = varargs.get(ctx); - let flags: i32 = varargs.get(ctx); + let _prot: i32 = varargs.get(ctx); + let _flags: i32 = varargs.get(ctx); let fd: i32 = varargs.get(ctx); - let off: i32 = varargs.get(ctx); + let _off: i32 = varargs.get(ctx); debug!( "=> addr: {}, len: {}, prot: {}, flags: {}, fd: {}, off: {}", - addr, len, prot, flags, fd, off + _addr, len, _prot, _flags, fd, _off ); if fd == -1 { - let ptr = env::call_memalign(16384, len, ctx); + let ptr = env::call_memalign(ctx, 16384, len); if ptr == 0 { return -1; } - env::call_memset(ptr, 0, len, ctx); + env::call_memset(ctx, ptr, 0, len); ptr as _ } else { -1 @@ -229,27 +250,21 @@ pub fn ___syscall192(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int } /// lseek -pub fn ___syscall140(which: i32, mut varargs: VarArgs, ctx: &mut Ctx) -> i32 { +pub fn ___syscall140(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 { // -> c_int - debug!("emscripten::___syscall140 (lseek) {}", which); + debug!("emscripten::___syscall140 (lseek) {}", _which); let fd: i32 = varargs.get(ctx); - let offset = varargs.get(ctx); + let offset: i32 = varargs.get(ctx); let whence: i32 = varargs.get(ctx); debug!("=> fd: {}, offset: {}, whence = {}", fd, offset, whence); - unsafe { lseek(fd, offset, whence) as _ } + unsafe { lseek(fd, offset as _, whence) as _ } } /// readv #[allow(clippy::cast_ptr_alignment)] -pub fn ___syscall145(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> i32 { +pub fn ___syscall145(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> i32 { // -> ssize_t - debug!("emscripten::___syscall145 (readv) {}", which); - // let fd: i32 = varargs.get(ctx); - // let iov: u32 = varargs.get(ctx); - // let iovcnt: i32 = varargs.get(ctx); - // debug!("=> fd: {}, iov: {}, iovcnt = {}", fd, iov, iovcnt); - // let iov_addr = emscripten_memory_pointer!(ctx.memory(0), iov) as *mut iovec; - // unsafe { readv(fd, iov_addr, iovcnt) } + debug!("emscripten::___syscall145 (readv) {}", _which); let fd: i32 = varargs.get(ctx); let iov: i32 = varargs.get(ctx); @@ -284,9 +299,9 @@ pub fn ___syscall145(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> i32 { // writev #[allow(clippy::cast_ptr_alignment)] -pub fn ___syscall146(which: i32, mut varargs: VarArgs, ctx: &mut Ctx) -> i32 { +pub fn ___syscall146(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 { // -> ssize_t - debug!("emscripten::___syscall146 (writev) {}", which); + debug!("emscripten::___syscall146 (writev) {}", _which); let fd: i32 = varargs.get(ctx); let iov: i32 = varargs.get(ctx); let iovcnt: i32 = varargs.get(ctx); @@ -318,34 +333,34 @@ pub fn ___syscall146(which: i32, mut varargs: VarArgs, ctx: &mut Ctx) -> i32 { } } -pub fn ___syscall168(_one: i32, _two: i32, _ctx: &mut Ctx) -> i32 { +pub fn ___syscall168(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall168"); -1 } -pub fn ___syscall191(_one: i32, _two: i32, _ctx: &mut Ctx) -> i32 { +pub fn ___syscall191(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall191 - stub"); -1 } -pub fn ___syscall194(_one: i32, _two: i32, _ctx: &mut Ctx) -> i32 { +pub fn ___syscall194(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall194 - stub"); -1 } -pub fn ___syscall196(_one: i32, _two: i32, _ctx: &mut Ctx) -> i32 { +pub fn ___syscall196(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall194 - stub"); -1 } -pub fn ___syscall199(_one: i32, _two: i32, _ctx: &mut Ctx) -> i32 { +pub fn ___syscall199(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall199 - stub"); -1 } // stat64 -pub fn ___syscall195(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int { - debug!("emscripten::___syscall195 (stat64) {}", which); +pub fn ___syscall195(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall195 (stat64) {}", _which); let pathname: u32 = varargs.get(ctx); let buf: u32 = varargs.get(ctx); @@ -364,8 +379,8 @@ pub fn ___syscall195(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int } // fstat64 -pub fn ___syscall197(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int { - debug!("emscripten::___syscall197 (fstat64) {}", which); +pub fn ___syscall197(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall197 (fstat64) {}", _which); let fd: c_int = varargs.get(ctx); let buf: u32 = varargs.get(ctx); @@ -382,14 +397,14 @@ pub fn ___syscall197(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int 0 } -pub fn ___syscall220(_one: i32, _two: i32, _ctx: &mut Ctx) -> i32 { +pub fn ___syscall220(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall220"); -1 } // fcntl64 -pub fn ___syscall221(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int { - debug!("emscripten::___syscall221 (fcntl64) {}", which); +pub fn ___syscall221(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall221 (fcntl64) {}", _which); // fcntl64 let _fd: i32 = varargs.get(ctx); let cmd: u32 = varargs.get(ctx); @@ -399,34 +414,34 @@ pub fn ___syscall221(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int } } -pub fn ___syscall268(_one: i32, _two: i32, _ctx: &mut Ctx) -> i32 { +pub fn ___syscall268(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall268"); -1 } -pub fn ___syscall272(_one: i32, _two: i32, _ctx: &mut Ctx) -> i32 { +pub fn ___syscall272(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall272"); -1 } -pub fn ___syscall295(_one: i32, _two: i32, _ctx: &mut Ctx) -> i32 { +pub fn ___syscall295(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall295"); -1 } -pub fn ___syscall300(_one: i32, _two: i32, _ctx: &mut Ctx) -> i32 { +pub fn ___syscall300(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall300"); -1 } -pub fn ___syscall334(_one: i32, _two: i32, _ctx: &mut Ctx) -> i32 { +pub fn ___syscall334(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall334"); -1 } // prlimit64 -pub fn ___syscall340(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int { - debug!("emscripten::___syscall340 (prlimit64), {}", which); +pub fn ___syscall340(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall340 (prlimit64), {}", _which); // NOTE: Doesn't really matter. Wasm modules cannot exceed WASM_PAGE_SIZE anyway. let _pid: i32 = varargs.get(ctx); let _resource: i32 = varargs.get(ctx); diff --git a/lib/emscripten/src/syscalls/unix.rs b/lib/emscripten/src/syscalls/unix.rs index 1e38c39c6..1102b7a59 100644 --- a/lib/emscripten/src/syscalls/unix.rs +++ b/lib/emscripten/src/syscalls/unix.rs @@ -24,6 +24,7 @@ use libc::{ listen, mkdir, msghdr, + open, pid_t, pread, pwrite, @@ -76,9 +77,25 @@ use libc::SO_NOSIGPIPE; #[cfg(not(target_os = "darwin"))] const SO_NOSIGPIPE: c_int = 0; +/// open +pub fn ___syscall5(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall5 (open) {}", _which); + let pathname: u32 = varargs.get(ctx); + let flags: i32 = varargs.get(ctx); + let mode: u32 = varargs.get(ctx); + let pathname_addr = emscripten_memory_pointer!(ctx.memory(0), pathname) as *const i8; + let _path_str = unsafe { std::ffi::CStr::from_ptr(pathname_addr).to_str().unwrap() }; + let fd = unsafe { open(pathname_addr, flags, mode) }; + debug!( + "=> pathname: {}, flags: {}, mode: {} = fd: {}\npath: {}", + pathname, flags, mode, fd, _path_str + ); + fd +} + // chown -pub fn ___syscall212(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int { - debug!("emscripten::___syscall212 (chown) {}", which); +pub fn ___syscall212(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall212 (chown) {}", _which); let pathname: u32 = varargs.get(ctx); let owner: u32 = varargs.get(ctx); @@ -90,8 +107,8 @@ pub fn ___syscall212(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int } // mkdir -pub fn ___syscall39(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int { - debug!("emscripten::___syscall39 (mkdir) {}", which); +pub fn ___syscall39(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall39 (mkdir) {}", _which); let pathname: u32 = varargs.get(ctx); let mode: u32 = varargs.get(ctx); let pathname_addr = emscripten_memory_pointer!(ctx.memory(0), pathname) as *const i8; @@ -99,7 +116,7 @@ pub fn ___syscall39(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int } // getgid -pub fn ___syscall201(_one: i32, _two: i32, _ctx: &mut Ctx) -> i32 { +pub fn ___syscall201(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall201 (getgid)"); unsafe { // Maybe fix: Emscripten returns 0 always @@ -108,7 +125,7 @@ pub fn ___syscall201(_one: i32, _two: i32, _ctx: &mut Ctx) -> i32 { } // getgid32 -pub fn ___syscall202(_one: i32, _two: i32, _ctx: &mut Ctx) -> i32 { +pub fn ___syscall202(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { // gid_t debug!("emscripten::___syscall202 (getgid32)"); unsafe { @@ -118,7 +135,7 @@ pub fn ___syscall202(_one: i32, _two: i32, _ctx: &mut Ctx) -> i32 { } /// dup3 -pub fn ___syscall330(_which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> pid_t { +pub fn ___syscall330(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> pid_t { // Implementation based on description at https://linux.die.net/man/2/dup3 debug!("emscripten::___syscall330 (dup3)"); let oldfd: c_int = varargs.get(ctx); @@ -152,8 +169,8 @@ pub fn ___syscall330(_which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> pid_ } /// ioctl -pub fn ___syscall54(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int { - debug!("emscripten::___syscall54 (ioctl) {}", which); +pub fn ___syscall54(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall54 (ioctl) {}", _which); let fd: i32 = varargs.get(ctx); let request: u32 = varargs.get(ctx); debug!("fd: {}, op: {}", fd, request); @@ -195,8 +212,8 @@ pub fn ___syscall54(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int // socketcall #[allow(clippy::cast_ptr_alignment)] -pub fn ___syscall102(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int { - debug!("emscripten::___syscall102 (socketcall) {}", which); +pub fn ___syscall102(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall102 (socketcall) {}", _which); let call: u32 = varargs.get(ctx); let mut socket_varargs: VarArgs = varargs.get(ctx); @@ -262,13 +279,11 @@ pub fn ___syscall102(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int let address = emscripten_memory_pointer!(ctx.memory(0), address) as *mut sockaddr; // Debug received address - unsafe { - let proper_address = address as *const GuestSockaddrIn; - debug!( + let _proper_address = address as *const GuestSockaddrIn; + debug!( "=> address.sin_family: {:?}, address.sin_port: {:?}, address.sin_addr.s_addr: {:?}", - (*proper_address).sin_family, (*proper_address).sin_port, (*proper_address).sin_addr.s_addr + (*_proper_address).sin_family, (*_proper_address).sin_port, (*_proper_address).sin_addr.s_addr ); - } let status = unsafe { bind(socket, address, address_len) }; // debug!("=> status: {}", status); @@ -447,8 +462,8 @@ pub fn ___syscall102(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int } // pread -pub fn ___syscall180(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int { - debug!("emscripten::___syscall180 (pread) {}", which); +pub fn ___syscall180(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall180 (pread) {}", _which); let fd: i32 = varargs.get(ctx); let buf: u32 = varargs.get(ctx); let count: u32 = varargs.get(ctx); @@ -464,8 +479,8 @@ pub fn ___syscall180(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int } // pwrite -pub fn ___syscall181(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int { - debug!("emscripten::___syscall181 (pwrite) {}", which); +pub fn ___syscall181(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall181 (pwrite) {}", _which); let fd: i32 = varargs.get(ctx); let buf: u32 = varargs.get(ctx); let count: u32 = varargs.get(ctx); @@ -486,7 +501,7 @@ pub fn ___syscall181(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int /// wait4 #[allow(clippy::cast_ptr_alignment)] -pub fn ___syscall114(_which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> pid_t { +pub fn ___syscall114(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> pid_t { debug!("emscripten::___syscall114 (wait4)"); let pid: pid_t = varargs.get(ctx); let status: u32 = varargs.get(ctx); @@ -504,8 +519,8 @@ pub fn ___syscall114(_which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> pid_ // select #[allow(clippy::cast_ptr_alignment)] -pub fn ___syscall142(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int { - debug!("emscripten::___syscall142 (newselect) {}", which); +pub fn ___syscall142(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall142 (newselect) {}", _which); let nfds: i32 = varargs.get(ctx); let readfds: u32 = varargs.get(ctx); @@ -523,8 +538,8 @@ pub fn ___syscall142(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int } // setpgid -pub fn ___syscall57(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int { - debug!("emscripten::___syscall57 (setpgid) {}", which); +pub fn ___syscall57(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall57 (setpgid) {}", _which); let pid: i32 = varargs.get(ctx); let pgid: i32 = varargs.get(ctx); unsafe { setpgid(pid, pgid) } @@ -532,8 +547,8 @@ pub fn ___syscall57(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int /// uname // NOTE: Wondering if we should return custom utsname, like Emscripten. -pub fn ___syscall122(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int { - debug!("emscripten::___syscall122 (uname) {}", which); +pub fn ___syscall122(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall122 (uname) {}", _which); let buf: u32 = varargs.get(ctx); debug!("=> buf: {}", buf); let buf_addr = emscripten_memory_pointer!(ctx.memory(0), buf) as *mut utsname; diff --git a/lib/emscripten/src/syscalls/windows.rs b/lib/emscripten/src/syscalls/windows.rs index 2b577fcac..bd32d3b53 100644 --- a/lib/emscripten/src/syscalls/windows.rs +++ b/lib/emscripten/src/syscalls/windows.rs @@ -1,18 +1,69 @@ +use crate::utils::copy_cstr_into_wasm; use crate::varargs::VarArgs; use libc::mkdir; +use libc::open; +use rand::Rng; +use std::env; +use std::ffi::CStr; +use std::ffi::CString; +use std::fs::File; +use std::io::Write; use std::os::raw::c_int; use wasmer_runtime_core::vm::Ctx; type pid_t = c_int; +/// open +pub fn ___syscall5(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall5 (open) {}", which); + let pathname: u32 = varargs.get(ctx); + let flags: i32 = varargs.get(ctx); + let mode: u32 = varargs.get(ctx); + let pathname_addr = emscripten_memory_pointer!(ctx.memory(0), pathname) as *const i8; + let path_str = unsafe { std::ffi::CStr::from_ptr(pathname_addr).to_str().unwrap() }; + match path_str { + "/dev/urandom" => { + // create a fake urandom file for windows, super hacky + // put it in the temp directory so we can just forget about it + let mut tmp_dir = env::temp_dir(); + tmp_dir.push("urandom"); + let tmp_dir_str = tmp_dir.to_str().unwrap(); + let tmp_dir_c_str = CString::new(tmp_dir_str).unwrap(); + let ptr = tmp_dir_c_str.as_ptr() as *const i8; + let mut urandom_file = File::create(tmp_dir).unwrap(); + // create some random bytes and put them into the file + let random_bytes = rand::thread_rng().gen::<[u8; 32]>(); + urandom_file.write_all(&random_bytes); + // put the file path string into wasm memory + let urandom_file_offset = unsafe { copy_cstr_into_wasm(ctx, ptr) }; + let raw_pointer_to_urandom_file = + emscripten_memory_pointer!(ctx.memory(0), urandom_file_offset) as *const i8; + let fd = unsafe { open(raw_pointer_to_urandom_file, flags, mode) }; + debug!( + "=> pathname: {}, flags: {}, mode: {} = fd: {}\npath: {}", + pathname, flags, mode, fd, s + ); + fd + } + _ => { + let fd = unsafe { open(pathname_addr, flags, mode) }; + debug!( + "=> pathname: {}, flags: {}, mode: {} = fd: {}\npath: {}", + pathname, flags, mode, fd, path_str + ); + fd + } + } +} + // chown -pub fn ___syscall212(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int { +pub fn ___syscall212(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall212 (chown) {}", which); -1 } // mkdir -pub fn ___syscall39(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int { +pub fn ___syscall39(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall39 (mkdir) {}", which); let pathname: u32 = varargs.get(ctx); let mode: u32 = varargs.get(ctx); @@ -21,72 +72,72 @@ pub fn ___syscall39(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int } // getgid -pub fn ___syscall201(_one: i32, _two: i32, _ctx: &mut Ctx) -> i32 { +pub fn ___syscall201(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall201 (getgid)"); -1 } // getgid32 -pub fn ___syscall202(_one: i32, _two: i32, _ctx: &mut Ctx) -> i32 { +pub fn ___syscall202(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { // gid_t debug!("emscripten::___syscall202 (getgid32)"); -1 } /// dup3 -pub fn ___syscall330(_which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> pid_t { +pub fn ___syscall330(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> pid_t { debug!("emscripten::___syscall330 (dup3)"); -1 } /// ioctl -pub fn ___syscall54(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int { +pub fn ___syscall54(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall54 (ioctl) {}", which); -1 } // socketcall #[allow(clippy::cast_ptr_alignment)] -pub fn ___syscall102(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int { +pub fn ___syscall102(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall102 (socketcall) {}", which); -1 } // pread -pub fn ___syscall180(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int { +pub fn ___syscall180(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall180 (pread) {}", which); -1 } // pwrite -pub fn ___syscall181(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int { +pub fn ___syscall181(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall181 (pwrite) {}", which); -1 } /// wait4 #[allow(clippy::cast_ptr_alignment)] -pub fn ___syscall114(_which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> pid_t { +pub fn ___syscall114(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> pid_t { debug!("emscripten::___syscall114 (wait4)"); -1 } // select #[allow(clippy::cast_ptr_alignment)] -pub fn ___syscall142(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int { +pub fn ___syscall142(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall142 (newselect) {}", which); -1 } // setpgid -pub fn ___syscall57(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int { +pub fn ___syscall57(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall57 (setpgid) {}", which); -1 } /// uname // NOTE: Wondering if we should return custom utsname, like Emscripten. -pub fn ___syscall122(which: c_int, mut varargs: VarArgs, ctx: &mut Ctx) -> c_int { +pub fn ___syscall122(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall122 (uname) {}", which); -1 } diff --git a/lib/emscripten/src/time.rs b/lib/emscripten/src/time.rs index c270e1785..459124f10 100644 --- a/lib/emscripten/src/time.rs +++ b/lib/emscripten/src/time.rs @@ -41,7 +41,7 @@ const CLOCK_MONOTONIC_COARSE: clockid_t = 6; /// emscripten: _gettimeofday #[allow(clippy::cast_ptr_alignment)] -pub fn _gettimeofday(tp: c_int, tz: c_int, ctx: &mut Ctx) -> c_int { +pub fn _gettimeofday(ctx: &mut Ctx, tp: c_int, tz: c_int) -> c_int { debug!("emscripten::_gettimeofday {} {}", tp, tz); #[repr(C)] struct GuestTimeVal { @@ -66,7 +66,7 @@ pub fn _gettimeofday(tp: c_int, tz: c_int, ctx: &mut Ctx) -> c_int { /// emscripten: _clock_gettime #[allow(clippy::cast_ptr_alignment)] -pub fn _clock_gettime(clk_id: clockid_t, tp: c_int, ctx: &mut Ctx) -> c_int { +pub fn _clock_gettime(ctx: &mut Ctx, clk_id: clockid_t, tp: c_int) -> c_int { debug!("emscripten::_clock_gettime {} {}", clk_id, tp); // debug!("Memory {:?}", ctx.memory(0)[..]); #[repr(C)] @@ -75,8 +75,10 @@ pub fn _clock_gettime(clk_id: clockid_t, tp: c_int, ctx: &mut Ctx) -> c_int { tv_nsec: i32, } + #[allow(unreachable_patterns)] let timespec = match clk_id { CLOCK_REALTIME => time::get_time(), + CLOCK_MONOTONIC | CLOCK_MONOTONIC_COARSE => { let precise_ns = time::precise_time_ns(); time::Timespec::new( @@ -97,9 +99,9 @@ pub fn _clock_gettime(clk_id: clockid_t, tp: c_int, ctx: &mut Ctx) -> c_int { } /// emscripten: ___clock_gettime -pub fn ___clock_gettime(clk_id: clockid_t, tp: c_int, ctx: &mut Ctx) -> c_int { +pub fn ___clock_gettime(ctx: &mut Ctx, clk_id: clockid_t, tp: c_int) -> c_int { debug!("emscripten::___clock_gettime {} {}", clk_id, tp); - _clock_gettime(clk_id, tp, ctx) + _clock_gettime(ctx, clk_id, tp) } /// emscripten: _clock @@ -109,22 +111,22 @@ pub fn _clock(_ctx: &mut Ctx) -> c_int { } /// emscripten: _difftime -pub fn _difftime(t0: u32, t1: u32, _ctx: &mut Ctx) -> f64 { +pub fn _difftime(_ctx: &mut Ctx, t0: u32, t1: u32) -> f64 { debug!("emscripten::_difftime"); (t0 - t1) as _ } -pub fn _gmtime_r(_one: i32, _two: i32, _ctx: &mut Ctx) -> i32 { +pub fn _gmtime_r(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::_gmtime_r"); -1 } -pub fn _mktime(_one: i32, _ctx: &mut Ctx) -> i32 { +pub fn _mktime(_ctx: &mut Ctx, _one: i32) -> i32 { debug!("emscripten::_mktime"); -1 } -pub fn _gmtime(_one: i32, _ctx: &mut Ctx) -> i32 { +pub fn _gmtime(_ctx: &mut Ctx, _one: i32) -> i32 { debug!("emscripten::_gmtime"); -1 } @@ -151,7 +153,7 @@ pub fn _tvset(_ctx: &mut Ctx) { /// formats time as a C string #[allow(clippy::cast_ptr_alignment)] -unsafe fn fmt_time(time: u32, ctx: &mut Ctx) -> *const c_char { +unsafe fn fmt_time(ctx: &mut Ctx, time: u32) -> *const c_char { let date = &*(emscripten_memory_pointer!(ctx.memory(0), time) as *mut guest_tm); let days = vec!["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; @@ -176,11 +178,11 @@ unsafe fn fmt_time(time: u32, ctx: &mut Ctx) -> *const c_char { } /// emscripten: _asctime -pub fn _asctime(time: u32, ctx: &mut Ctx) -> u32 { +pub fn _asctime(ctx: &mut Ctx, time: u32) -> u32 { debug!("emscripten::_asctime {}", time); unsafe { - let time_str_ptr = fmt_time(time, ctx); + let time_str_ptr = fmt_time(ctx, time); copy_cstr_into_wasm(ctx, time_str_ptr) // let c_str = emscripten_memory_pointer!(ctx.memory(0), res) as *mut i8; @@ -190,7 +192,7 @@ pub fn _asctime(time: u32, ctx: &mut Ctx) -> u32 { } /// emscripten: _asctime_r -pub fn _asctime_r(time: u32, buf: u32, ctx: &mut Ctx) -> u32 { +pub fn _asctime_r(ctx: &mut Ctx, time: u32, buf: u32) -> u32 { debug!("emscripten::_asctime_r {}, {}", time, buf); unsafe { @@ -198,8 +200,8 @@ pub fn _asctime_r(time: u32, buf: u32, ctx: &mut Ctx) -> u32 { // to write out more than 26 bytes (including the null terminator). // See http://pubs.opengroup.org/onlinepubs/9699919799/functions/asctime.html // Our undefined behavior is to truncate the write to at most 26 bytes, including null terminator. - let time_str_ptr = fmt_time(time, ctx); - write_to_buf(time_str_ptr, buf, 26, ctx) + let time_str_ptr = fmt_time(ctx, time); + write_to_buf(ctx, time_str_ptr, buf, 26) // let c_str = emscripten_memory_pointer!(ctx.memory(0), res) as *mut i8; // use std::ffi::CStr; @@ -209,7 +211,7 @@ pub fn _asctime_r(time: u32, buf: u32, ctx: &mut Ctx) -> u32 { /// emscripten: _localtime #[allow(clippy::cast_ptr_alignment)] -pub fn _localtime(time_p: u32, ctx: &mut Ctx) -> c_int { +pub fn _localtime(ctx: &mut Ctx, time_p: u32) -> c_int { debug!("emscripten::_localtime {}", time_p); // NOTE: emscripten seems to want tzset() called in this function // https://stackoverflow.com/questions/19170721/real-time-awareness-of-timezone-change-in-localtime-vs-localtime-r @@ -222,7 +224,7 @@ pub fn _localtime(time_p: u32, ctx: &mut Ctx) -> c_int { let result_tm = time::at(timespec); unsafe { - let tm_struct_offset = env::call_malloc(mem::size_of::() as _, ctx); + let tm_struct_offset = env::call_malloc(ctx, mem::size_of::() as _); let tm_struct_ptr = emscripten_memory_pointer!(ctx.memory(0), tm_struct_offset) as *mut guest_tm; // debug!( @@ -247,7 +249,7 @@ pub fn _localtime(time_p: u32, ctx: &mut Ctx) -> c_int { } /// emscripten: _localtime_r #[allow(clippy::cast_ptr_alignment)] -pub fn _localtime_r(time_p: u32, result: u32, ctx: &mut Ctx) -> c_int { +pub fn _localtime_r(ctx: &mut Ctx, time_p: u32, result: u32) -> c_int { debug!("emscripten::_localtime_r {}", time_p); // NOTE: emscripten seems to want tzset() called in this function @@ -284,7 +286,7 @@ pub fn _localtime_r(time_p: u32, result: u32, ctx: &mut Ctx) -> c_int { /// emscripten: _time #[allow(clippy::cast_ptr_alignment)] -pub fn _time(time_p: u32, ctx: &mut Ctx) -> i32 { +pub fn _time(ctx: &mut Ctx, time_p: u32) -> i32 { debug!("emscripten::_time {}", time_p); unsafe { @@ -295,15 +297,15 @@ pub fn _time(time_p: u32, ctx: &mut Ctx) -> i32 { /// emscripten: _strftime pub fn _strftime( - s_ptr: c_int, - maxsize: u32, - format_ptr: c_int, - tm_ptr: c_int, _ctx: &mut Ctx, + _s_ptr: c_int, + _maxsize: u32, + _format_ptr: c_int, + _tm_ptr: c_int, ) -> i32 { debug!( "emscripten::_strftime {} {} {} {}", - s_ptr, maxsize, format_ptr, tm_ptr + _s_ptr, _maxsize, _format_ptr, _tm_ptr ); 0 } diff --git a/lib/emscripten/src/utils.rs b/lib/emscripten/src/utils.rs index 4437f6262..79936f2f4 100644 --- a/lib/emscripten/src/utils.rs +++ b/lib/emscripten/src/utils.rs @@ -16,13 +16,12 @@ use wasmer_runtime_core::{ /// We check if a provided module is an Emscripten generated one pub fn is_emscripten_module(module: &Module) -> bool { - for (_, import_name) in &module.0.info.imported_functions { + for (_, import_name) in &module.info().imported_functions { let namespace = module - .0 - .info + .info() .namespace_table .get(import_name.namespace_index); - let field = module.0.info.name_table.get(import_name.name_index); + let field = module.info().name_table.get(import_name.name_index); if field == "_emscripten_memcpy_big" && namespace == "env" { return true; } @@ -31,16 +30,16 @@ pub fn is_emscripten_module(module: &Module) -> bool { } pub fn get_emscripten_table_size(module: &Module) -> (u32, Option) { - let (_, table) = &module.0.info.imported_tables[ImportedTableIndex::new(0)]; + let (_, table) = &module.info().imported_tables[ImportedTableIndex::new(0)]; (table.minimum, table.maximum) } pub fn get_emscripten_memory_size(module: &Module) -> (Pages, Option) { - let (_, memory) = &module.0.info.imported_memories[ImportedMemoryIndex::new(0)]; + let (_, memory) = &module.info().imported_memories[ImportedMemoryIndex::new(0)]; (memory.minimum, memory.maximum) } -pub unsafe fn write_to_buf(string: *const c_char, buf: u32, max: u32, ctx: &mut Ctx) -> u32 { +pub unsafe fn write_to_buf(ctx: &mut Ctx, string: *const c_char, buf: u32, max: u32) -> u32 { let buf_addr = emscripten_memory_pointer!(ctx.memory(0), buf) as *mut c_char; for i in 0..max { @@ -54,7 +53,7 @@ pub unsafe fn write_to_buf(string: *const c_char, buf: u32, max: u32, ctx: &mut pub unsafe fn copy_cstr_into_wasm(ctx: &mut Ctx, cstr: *const c_char) -> u32 { let s = CStr::from_ptr(cstr).to_str().unwrap(); let cstr_len = s.len(); - let space_offset = env::call_malloc((cstr_len as u32) + 1, ctx); + let space_offset = env::call_malloc(ctx, (cstr_len as u32) + 1); let raw_memory = emscripten_memory_pointer!(ctx.memory(0), space_offset) as *mut c_char; let slice = slice::from_raw_parts_mut(raw_memory, cstr_len); @@ -69,7 +68,7 @@ pub unsafe fn copy_cstr_into_wasm(ctx: &mut Ctx, cstr: *const c_char) -> u32 { space_offset } -pub unsafe fn allocate_on_stack<'a, T: Copy>(count: u32, ctx: &'a mut Ctx) -> (u32, &'a mut [T]) { +pub unsafe fn allocate_on_stack<'a, T: Copy>(ctx: &'a mut Ctx, count: u32) -> (u32, &'a mut [T]) { let offset = get_emscripten_data(ctx) .stack_alloc .call(count * (size_of::() as u32)) @@ -80,8 +79,8 @@ pub unsafe fn allocate_on_stack<'a, T: Copy>(count: u32, ctx: &'a mut Ctx) -> (u (offset, slice) } -pub unsafe fn allocate_cstr_on_stack<'a>(s: &str, ctx: &'a mut Ctx) -> (u32, &'a [u8]) { - let (offset, slice) = allocate_on_stack((s.len() + 1) as u32, ctx); +pub unsafe fn allocate_cstr_on_stack<'a>(ctx: &'a mut Ctx, s: &str) -> (u32, &'a [u8]) { + let (offset, slice) = allocate_on_stack(ctx, (s.len() + 1) as u32); use std::iter; for (byte, loc) in s.bytes().chain(iter::once(0)).zip(slice.iter_mut()) { @@ -92,7 +91,7 @@ pub unsafe fn allocate_cstr_on_stack<'a>(s: &str, ctx: &'a mut Ctx) -> (u32, &'a } pub unsafe fn copy_terminated_array_of_cstrs(_ctx: &mut Ctx, cstrs: *mut *mut c_char) -> u32 { - let total_num = { + let _total_num = { let mut ptr = cstrs; let mut counter = 0; while !(*ptr).is_null() { @@ -103,7 +102,7 @@ pub unsafe fn copy_terminated_array_of_cstrs(_ctx: &mut Ctx, cstrs: *mut *mut c_ }; debug!( "emscripten::copy_terminated_array_of_cstrs::total_num: {}", - total_num + _total_num ); 0 } @@ -156,6 +155,7 @@ pub unsafe fn copy_stat_into_wasm(ctx: &mut Ctx, buf: u32, stat: &stat) { (*stat_ptr).st_ino = stat.st_ino as _; } +#[allow(dead_code)] // it's used in `env/windows/mod.rs`. pub fn read_string_from_wasm(memory: &Memory, offset: u32) -> String { let v: Vec = memory.view()[(offset as usize)..] .iter() @@ -170,15 +170,34 @@ mod tests { use super::is_emscripten_module; use std::sync::Arc; use wabt::wat2wasm; - use wasmer_clif_backend::CraneliftCompiler; + use wasmer_runtime_core::backend::Compiler; use wasmer_runtime_core::compile_with; + #[cfg(feature = "clif")] + fn get_compiler() -> impl Compiler { + use wasmer_clif_backend::CraneliftCompiler; + CraneliftCompiler::new() + } + + #[cfg(feature = "llvm")] + fn get_compiler() -> impl Compiler { + use wasmer_llvm_backend::LLVMCompiler; + LLVMCompiler::new() + } + + #[cfg(not(any(feature = "llvm", feature = "clif")))] + fn get_compiler() -> impl Compiler { + panic!("compiler not specified, activate a compiler via features"); + use wasmer_clif_backend::CraneliftCompiler; + CraneliftCompiler::new() + } + #[test] fn should_detect_emscripten_files() { const WAST_BYTES: &[u8] = include_bytes!("tests/is_emscripten_true.wast"); let wasm_binary = wat2wasm(WAST_BYTES.to_vec()).expect("Can't convert to wasm"); - let module = compile_with(&wasm_binary[..], &CraneliftCompiler::new()) - .expect("WASM can't be compiled"); + let module = + compile_with(&wasm_binary[..], &get_compiler()).expect("WASM can't be compiled"); let module = Arc::new(module); assert!(is_emscripten_module(&module)); } @@ -187,8 +206,8 @@ mod tests { fn should_detect_non_emscripten_files() { const WAST_BYTES: &[u8] = include_bytes!("tests/is_emscripten_false.wast"); let wasm_binary = wat2wasm(WAST_BYTES.to_vec()).expect("Can't convert to wasm"); - let module = compile_with(&wasm_binary[..], &CraneliftCompiler::new()) - .expect("WASM can't be compiled"); + let module = + compile_with(&wasm_binary[..], &get_compiler()).expect("WASM can't be compiled"); let module = Arc::new(module); assert!(!is_emscripten_module(&module)); } diff --git a/lib/emscripten/tests/emtests/_common.rs b/lib/emscripten/tests/emtests/_common.rs index 8d3e00296..e7a42b356 100644 --- a/lib/emscripten/tests/emtests/_common.rs +++ b/lib/emscripten/tests/emtests/_common.rs @@ -1,16 +1,35 @@ macro_rules! assert_emscripten_output { ($file:expr, $name:expr, $args:expr, $expected:expr) => {{ - use wasmer_clif_backend::CraneliftCompiler; use wasmer_emscripten::{ EmscriptenGlobals, generate_emscripten_env, stdio::StdioCapturer }; + use wasmer_runtime_core::backend::Compiler; + + #[cfg(feature = "clif")] + fn get_compiler() -> impl Compiler { + use wasmer_clif_backend::CraneliftCompiler; + CraneliftCompiler::new() + } + + #[cfg(feature = "llvm")] + fn get_compiler() -> impl Compiler { + use wasmer_llvm_backend::LLVMCompiler; + LLVMCompiler::new() + } + + #[cfg(not(any(feature = "llvm", feature = "clif")))] + fn get_compiler() -> impl Compiler { + panic!("compiler not specified, activate a compiler via features"); + use wasmer_clif_backend::CraneliftCompiler; + CraneliftCompiler::new() + } let wasm_bytes = include_bytes!($file); - let module = wasmer_runtime_core::compile_with(&wasm_bytes[..], &CraneliftCompiler::new()) + let module = wasmer_runtime_core::compile_with(&wasm_bytes[..], &get_compiler()) .expect("WASM can't be compiled"); // let module = compile(&wasm_bytes[..]) @@ -41,3 +60,37 @@ macro_rules! assert_emscripten_output { ); }}; } + +pub fn assert_emscripten_output(wasm_bytes: &[u8], raw_expected_str: &str) { + use wasmer_clif_backend::CraneliftCompiler; + use wasmer_emscripten::{generate_emscripten_env, stdio::StdioCapturer, EmscriptenGlobals}; + + let module = wasmer_runtime_core::compile_with(&wasm_bytes[..], &CraneliftCompiler::new()) + .expect("WASM can't be compiled"); + + let mut emscripten_globals = EmscriptenGlobals::new(&module); + let import_object = generate_emscripten_env(&mut emscripten_globals); + let mut instance = module + .instantiate(&import_object) + .map_err(|err| format!("Can't instantiate the WebAssembly module: {:?}", err)) + .unwrap(); + + let capturer = StdioCapturer::new(); + + wasmer_emscripten::run_emscripten_instance(&module, &mut instance, "test", vec![]) + .expect("run_emscripten_instance finishes"); + + let raw_output_string = capturer.end().unwrap().0; + + // trim the strings to avoid cross-platform line ending and white space issues + let output = raw_output_string.trim(); + let expected_output = raw_expected_str.trim(); + + let contains_output = output.contains(expected_output); + + assert!( + contains_output, + "Output: `{}` does not contain expected output: `{}`", + output, expected_output + ); +} diff --git a/lib/emscripten/tests/emtests/mod.rs b/lib/emscripten/tests/emtests/mod.rs index 37c88a041..89a66bf2b 100644 --- a/lib/emscripten/tests/emtests/mod.rs +++ b/lib/emscripten/tests/emtests/mod.rs @@ -43,6 +43,7 @@ mod test_exceptions_2; mod test_exceptions_multi; mod test_exceptions_std; mod test_exceptions_white_list; +mod test_execvp; mod test_fast_math; mod test_flexarray_struct; mod test_float32_precise; @@ -54,6 +55,7 @@ mod test_funcptrfunc; mod test_funcs; mod test_functionpointer_libfunc_varargs; mod test_fwrite_0; +mod test_getcwd; mod test_getgep; mod test_getloadavg; mod test_getopt; @@ -117,6 +119,7 @@ mod test_nested_struct_varargs; mod test_nl_types; mod test_perrar; mod test_phiundef; +mod test_pipe; mod test_poll; mod test_posixtime; mod test_printf_2; diff --git a/lib/emscripten/tests/emtests/test_execvp.rs b/lib/emscripten/tests/emtests/test_execvp.rs new file mode 100644 index 000000000..c9b4b5113 --- /dev/null +++ b/lib/emscripten/tests/emtests/test_execvp.rs @@ -0,0 +1,17 @@ +#[test] +fn test_execvp() { + #[cfg(not(target_os = "windows"))] + assert_emscripten_output!( + "../../emtests/test_execvp.wasm", + "test_execvp", + vec![], + "../../emtests/test_execvp.out" + ); + #[cfg(target_os = "windows")] + assert_emscripten_output!( + "../../emtests/test_execvp_windows.wasm", + "test_execvp", + vec![], + "../../emtests/test_execvp.out" + ); +} diff --git a/lib/emscripten/tests/emtests/test_getcwd.rs b/lib/emscripten/tests/emtests/test_getcwd.rs new file mode 100644 index 000000000..ec770b505 --- /dev/null +++ b/lib/emscripten/tests/emtests/test_getcwd.rs @@ -0,0 +1,9 @@ +#[test] +fn test_getcwd() { + assert_emscripten_output!( + "../../emtests/test_getcwd.wasm", + "getcwd", + vec![], + "../../emtests/test_getcwd.out" + ); +} diff --git a/lib/emscripten/tests/emtests/test_pipe.rs b/lib/emscripten/tests/emtests/test_pipe.rs new file mode 100644 index 000000000..9ffe0c828 --- /dev/null +++ b/lib/emscripten/tests/emtests/test_pipe.rs @@ -0,0 +1,8 @@ +use crate::emtests::_common::assert_emscripten_output; + +#[test] +fn test_pipe() { + let wasm_bytes = include_bytes!("../../emtests/test_pipe.wasm"); + let expected_str = include_str!("../../emtests/test_pipe.out"); + assert_emscripten_output(wasm_bytes, expected_str); +} diff --git a/lib/llvm-backend/Cargo.toml b/lib/llvm-backend/Cargo.toml new file mode 100644 index 000000000..8be5aaa32 --- /dev/null +++ b/lib/llvm-backend/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "wasmer-llvm-backend" +version = "0.1.0" +authors = ["Lachlan Sneff "] +edition = "2018" + +[dependencies] +wasmer-runtime-core = { path = "../runtime-core", version = "0.2.1" } +inkwell = { git = "https://github.com/wasmerio/inkwell", branch = "llvm7-0" } +wasmparser = "0.28.0" +hashbrown = "0.1.8" +smallvec = "0.6.8" +goblin = "0.0.20" +libc = "0.2.49" +nix = "0.13.0" +capstone = { version = "0.5.0", optional = true } + +[build-dependencies] +cc = "1.0" +lazy_static = "1.2.0" +regex = "1.1.0" +semver = "0.9" +rustc_version = "0.2.3" + +[dev-dependencies] +wabt = "0.7.4" + +[features] +debug = ["wasmer-runtime-core/debug"] +disasm = ["capstone"] \ No newline at end of file diff --git a/lib/llvm-backend/build.rs b/lib/llvm-backend/build.rs new file mode 100644 index 000000000..ed96cae9d --- /dev/null +++ b/lib/llvm-backend/build.rs @@ -0,0 +1,221 @@ +//! This file was mostly taken from the llvm-sys crate. +//! (https://bitbucket.org/tari/llvm-sys.rs/src/21ab524ec4df1450035df895209c3f8fbeb8775f/build.rs?at=default&fileviewer=file-view-default) + +use lazy_static::lazy_static; +use regex::Regex; +use semver::Version; +use std::env; +use std::ffi::OsStr; +use std::io::{self, ErrorKind}; +use std::path::PathBuf; +use std::process::Command; + +lazy_static! { + /// LLVM version used by this version of the crate. + static ref CRATE_VERSION: Version = { + let crate_version = Version::parse(env!("CARGO_PKG_VERSION")) + .expect("Crate version is somehow not valid semver"); + Version { + major: crate_version.major / 10, + minor: crate_version.major % 10, + .. crate_version + } + }; + + static ref LLVM_CONFIG_BINARY_NAMES: Vec = { + vec![ + "llvm-config".into(), + // format!("llvm-config-{}", CRATE_VERSION.major), + // format!("llvm-config-{}.{}", CRATE_VERSION.major, CRATE_VERSION.minor), + ] + }; + + /// Filesystem path to an llvm-config binary for the correct version. + static ref LLVM_CONFIG_PATH: PathBuf = { + // Try llvm-config via PATH first. + if let Some(name) = locate_system_llvm_config() { + return name.into(); + } else { + println!("Didn't find usable system-wide LLVM."); + } + + // Did the user give us a binary path to use? If yes, try + // to use that and fail if it doesn't work. + let binary_prefix_var = "LLVM_SYS_70_PREFIX"; + + let path = if let Some(path) = env::var_os(&binary_prefix_var) { + Some(path.to_str().unwrap().to_owned()) + } else if let Ok(mut file) = std::fs::File::open(".llvmenv") { + use std::io::Read; + let mut s = String::new(); + file.read_to_string(&mut s).unwrap(); + s.truncate(s.len() - 4); + Some(s) + } else { + None + }; + + if let Some(path) = path { + for binary_name in LLVM_CONFIG_BINARY_NAMES.iter() { + let mut pb: PathBuf = path.clone().into(); + pb.push("bin"); + pb.push(binary_name); + + let ver = llvm_version(&pb) + .expect(&format!("Failed to execute {:?}", &pb)); + if is_compatible_llvm(&ver) { + return pb; + } else { + println!("LLVM binaries specified by {} are the wrong version. + (Found {}, need {}.)", binary_prefix_var, ver, *CRATE_VERSION); + } + } + } + + println!("No suitable version of LLVM was found system-wide or pointed + to by {}. + + Consider using `llvmenv` to compile an appropriate copy of LLVM, and + refer to the llvm-sys documentation for more information. + + llvm-sys: https://crates.io/crates/llvm-sys + llvmenv: https://crates.io/crates/llvmenv", binary_prefix_var); + panic!("Could not find a compatible version of LLVM"); + }; +} + +/// Try to find a system-wide version of llvm-config that is compatible with +/// this crate. +/// +/// Returns None on failure. +fn locate_system_llvm_config() -> Option<&'static str> { + for binary_name in LLVM_CONFIG_BINARY_NAMES.iter() { + match llvm_version(binary_name) { + Ok(ref version) if is_compatible_llvm(version) => { + // Compatible version found. Nice. + return Some(binary_name); + } + Ok(version) => { + // Version mismatch. Will try further searches, but warn that + // we're not using the system one. + println!( + "Found LLVM version {} on PATH, but need {}.", + version, *CRATE_VERSION + ); + } + Err(ref e) if e.kind() == ErrorKind::NotFound => { + // Looks like we failed to execute any llvm-config. Keep + // searching. + } + // Some other error, probably a weird failure. Give up. + Err(e) => panic!("Failed to search PATH for llvm-config: {}", e), + } + } + + None +} + +/// Check whether the given LLVM version is compatible with this version of +/// the crate. +fn is_compatible_llvm(llvm_version: &Version) -> bool { + let strict = env::var_os(format!( + "LLVM_SYS_{}_STRICT_VERSIONING", + env!("CARGO_PKG_VERSION_MAJOR") + )) + .is_some() + || cfg!(feature = "strict-versioning"); + if strict { + llvm_version.major == CRATE_VERSION.major && llvm_version.minor == CRATE_VERSION.minor + } else { + llvm_version.major >= CRATE_VERSION.major + || (llvm_version.major == CRATE_VERSION.major + && llvm_version.minor >= CRATE_VERSION.minor) + } +} + +/// Get the output from running `llvm-config` with the given argument. +/// +/// Lazily searches for or compiles LLVM as configured by the environment +/// variables. +fn llvm_config(arg: &str) -> String { + llvm_config_ex(&*LLVM_CONFIG_PATH, arg).expect("Surprising failure from llvm-config") +} + +/// Invoke the specified binary as llvm-config. +/// +/// Explicit version of the `llvm_config` function that bubbles errors +/// up. +fn llvm_config_ex>(binary: S, arg: &str) -> io::Result { + Command::new(binary) + .arg(arg) + .arg("--link-static") // Don't use dylib for >= 3.9 + .output() + .map(|output| { + String::from_utf8(output.stdout).expect("Output from llvm-config was not valid UTF-8") + }) +} + +/// Get the LLVM version using llvm-config. +fn llvm_version>(binary: S) -> io::Result { + let version_str = llvm_config_ex(binary.as_ref(), "--version")?; + + // LLVM isn't really semver and uses version suffixes to build + // version strings like '3.8.0svn', so limit what we try to parse + // to only the numeric bits. + let re = Regex::new(r"^(?P\d+)\.(?P\d+)(?:\.(?P\d+))??").unwrap(); + let c = re + .captures(&version_str) + .expect("Could not determine LLVM version from llvm-config."); + + // some systems don't have a patch number but Version wants it so we just append .0 if it isn't + // there + let s = match c.name("patch") { + None => format!("{}.0", &c[0]), + Some(_) => c[0].to_string(), + }; + Ok(Version::parse(&s).unwrap()) +} + +fn get_llvm_cxxflags() -> String { + let output = llvm_config("--cxxflags"); + + // llvm-config includes cflags from its own compilation with --cflags that + // may not be relevant to us. In particularly annoying cases, these might + // include flags that aren't understood by the default compiler we're + // using. Unless requested otherwise, clean CFLAGS of options that are + // known to be possibly-harmful. + let no_clean = env::var_os(format!( + "LLVM_SYS_{}_NO_CLEAN_CFLAGS", + env!("CARGO_PKG_VERSION_MAJOR") + )) + .is_some(); + if no_clean || cfg!(target_env = "msvc") { + // MSVC doesn't accept -W... options, so don't try to strip them and + // possibly strip something that should be retained. Also do nothing if + // the user requests it. + return output; + } + + output + .split(&[' ', '\n'][..]) + .filter(|word| !word.starts_with("-W")) + .filter(|word| word != &"-fno-exceptions") + .collect::>() + .join(" ") +} + +fn main() { + std::env::set_var("CXXFLAGS", get_llvm_cxxflags()); + cc::Build::new() + .cpp(true) + .file("cpp/object_loader.cpp") + .compile("llvm-backend"); + + println!("cargo:rustc-link-lib=static=llvm-backend"); + println!("cargo:rerun-if-changed=build.rs"); + + // Enable "nightly" cfg if the current compiler is nightly. + if rustc_version::version_meta().unwrap().channel == rustc_version::Channel::Nightly { + println!("cargo:rustc-cfg=nightly"); + } +} diff --git a/lib/llvm-backend/cpp/object_loader.cpp b/lib/llvm-backend/cpp/object_loader.cpp new file mode 100644 index 000000000..cef7c7214 --- /dev/null +++ b/lib/llvm-backend/cpp/object_loader.cpp @@ -0,0 +1,199 @@ +#include "object_loader.hh" +#include +#include + +extern "C" void __register_frame(uint8_t *); +extern "C" void __deregister_frame(uint8_t *); + +struct MemoryManager : llvm::RuntimeDyld::MemoryManager { +public: + MemoryManager(callbacks_t callbacks) : callbacks(callbacks) {} + + virtual ~MemoryManager() override { + deregisterEHFrames(); + // Deallocate all of the allocated memory. + callbacks.dealloc_memory(code_section.base, code_section.size); + callbacks.dealloc_memory(read_section.base, read_section.size); + callbacks.dealloc_memory(readwrite_section.base, readwrite_section.size); + } + + virtual uint8_t* allocateCodeSection(uintptr_t size, unsigned alignment, unsigned section_id, llvm::StringRef section_name) override { + return allocate_bump(code_section, code_bump_ptr, size, alignment); + } + + virtual uint8_t* allocateDataSection(uintptr_t size, unsigned alignment, unsigned section_id, llvm::StringRef section_name, bool read_only) override { + // Allocate from the read-only section or the read-write section, depending on if this allocation + // should be read-only or not. + if (read_only) { + return allocate_bump(read_section, read_bump_ptr, size, alignment); + } else { + return allocate_bump(readwrite_section, readwrite_bump_ptr, size, alignment); + } + } + + virtual void reserveAllocationSpace( + uintptr_t code_size, + uint32_t code_align, + uintptr_t read_data_size, + uint32_t read_data_align, + uintptr_t read_write_data_size, + uint32_t read_write_data_align + ) override { + auto aligner = [](uintptr_t ptr, size_t align) { + if (ptr == 0) { + return align; + } + return (ptr + align - 1) & ~(align - 1); + }; + + + uint8_t *code_ptr_out = nullptr; + size_t code_size_out = 0; + auto code_result = callbacks.alloc_memory(aligner(code_size, 4096), PROTECT_READ_WRITE, &code_ptr_out, &code_size_out); + assert(code_result == RESULT_OK); + code_section = Section { code_ptr_out, code_size_out }; + code_bump_ptr = (uintptr_t)code_ptr_out; + + uint8_t *read_ptr_out = nullptr; + size_t read_size_out = 0; + auto read_result = callbacks.alloc_memory(aligner(read_data_size, 4096), PROTECT_READ_WRITE, &read_ptr_out, &read_size_out); + assert(read_result == RESULT_OK); + read_section = Section { read_ptr_out, read_size_out }; + read_bump_ptr = (uintptr_t)read_ptr_out; + + uint8_t *readwrite_ptr_out = nullptr; + size_t readwrite_size_out = 0; + auto readwrite_result = callbacks.alloc_memory(aligner(read_write_data_size, 4096), PROTECT_READ_WRITE, &readwrite_ptr_out, &readwrite_size_out); + assert(readwrite_result == RESULT_OK); + readwrite_section = Section { readwrite_ptr_out, readwrite_size_out }; + readwrite_bump_ptr = (uintptr_t)readwrite_ptr_out; + } + + /* Turn on the `reserveAllocationSpace` callback. */ + virtual bool needsToReserveAllocationSpace() override { + return true; + } + + virtual void registerEHFrames(uint8_t* addr, uint64_t LoadAddr, size_t size) override { + eh_frame_ptr = addr; + eh_frame_size = size; + eh_frames_registered = true; + callbacks.visit_fde(addr, size, __register_frame); + } + + virtual void deregisterEHFrames() override { + if (eh_frames_registered) { + callbacks.visit_fde(eh_frame_ptr, eh_frame_size, __deregister_frame); + } + } + + virtual bool finalizeMemory(std::string *ErrMsg = nullptr) override { + auto code_result = callbacks.protect_memory(code_section.base, code_section.size, mem_protect_t::PROTECT_READ_EXECUTE); + if (code_result != RESULT_OK) { + return false; + } + + auto read_result = callbacks.protect_memory(read_section.base, read_section.size, mem_protect_t::PROTECT_READ); + if (read_result != RESULT_OK) { + return false; + } + + // The readwrite section is already mapped as read-write. + + return false; + } + + virtual void notifyObjectLoaded(llvm::RuntimeDyld &RTDyld, const llvm::object::ObjectFile &Obj) override {} +private: + struct Section { + uint8_t* base; + size_t size; + }; + + uint8_t* allocate_bump(Section& section, uintptr_t& bump_ptr, size_t size, size_t align) { + auto aligner = [](uintptr_t& ptr, size_t align) { + ptr = (ptr + align - 1) & ~(align - 1); + }; + + // Align the bump pointer to the requires alignment. + aligner(bump_ptr, align); + + auto ret_ptr = bump_ptr; + bump_ptr += size; + + assert(bump_ptr <= (uintptr_t)section.base + section.size); + + return (uint8_t*)ret_ptr; + } + + Section code_section, read_section, readwrite_section; + uintptr_t code_bump_ptr, read_bump_ptr, readwrite_bump_ptr; + uint8_t* eh_frame_ptr; + size_t eh_frame_size; + bool eh_frames_registered = false; + + callbacks_t callbacks; +}; + +struct SymbolLookup : llvm::JITSymbolResolver { +public: + SymbolLookup(callbacks_t callbacks) : callbacks(callbacks) {} + + virtual llvm::Expected lookup(const LookupSet& symbols) override { + LookupResult result; + + for (auto symbol : symbols) { + result.emplace(symbol, symbol_lookup(symbol)); + } + + return result; + } + + virtual llvm::Expected lookupFlags(const LookupSet& symbols) override { + LookupFlagsResult result; + + for (auto symbol : symbols) { + result.emplace(symbol, symbol_lookup(symbol).getFlags()); + } + + return result; + } + +private: + llvm::JITEvaluatedSymbol symbol_lookup(llvm::StringRef name) { + uint64_t addr = callbacks.lookup_vm_symbol(name.data(), name.size()); + + return llvm::JITEvaluatedSymbol(addr, llvm::JITSymbolFlags::None); + } + + callbacks_t callbacks; +}; + +WasmModule::WasmModule( + const uint8_t *object_start, + size_t object_size, + callbacks_t callbacks +) : memory_manager(std::unique_ptr(new MemoryManager(callbacks))) +{ + object_file = llvm::cantFail(llvm::object::ObjectFile::createObjectFile(llvm::MemoryBufferRef( + llvm::StringRef((const char *)object_start, object_size), "object" + ))); + + SymbolLookup symbol_resolver(callbacks); + runtime_dyld = std::unique_ptr(new llvm::RuntimeDyld(*memory_manager, symbol_resolver)); + + runtime_dyld->setProcessAllSections(true); + + runtime_dyld->loadObject(*object_file); + runtime_dyld->finalizeWithMemoryManagerLocking(); + + if (runtime_dyld->hasError()) { + std::cout << "RuntimeDyld error: " << (std::string)runtime_dyld->getErrorString() << std::endl; + abort(); + } +} + +void* WasmModule::get_func(llvm::StringRef name) const { + auto symbol = runtime_dyld->getSymbol(name); + return (void*)symbol.getAddress(); +} \ No newline at end of file diff --git a/lib/llvm-backend/cpp/object_loader.hh b/lib/llvm-backend/cpp/object_loader.hh new file mode 100644 index 000000000..d22acb919 --- /dev/null +++ b/lib/llvm-backend/cpp/object_loader.hh @@ -0,0 +1,185 @@ +#include +#include +#include +#include +#include +#include + +typedef enum { + PROTECT_NONE, + PROTECT_READ, + PROTECT_READ_WRITE, + PROTECT_READ_EXECUTE, +} mem_protect_t; + +typedef enum { + RESULT_OK, + RESULT_ALLOCATE_FAILURE, + RESULT_PROTECT_FAILURE, + RESULT_DEALLOC_FAILURE, + RESULT_OBJECT_LOAD_FAILURE, +} result_t; + +typedef result_t (*alloc_memory_t)(size_t size, mem_protect_t protect, uint8_t** ptr_out, size_t* size_out); +typedef result_t (*protect_memory_t)(uint8_t* ptr, size_t size, mem_protect_t protect); +typedef result_t (*dealloc_memory_t)(uint8_t* ptr, size_t size); +typedef uintptr_t (*lookup_vm_symbol_t)(const char* name_ptr, size_t length); +typedef void (*fde_visitor_t)(uint8_t *fde); +typedef result_t (*visit_fde_t)(uint8_t *fde, size_t size, fde_visitor_t visitor); + +typedef void (*trampoline_t)(void*, void*, void*, void*); + +typedef struct { + /* Memory management. */ + alloc_memory_t alloc_memory; + protect_memory_t protect_memory; + dealloc_memory_t dealloc_memory; + + lookup_vm_symbol_t lookup_vm_symbol; + + visit_fde_t visit_fde; +} callbacks_t; + +struct WasmException { +public: + virtual std::string description() const noexcept = 0; +}; + +struct UncatchableException : WasmException { +public: + virtual std::string description() const noexcept override { + return "Uncatchable exception"; + } +}; + +struct UserException : UncatchableException { +public: + UserException(std::string msg) : msg(msg) {} + + virtual std::string description() const noexcept override { + return std::string("user exception: ") + msg; + } +private: + std::string msg; +}; + +struct WasmTrap : UncatchableException { +public: + enum Type { + Unreachable = 0, + IncorrectCallIndirectSignature = 1, + MemoryOutOfBounds = 2, + CallIndirectOOB = 3, + IllegalArithmetic = 4, + Unknown, + }; + + WasmTrap(Type type) : type(type) {} + + virtual std::string description() const noexcept override { + std::ostringstream ss; + ss + << "WebAssembly trap:" << '\n' + << " - type: " << type << '\n'; + + return ss.str(); + } + + Type type; + +private: + friend std::ostream& operator<<(std::ostream& out, const Type& ty) { + switch (ty) { + case Type::Unreachable: + out << "unreachable"; + break; + case Type::IncorrectCallIndirectSignature: + out << "incorrect call_indirect signature"; + break; + case Type::MemoryOutOfBounds: + out << "memory access out-of-bounds"; + break; + case Type::CallIndirectOOB: + out << "call_indirect out-of-bounds"; + break; + case Type::IllegalArithmetic: + out << "illegal arithmetic operation"; + break; + case Type::Unknown: + default: + out << "unknown"; + break; + } + return out; + } +}; + +struct CatchableException : WasmException { +public: + CatchableException(uint32_t type_id, uint32_t value_num) : type_id(type_id), value_num(value_num) {} + + virtual std::string description() const noexcept override { + return "catchable exception"; + } + + uint32_t type_id, value_num; + uint64_t values[1]; +}; + +struct WasmModule { +public: + WasmModule( + const uint8_t *object_start, + size_t object_size, + callbacks_t callbacks + ); + + void *get_func(llvm::StringRef name) const; +private: + std::unique_ptr memory_manager; + std::unique_ptr object_file; + std::unique_ptr runtime_dyld; +}; + +extern "C" { + result_t module_load(const uint8_t* mem_ptr, size_t mem_size, callbacks_t callbacks, WasmModule** module_out) { + *module_out = new WasmModule(mem_ptr, mem_size, callbacks); + + return RESULT_OK; + } + + [[noreturn]] void throw_trap(WasmTrap::Type ty) { + throw WasmTrap(ty); + } + + void module_delete(WasmModule* module) { + delete module; + } + + bool invoke_trampoline( + trampoline_t trampoline, + void* ctx, + void* func, + void* params, + void* results, + WasmTrap::Type* trap_out + ) throw() { + try { + trampoline(ctx, func, params, results); + return true; + } catch(const WasmTrap& e) { + *trap_out = e.type; + return false; + } catch(const WasmException& e) { + *trap_out = WasmTrap::Type::Unknown; + return false; + } catch (...) { + *trap_out = WasmTrap::Type::Unknown; + return false; + } + } + + void* get_func_symbol(WasmModule* module, const char* name) { + return module->get_func(llvm::StringRef(name)); + } +} \ No newline at end of file diff --git a/lib/llvm-backend/src/backend.rs b/lib/llvm-backend/src/backend.rs new file mode 100644 index 000000000..742069d48 --- /dev/null +++ b/lib/llvm-backend/src/backend.rs @@ -0,0 +1,502 @@ +use crate::intrinsics::Intrinsics; +use inkwell::{ + memory_buffer::MemoryBuffer, + module::Module, + targets::{CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine}, + OptimizationLevel, +}; +use libc::{ + c_char, mmap, mprotect, munmap, MAP_ANON, MAP_PRIVATE, PROT_EXEC, PROT_NONE, PROT_READ, + PROT_WRITE, +}; +use std::{ + any::Any, + ffi::CString, + mem, + ptr::{self, NonNull}, + slice, str, + sync::Once, +}; +use wasmer_runtime_core::{ + backend::{FuncResolver, ProtectedCaller, Token, UserTrapper}, + error::{RuntimeError, RuntimeResult}, + export::Context, + module::{ModuleInfo, ModuleInner}, + structures::TypedIndex, + types::{ + FuncIndex, FuncSig, LocalFuncIndex, LocalOrImport, MemoryIndex, SigIndex, TableIndex, Type, + Value, + }, + vm::{self, ImportBacking}, + vmcalls, +}; + +#[repr(C)] +struct LLVMModule { + _private: [u8; 0], +} + +#[allow(non_camel_case_types, dead_code)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[repr(C)] +enum MemProtect { + NONE, + READ, + READ_WRITE, + READ_EXECUTE, +} + +#[allow(non_camel_case_types, dead_code)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[repr(C)] +enum LLVMResult { + OK, + ALLOCATE_FAILURE, + PROTECT_FAILURE, + DEALLOC_FAILURE, + OBJECT_LOAD_FAILURE, +} + +#[repr(C)] +enum WasmTrapType { + Unreachable = 0, + IncorrectCallIndirectSignature = 1, + MemoryOutOfBounds = 2, + CallIndirectOOB = 3, + IllegalArithmetic = 4, + Unknown, +} + +#[repr(C)] +struct Callbacks { + alloc_memory: extern "C" fn(usize, MemProtect, &mut *mut u8, &mut usize) -> LLVMResult, + protect_memory: extern "C" fn(*mut u8, usize, MemProtect) -> LLVMResult, + dealloc_memory: extern "C" fn(*mut u8, usize) -> LLVMResult, + + lookup_vm_symbol: extern "C" fn(*const c_char, usize) -> *const vm::Func, + visit_fde: extern "C" fn(*mut u8, usize, extern "C" fn(*mut u8)), +} + +extern "C" { + fn module_load( + mem_ptr: *const u8, + mem_size: usize, + callbacks: Callbacks, + module_out: &mut *mut LLVMModule, + ) -> LLVMResult; + fn module_delete(module: *mut LLVMModule); + fn get_func_symbol(module: *mut LLVMModule, name: *const c_char) -> *const vm::Func; + + fn throw_trap(ty: i32); + + fn invoke_trampoline( + trampoline: unsafe extern "C" fn(*mut vm::Ctx, *const vm::Func, *const u64, *mut u64), + vmctx_ptr: *mut vm::Ctx, + func_ptr: *const vm::Func, + params: *const u64, + results: *mut u64, + trap_out: *mut WasmTrapType, + ) -> bool; +} + +fn get_callbacks() -> Callbacks { + fn round_up_to_page_size(size: usize) -> usize { + (size + (4096 - 1)) & !(4096 - 1) + } + + extern "C" fn alloc_memory( + size: usize, + protect: MemProtect, + ptr_out: &mut *mut u8, + size_out: &mut usize, + ) -> LLVMResult { + let size = round_up_to_page_size(size); + let ptr = unsafe { + mmap( + ptr::null_mut(), + size, + match protect { + MemProtect::NONE => PROT_NONE, + MemProtect::READ => PROT_READ, + MemProtect::READ_WRITE => PROT_READ | PROT_WRITE, + MemProtect::READ_EXECUTE => PROT_READ | PROT_EXEC, + }, + MAP_PRIVATE | MAP_ANON, + -1, + 0, + ) + }; + if ptr as isize == -1 { + return LLVMResult::ALLOCATE_FAILURE; + } + *ptr_out = ptr as _; + *size_out = size; + LLVMResult::OK + } + + extern "C" fn protect_memory(ptr: *mut u8, size: usize, protect: MemProtect) -> LLVMResult { + let res = unsafe { + mprotect( + ptr as _, + round_up_to_page_size(size), + match protect { + MemProtect::NONE => PROT_NONE, + MemProtect::READ => PROT_READ, + MemProtect::READ_WRITE => PROT_READ | PROT_WRITE, + MemProtect::READ_EXECUTE => PROT_READ | PROT_EXEC, + }, + ) + }; + + if res == 0 { + LLVMResult::OK + } else { + LLVMResult::PROTECT_FAILURE + } + } + + extern "C" fn dealloc_memory(ptr: *mut u8, size: usize) -> LLVMResult { + let res = unsafe { munmap(ptr as _, round_up_to_page_size(size)) }; + + if res == 0 { + LLVMResult::OK + } else { + LLVMResult::DEALLOC_FAILURE + } + } + + extern "C" fn lookup_vm_symbol(name_ptr: *const c_char, length: usize) -> *const vm::Func { + #[cfg(target_os = "macos")] + macro_rules! fn_name { + ($s:literal) => { + concat!("_", $s) + }; + } + + #[cfg(not(target_os = "macos"))] + macro_rules! fn_name { + ($s:literal) => { + $s + }; + } + + let name_slice = unsafe { slice::from_raw_parts(name_ptr as *const u8, length) }; + let name = str::from_utf8(name_slice).unwrap(); + + match name { + fn_name!("vm.memory.grow.dynamic.local") => vmcalls::local_dynamic_memory_grow as _, + fn_name!("vm.memory.size.dynamic.local") => vmcalls::local_dynamic_memory_size as _, + fn_name!("vm.memory.grow.static.local") => vmcalls::local_static_memory_grow as _, + fn_name!("vm.memory.size.static.local") => vmcalls::local_static_memory_size as _, + + fn_name!("vm.exception.trap") => throw_trap as _, + + _ => ptr::null(), + } + } + + extern "C" fn visit_fde(fde: *mut u8, size: usize, visitor: extern "C" fn(*mut u8)) { + unsafe { + crate::platform::visit_fde(fde, size, visitor); + } + } + + Callbacks { + alloc_memory, + protect_memory, + dealloc_memory, + lookup_vm_symbol, + visit_fde, + } +} + +unsafe impl Send for LLVMBackend {} +unsafe impl Sync for LLVMBackend {} + +pub struct LLVMBackend { + module: *mut LLVMModule, + #[allow(dead_code)] + memory_buffer: MemoryBuffer, +} + +impl LLVMBackend { + pub fn new(module: Module, intrinsics: Intrinsics) -> (Self, LLVMProtectedCaller) { + Target::initialize_x86(&InitializationConfig { + asm_parser: true, + asm_printer: true, + base: true, + disassembler: true, + info: true, + machine_code: true, + }); + let triple = TargetMachine::get_default_triple().to_string(); + let target = Target::from_triple(&triple).unwrap(); + let target_machine = target + .create_target_machine( + &triple, + &TargetMachine::get_host_cpu_name().to_string(), + &TargetMachine::get_host_cpu_features().to_string(), + OptimizationLevel::Aggressive, + RelocMode::PIC, + CodeModel::Default, + ) + .unwrap(); + + let memory_buffer = target_machine + .write_to_memory_buffer(&module, FileType::Object) + .unwrap(); + let mem_buf_slice = memory_buffer.as_slice(); + + let callbacks = get_callbacks(); + let mut module: *mut LLVMModule = ptr::null_mut(); + + let res = unsafe { + module_load( + mem_buf_slice.as_ptr(), + mem_buf_slice.len(), + callbacks, + &mut module, + ) + }; + + static SIGNAL_HANDLER_INSTALLED: Once = Once::new(); + + SIGNAL_HANDLER_INSTALLED.call_once(|| unsafe { + crate::platform::install_signal_handler(); + }); + + if res != LLVMResult::OK { + panic!("failed to load object") + } + + ( + Self { + module, + memory_buffer, + }, + LLVMProtectedCaller { module }, + ) + } + + pub fn get_func( + &self, + info: &ModuleInfo, + local_func_index: LocalFuncIndex, + ) -> Option> { + let index = info.imported_functions.len() + local_func_index.index(); + let name = if cfg!(target_os = "macos") { + format!("_fn{}", index) + } else { + format!("fn{}", index) + }; + + let c_str = CString::new(name).ok()?; + let ptr = unsafe { get_func_symbol(self.module, c_str.as_ptr()) }; + + NonNull::new(ptr as _) + } +} + +impl Drop for LLVMBackend { + fn drop(&mut self) { + unsafe { module_delete(self.module) } + } +} + +impl FuncResolver for LLVMBackend { + fn get( + &self, + module: &ModuleInner, + local_func_index: LocalFuncIndex, + ) -> Option> { + self.get_func(&module.info, local_func_index) + } +} + +struct Placeholder; + +unsafe impl Send for LLVMProtectedCaller {} +unsafe impl Sync for LLVMProtectedCaller {} + +pub struct LLVMProtectedCaller { + module: *mut LLVMModule, +} + +impl ProtectedCaller for LLVMProtectedCaller { + fn call( + &self, + module: &ModuleInner, + func_index: FuncIndex, + params: &[Value], + import_backing: &ImportBacking, + vmctx: *mut vm::Ctx, + _: Token, + ) -> RuntimeResult> { + let (func_ptr, ctx, signature, sig_index) = + get_func_from_index(&module, import_backing, func_index); + + let vmctx_ptr = match ctx { + Context::External(external_vmctx) => external_vmctx, + Context::Internal => vmctx, + }; + + assert!( + signature.returns().len() <= 1, + "multi-value returns not yet supported" + ); + + assert!( + signature.check_param_value_types(params), + "incorrect signature" + ); + + let param_vec: Vec = params + .iter() + .map(|val| match val { + Value::I32(x) => *x as u64, + Value::I64(x) => *x as u64, + Value::F32(x) => x.to_bits() as u64, + Value::F64(x) => x.to_bits(), + }) + .collect(); + + let mut return_vec = vec![0; signature.returns().len()]; + + let trampoline: unsafe extern "C" fn(*mut vm::Ctx, *const vm::Func, *const u64, *mut u64) = unsafe { + let name = if cfg!(target_os = "macos") { + format!("_trmp{}", sig_index.index()) + } else { + format!("trmp{}", sig_index.index()) + }; + + let c_str = CString::new(name).unwrap(); + let symbol = get_func_symbol(self.module, c_str.as_ptr()); + assert!(!symbol.is_null()); + + mem::transmute(symbol) + }; + + let mut trap_out = WasmTrapType::Unknown; + + // Here we go. + let success = unsafe { + invoke_trampoline( + trampoline, + vmctx_ptr, + func_ptr, + param_vec.as_ptr(), + return_vec.as_mut_ptr(), + &mut trap_out, + ) + }; + + if success { + Ok(return_vec + .iter() + .zip(signature.returns().iter()) + .map(|(&x, ty)| match ty { + Type::I32 => Value::I32(x as i32), + Type::I64 => Value::I64(x as i64), + Type::F32 => Value::F32(f32::from_bits(x as u32)), + Type::F64 => Value::F64(f64::from_bits(x as u64)), + }) + .collect()) + } else { + Err(match trap_out { + WasmTrapType::Unreachable => RuntimeError::Trap { + msg: "unreachable".into(), + }, + WasmTrapType::IncorrectCallIndirectSignature => RuntimeError::Trap { + msg: "uncorrect call_indirect signature".into(), + }, + WasmTrapType::MemoryOutOfBounds => RuntimeError::Trap { + msg: "memory out-of-bounds access".into(), + }, + WasmTrapType::CallIndirectOOB => RuntimeError::Trap { + msg: "call_indirect out-of-bounds".into(), + }, + WasmTrapType::IllegalArithmetic => RuntimeError::Trap { + msg: "illegal arithmetic operation".into(), + }, + WasmTrapType::Unknown => RuntimeError::Trap { + msg: "unknown trap".into(), + }, + }) + } + } + + fn get_early_trapper(&self) -> Box { + Box::new(Placeholder) + } +} + +impl UserTrapper for Placeholder { + unsafe fn do_early_trap(&self, _data: Box) -> ! { + unimplemented!("do early trap") + } +} + +fn get_func_from_index<'a>( + module: &'a ModuleInner, + import_backing: &ImportBacking, + func_index: FuncIndex, +) -> (*const vm::Func, Context, &'a FuncSig, SigIndex) { + let sig_index = *module + .info + .func_assoc + .get(func_index) + .expect("broken invariant, incorrect func index"); + + let (func_ptr, ctx) = match func_index.local_or_import(&module.info) { + LocalOrImport::Local(local_func_index) => ( + module + .func_resolver + .get(&module, local_func_index) + .expect("broken invariant, func resolver not synced with module.exports") + .cast() + .as_ptr() as *const _, + Context::Internal, + ), + LocalOrImport::Import(imported_func_index) => { + let imported_func = import_backing.imported_func(imported_func_index); + ( + imported_func.func as *const _, + Context::External(imported_func.vmctx), + ) + } + }; + + let signature = &module.info.signatures[sig_index]; + + (func_ptr, ctx, signature, sig_index) +} + +#[cfg(feature = "disasm")] +unsafe fn disass_ptr(ptr: *const u8, size: usize, inst_count: usize) { + use capstone::arch::BuildsCapstone; + let mut cs = capstone::Capstone::new() // Call builder-pattern + .x86() // X86 architecture + .mode(capstone::arch::x86::ArchMode::Mode64) // 64-bit mode + .detail(true) // Generate extra instruction details + .build() + .expect("Failed to create Capstone object"); + + // Get disassembled instructions + let insns = cs + .disasm_count( + std::slice::from_raw_parts(ptr, size), + ptr as u64, + inst_count, + ) + .expect("Failed to disassemble"); + + println!("count = {}", insns.len()); + for insn in insns.iter() { + println!( + "0x{:x}: {:6} {}", + insn.address(), + insn.mnemonic().unwrap_or(""), + insn.op_str().unwrap_or("") + ); + } +} diff --git a/lib/llvm-backend/src/code.rs b/lib/llvm-backend/src/code.rs new file mode 100644 index 000000000..173262b67 --- /dev/null +++ b/lib/llvm-backend/src/code.rs @@ -0,0 +1,2466 @@ +use inkwell::{ + builder::Builder, + context::Context, + module::{Linkage, Module}, + passes::PassManager, + types::{BasicType, BasicTypeEnum, FunctionType, IntType, PointerType}, + values::{BasicValue, FloatValue, FunctionValue, IntValue, PhiValue, PointerValue}, + AddressSpace, FloatPredicate, IntPredicate, +}; +use smallvec::SmallVec; +use wasmer_runtime_core::{ + memory::MemoryType, + module::{ExportIndex, ModuleInfo}, + structures::{Map, SliceMap, TypedIndex}, + types::{ + FuncIndex, FuncSig, GlobalIndex, LocalFuncIndex, LocalOrImport, MemoryIndex, SigIndex, + TableIndex, Type, + }, +}; +use wasmparser::{ + BinaryReaderError, CodeSectionReader, LocalsReader, MemoryImmediate, Operator, OperatorsReader, +}; + +use crate::intrinsics::{CtxType, GlobalCache, Intrinsics, MemoryCache}; +use crate::read_info::type_to_type; +use crate::state::{ControlFrame, IfElseState, State}; +use crate::trampolines::generate_trampolines; + +fn func_sig_to_llvm(context: &Context, intrinsics: &Intrinsics, sig: &FuncSig) -> FunctionType { + let user_param_types = sig.params().iter().map(|&ty| type_to_llvm(intrinsics, ty)); + + let param_types: Vec<_> = std::iter::once(intrinsics.ctx_ptr_ty.as_basic_type_enum()) + .chain(user_param_types) + .collect(); + + match sig.returns() { + &[] => intrinsics.void_ty.fn_type(¶m_types, false), + &[single_value] => type_to_llvm(intrinsics, single_value).fn_type(¶m_types, false), + returns @ _ => { + let basic_types: Vec<_> = returns + .iter() + .map(|&ty| type_to_llvm(intrinsics, ty)) + .collect(); + + context + .struct_type(&basic_types, false) + .fn_type(¶m_types, false) + } + } +} + +fn type_to_llvm(intrinsics: &Intrinsics, ty: Type) -> BasicTypeEnum { + match ty { + Type::I32 => intrinsics.i32_ty.as_basic_type_enum(), + Type::I64 => intrinsics.i64_ty.as_basic_type_enum(), + Type::F32 => intrinsics.f32_ty.as_basic_type_enum(), + Type::F64 => intrinsics.f64_ty.as_basic_type_enum(), + } +} + +pub fn parse_function_bodies( + info: &ModuleInfo, + code_reader: CodeSectionReader, +) -> Result<(Module, Intrinsics), BinaryReaderError> { + let context = Context::create(); + let module = context.create_module("module"); + let builder = context.create_builder(); + + let intrinsics = Intrinsics::declare(&module, &context); + + let personality_func = module.add_function( + "__gxx_personality_v0", + intrinsics.i32_ty.fn_type(&[], false), + Some(Linkage::External), + ); + + let signatures: Map = info + .signatures + .iter() + .map(|(_, sig)| func_sig_to_llvm(&context, &intrinsics, sig)) + .collect(); + let functions: Map = info + .func_assoc + .iter() + .skip(info.imported_functions.len()) + .map(|(func_index, &sig_index)| { + let func = module.add_function( + &format!("fn{}", func_index.index()), + signatures[sig_index], + Some(Linkage::External), + ); + func.set_personality_function(personality_func); + func + }) + .collect(); + + for (local_func_index, body) in code_reader.into_iter().enumerate() { + let body = body?; + + let locals_reader = body.get_locals_reader()?; + let op_reader = body.get_operators_reader()?; + + parse_function( + &context, + &module, + &builder, + &intrinsics, + info, + &signatures, + &functions, + LocalFuncIndex::new(local_func_index), + locals_reader, + op_reader, + ) + .map_err(|e| BinaryReaderError { + message: e.message, + offset: local_func_index, + })?; + } + + // module.print_to_stderr(); + + generate_trampolines(info, &signatures, &module, &context, &builder, &intrinsics); + + let pass_manager = PassManager::create_for_module(); + // pass_manager.add_verifier_pass(); + pass_manager.add_function_inlining_pass(); + pass_manager.add_promote_memory_to_register_pass(); + pass_manager.add_cfg_simplification_pass(); + // pass_manager.add_instruction_combining_pass(); + pass_manager.add_aggressive_inst_combiner_pass(); + pass_manager.add_merged_load_store_motion_pass(); + // pass_manager.add_sccp_pass(); + // pass_manager.add_gvn_pass(); + pass_manager.add_new_gvn_pass(); + pass_manager.add_aggressive_dce_pass(); + pass_manager.run_on_module(&module); + + // module.print_to_stderr(); + + Ok((module, intrinsics)) +} + +fn parse_function( + context: &Context, + module: &Module, + builder: &Builder, + intrinsics: &Intrinsics, + info: &ModuleInfo, + signatures: &SliceMap, + functions: &SliceMap, + func_index: LocalFuncIndex, + locals_reader: LocalsReader, + op_reader: OperatorsReader, +) -> Result<(), BinaryReaderError> { + let sig_index = info.func_assoc[func_index.convert_up(info)]; + let func_sig = &info.signatures[sig_index]; + let llvm_sig = &signatures[sig_index]; + + let function = functions[func_index]; + let mut state = State::new(); + let entry_block = context.append_basic_block(&function, "entry"); + + let return_block = context.append_basic_block(&function, "return"); + builder.position_at_end(&return_block); + + let phis: SmallVec<[PhiValue; 1]> = func_sig + .returns() + .iter() + .map(|&wasmer_ty| type_to_llvm(intrinsics, wasmer_ty)) + .map(|ty| builder.build_phi(ty, &state.var_name())) + .collect(); + + state.push_block(return_block, phis); + builder.position_at_end(&entry_block); + + let mut locals = Vec::with_capacity(locals_reader.get_count() as usize); // TODO fix capacity + + locals.extend( + function + .get_param_iter() + .skip(1) + .enumerate() + .map(|(index, param)| { + let ty = param.get_type(); + + let alloca = builder.build_alloca(ty, &format!("local{}", index)); + builder.build_store(alloca, param); + alloca + }), + ); + + let param_len = locals.len(); + + let mut local_idx = 0; + for (index, local) in locals_reader.into_iter().enumerate() { + let (count, ty) = local?; + let wasmer_ty = type_to_type(ty)?; + let ty = type_to_llvm(intrinsics, wasmer_ty); + + let default_value = match wasmer_ty { + Type::I32 => intrinsics.i32_zero.as_basic_value_enum(), + Type::I64 => intrinsics.i64_zero.as_basic_value_enum(), + Type::F32 => intrinsics.f32_zero.as_basic_value_enum(), + Type::F64 => intrinsics.f64_zero.as_basic_value_enum(), + }; + + for _ in 0..count { + let alloca = builder.build_alloca(ty, &format!("local{}", param_len + local_idx)); + + builder.build_store(alloca, default_value); + + locals.push(alloca); + local_idx += 1; + } + } + + let start_of_code_block = context.append_basic_block(&function, "start_of_code"); + let entry_end_inst = builder.build_unconditional_branch(&start_of_code_block); + builder.position_at_end(&start_of_code_block); + + let cache_builder = context.create_builder(); + cache_builder.position_before(&entry_end_inst); + let mut ctx = intrinsics.ctx(info, builder, &function, cache_builder); + let mut unreachable_depth = 0; + + for op in op_reader { + let op = op?; + if !state.reachable { + match op { + Operator::Block { ty: _ } | Operator::Loop { ty: _ } | Operator::If { ty: _ } => { + unreachable_depth += 1; + continue; + } + Operator::Else => { + if unreachable_depth != 0 { + continue; + } + } + Operator::End => { + if unreachable_depth != 0 { + unreachable_depth -= 1; + continue; + } + } + _ => { + continue; + } + } + } + + match op { + /*************************** + * Control Flow instructions. + * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#control-flow-instructions + ***************************/ + Operator::Block { ty } => { + let current_block = builder.get_insert_block().ok_or(BinaryReaderError { + message: "not currently in a block", + offset: -1isize as usize, + })?; + + let end_block = context.append_basic_block(&function, "end"); + builder.position_at_end(&end_block); + + let phis = if let Ok(wasmer_ty) = type_to_type(ty) { + let llvm_ty = type_to_llvm(intrinsics, wasmer_ty); + [llvm_ty] + .iter() + .map(|&ty| builder.build_phi(ty, &state.var_name())) + .collect() + } else { + SmallVec::new() + }; + + state.push_block(end_block, phis); + builder.position_at_end(¤t_block); + } + Operator::Loop { ty } => { + let loop_body = context.append_basic_block(&function, "loop_body"); + let loop_next = context.append_basic_block(&function, "loop_outer"); + + builder.build_unconditional_branch(&loop_body); + + builder.position_at_end(&loop_next); + let phis = if let Ok(wasmer_ty) = type_to_type(ty) { + let llvm_ty = type_to_llvm(intrinsics, wasmer_ty); + [llvm_ty] + .iter() + .map(|&ty| builder.build_phi(ty, &state.var_name())) + .collect() + } else { + SmallVec::new() + }; + + builder.position_at_end(&loop_body); + state.push_loop(loop_body, loop_next, phis); + } + Operator::Br { relative_depth } => { + let frame = state.frame_at_depth(relative_depth)?; + + let current_block = builder.get_insert_block().ok_or(BinaryReaderError { + message: "not currently in a block", + offset: -1isize as usize, + })?; + + let value_len = if frame.is_loop() { + 0 + } else { + frame.phis().len() + }; + + let values = state.peekn(value_len)?; + + // For each result of the block we're branching to, + // pop a value off the value stack and load it into + // the corresponding phi. + for (phi, value) in frame.phis().iter().zip(values.iter()) { + phi.add_incoming(&[(value, ¤t_block)]); + } + + builder.build_unconditional_branch(frame.br_dest()); + + state.popn(value_len)?; + state.reachable = false; + } + Operator::BrIf { relative_depth } => { + let cond = state.pop1()?; + let frame = state.frame_at_depth(relative_depth)?; + + let current_block = builder.get_insert_block().ok_or(BinaryReaderError { + message: "not currently in a block", + offset: -1isize as usize, + })?; + + let value_len = if frame.is_loop() { + 0 + } else { + frame.phis().len() + }; + + let param_stack = state.peekn(value_len)?; + + for (phi, value) in frame.phis().iter().zip(param_stack.iter()) { + phi.add_incoming(&[(value, ¤t_block)]); + } + + let else_block = context.append_basic_block(&function, "else"); + + let cond_value = builder.build_int_compare( + IntPredicate::NE, + cond.into_int_value(), + intrinsics.i32_zero, + &state.var_name(), + ); + builder.build_conditional_branch(cond_value, frame.br_dest(), &else_block); + builder.position_at_end(&else_block); + } + Operator::BrTable { ref table } => { + let current_block = builder.get_insert_block().ok_or(BinaryReaderError { + message: "not currently in a block", + offset: -1isize as usize, + })?; + + let (label_depths, default_depth) = table.read_table()?; + + let index = state.pop1()?; + + let default_frame = state.frame_at_depth(default_depth)?; + + let args = if default_frame.is_loop() { + &[] + } else { + let res_len = default_frame.phis().len(); + state.peekn(res_len)? + }; + + for (phi, value) in default_frame.phis().iter().zip(args.iter()) { + phi.add_incoming(&[(value, ¤t_block)]); + } + + let cases: Vec<_> = label_depths + .iter() + .enumerate() + .map(|(case_index, &depth)| { + let frame = state.frame_at_depth(depth)?; + let case_index_literal = + context.i32_type().const_int(case_index as u64, false); + + for (phi, value) in frame.phis().iter().zip(args.iter()) { + phi.add_incoming(&[(value, ¤t_block)]); + } + + Ok((case_index_literal, frame.br_dest())) + }) + .collect::>()?; + + builder.build_switch(index.into_int_value(), default_frame.br_dest(), &cases[..]); + + state.popn(args.len())?; + state.reachable = false; + } + Operator::If { ty } => { + let current_block = builder.get_insert_block().ok_or(BinaryReaderError { + message: "not currently in a block", + offset: -1isize as usize, + })?; + let if_then_block = context.append_basic_block(&function, "if_then"); + let if_else_block = context.append_basic_block(&function, "if_else"); + let end_block = context.append_basic_block(&function, "if_end"); + + let end_phis = { + builder.position_at_end(&end_block); + + let phis = if let Ok(wasmer_ty) = type_to_type(ty) { + let llvm_ty = type_to_llvm(intrinsics, wasmer_ty); + [llvm_ty] + .iter() + .map(|&ty| builder.build_phi(ty, &state.var_name())) + .collect() + } else { + SmallVec::new() + }; + + builder.position_at_end(¤t_block); + phis + }; + + let cond = state.pop1()?; + + let cond_value = builder.build_int_compare( + IntPredicate::NE, + cond.into_int_value(), + intrinsics.i32_zero, + &state.var_name(), + ); + + builder.build_conditional_branch(cond_value, &if_then_block, &if_else_block); + builder.position_at_end(&if_then_block); + state.push_if(if_then_block, if_else_block, end_block, end_phis); + } + Operator::Else => { + if state.reachable { + let frame = state.frame_at_depth(0)?; + builder.build_unconditional_branch(frame.code_after()); + let current_block = builder.get_insert_block().ok_or(BinaryReaderError { + message: "not currently in a block", + offset: -1isize as usize, + })?; + + for phi in frame.phis().to_vec().iter().rev() { + let value = state.pop1()?; + phi.add_incoming(&[(&value, ¤t_block)]) + } + } + + let (if_else_block, if_else_state) = if let ControlFrame::IfElse { + if_else, + if_else_state, + .. + } = state.frame_at_depth_mut(0)? + { + (if_else, if_else_state) + } else { + unreachable!() + }; + + *if_else_state = IfElseState::Else; + + builder.position_at_end(if_else_block); + state.reachable = true; + } + + Operator::End => { + let frame = state.pop_frame()?; + let current_block = builder.get_insert_block().ok_or(BinaryReaderError { + message: "not currently in a block", + offset: -1isize as usize, + })?; + + if state.reachable { + builder.build_unconditional_branch(frame.code_after()); + + for phi in frame.phis().iter().rev() { + let value = state.pop1()?; + phi.add_incoming(&[(&value, ¤t_block)]); + } + } + + if let ControlFrame::IfElse { + if_else, + next, + phis, + if_else_state, + .. + } = &frame + { + if let IfElseState::If = if_else_state { + builder.position_at_end(if_else); + builder.build_unconditional_branch(next); + } + } + + builder.position_at_end(frame.code_after()); + state.reset_stack(&frame); + + state.reachable = true; + + // Push each phi value to the value stack. + for phi in frame.phis() { + if phi.count_incoming() != 0 { + state.push1(phi.as_basic_value()); + } else { + let basic_ty = phi.as_basic_value().get_type(); + let placeholder_value = match basic_ty { + BasicTypeEnum::IntType(int_ty) => { + int_ty.const_int(0, false).as_basic_value_enum() + } + BasicTypeEnum::FloatType(float_ty) => { + float_ty.const_float(0.0).as_basic_value_enum() + } + _ => unimplemented!(), + }; + state.push1(placeholder_value); + phi.as_instruction().erase_from_basic_block(); + } + } + } + Operator::Return => { + let frame = state.outermost_frame()?; + let current_block = builder.get_insert_block().ok_or(BinaryReaderError { + message: "not currently in a block", + offset: -1isize as usize, + })?; + + builder.build_unconditional_branch(frame.br_dest()); + + let phis = frame.phis().to_vec(); + + for phi in phis.iter() { + let arg = state.pop1()?; + phi.add_incoming(&[(&arg, ¤t_block)]); + } + + state.reachable = false; + } + + Operator::Unreachable => { + // Emit an unreachable instruction. + // If llvm cannot prove that this is never touched, + // it will emit a `ud2` instruction on x86_64 arches. + + builder.build_call( + intrinsics.throw_trap, + &[intrinsics.trap_unreachable], + "throw", + ); + builder.build_unreachable(); + + state.reachable = false; + } + + /*************************** + * Basic instructions. + * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#basic-instructions + ***************************/ + Operator::Nop => { + // Do nothing. + } + Operator::Drop => { + state.pop1()?; + } + + // Generate const values. + Operator::I32Const { value } => { + let i = intrinsics.i32_ty.const_int(value as u64, false); + state.push1(i); + } + Operator::I64Const { value } => { + let i = intrinsics.i64_ty.const_int(value as u64, false); + state.push1(i); + } + Operator::F32Const { value } => { + let bits = intrinsics.i32_ty.const_int(value.bits() as u64, false); + let space = + builder.build_alloca(intrinsics.f32_ty.as_basic_type_enum(), "const_space"); + let i32_space = + builder.build_pointer_cast(space, intrinsics.i32_ptr_ty, "i32_space"); + builder.build_store(i32_space, bits); + let f = builder.build_load(space, "f"); + state.push1(f); + } + Operator::F64Const { value } => { + let bits = intrinsics.i64_ty.const_int(value.bits(), false); + let space = + builder.build_alloca(intrinsics.f64_ty.as_basic_type_enum(), "const_space"); + let i64_space = + builder.build_pointer_cast(space, intrinsics.i64_ptr_ty, "i32_space"); + builder.build_store(i64_space, bits); + let f = builder.build_load(space, "f"); + state.push1(f); + } + + // Operate on locals. + Operator::GetLocal { local_index } => { + let pointer_value = locals[local_index as usize]; + let v = builder.build_load(pointer_value, &state.var_name()); + state.push1(v); + } + Operator::SetLocal { local_index } => { + let pointer_value = locals[local_index as usize]; + let v = state.pop1()?; + builder.build_store(pointer_value, v); + } + Operator::TeeLocal { local_index } => { + let pointer_value = locals[local_index as usize]; + let v = state.peek1()?; + builder.build_store(pointer_value, v); + } + + Operator::GetGlobal { global_index } => { + let index = GlobalIndex::new(global_index as usize); + let global_cache = ctx.global_cache(index); + match global_cache { + GlobalCache::Const { value } => { + state.push1(value); + } + GlobalCache::Mut { ptr_to_value } => { + let value = builder.build_load(ptr_to_value, "global_value"); + state.push1(value); + } + } + } + Operator::SetGlobal { global_index } => { + let value = state.pop1()?; + let index = GlobalIndex::new(global_index as usize); + let global_cache = ctx.global_cache(index); + match global_cache { + GlobalCache::Mut { ptr_to_value } => { + builder.build_store(ptr_to_value, value); + } + GlobalCache::Const { value: _ } => { + unreachable!("cannot set non-mutable globals") + } + } + } + + Operator::Select => { + let (v1, v2, cond) = state.pop3()?; + let cond_value = builder.build_int_compare( + IntPredicate::NE, + cond.into_int_value(), + intrinsics.i32_zero, + &state.var_name(), + ); + let res = builder.build_select(cond_value, v1, v2, &state.var_name()); + state.push1(res); + } + Operator::Call { function_index } => { + let func_index = FuncIndex::new(function_index as usize); + let sigindex = info.func_assoc[func_index]; + let llvm_sig = signatures[sigindex]; + let func_sig = &info.signatures[sigindex]; + + let call_site = match func_index.local_or_import(info) { + LocalOrImport::Local(local_func_index) => { + let params: Vec<_> = [ctx.basic()] + .iter() + .chain(state.peekn(func_sig.params().len())?.iter()) + .map(|v| *v) + .collect(); + + let func_ptr = ctx.local_func(local_func_index, llvm_sig); + + builder.build_call(func_ptr, ¶ms, &state.var_name()) + } + LocalOrImport::Import(import_func_index) => { + let (func_ptr_untyped, ctx_ptr) = ctx.imported_func(import_func_index); + let params: Vec<_> = [ctx_ptr.as_basic_value_enum()] + .iter() + .chain(state.peekn(func_sig.params().len())?.iter()) + .map(|v| *v) + .collect(); + + let func_ptr_ty = llvm_sig.ptr_type(AddressSpace::Generic); + + let func_ptr = builder.build_pointer_cast( + func_ptr_untyped, + func_ptr_ty, + "typed_func_ptr", + ); + + builder.build_call(func_ptr, ¶ms, &state.var_name()) + } + }; + + state.popn(func_sig.params().len())?; + + if let Some(basic_value) = call_site.try_as_basic_value().left() { + match func_sig.returns().len() { + 1 => state.push1(basic_value), + count @ _ => { + // This is a multi-value return. + let struct_value = basic_value.into_struct_value(); + for i in 0..(count as u32) { + let value = builder + .build_extract_value(struct_value, i, &state.var_name()) + .unwrap(); + state.push1(value); + } + } + } + } + } + Operator::CallIndirect { index, table_index } => { + let sig_index = SigIndex::new(index as usize); + let expected_dynamic_sigindex = ctx.dynamic_sigindex(sig_index); + let (table_base, table_bound) = ctx.table(TableIndex::new(table_index as usize)); + let func_index = state.pop1()?.into_int_value(); + + // We assume the table has the `anyfunc` element type. + let casted_table_base = builder.build_pointer_cast( + table_base, + intrinsics.anyfunc_ty.ptr_type(AddressSpace::Generic), + "casted_table_base", + ); + + let anyfunc_struct_ptr = unsafe { + builder.build_in_bounds_gep( + casted_table_base, + &[func_index], + "anyfunc_struct_ptr", + ) + }; + + // Load things from the anyfunc data structure. + let (func_ptr, ctx_ptr, found_dynamic_sigindex) = unsafe { + ( + builder + .build_load( + builder.build_struct_gep(anyfunc_struct_ptr, 0, "func_ptr_ptr"), + "func_ptr", + ) + .into_pointer_value(), + builder.build_load( + builder.build_struct_gep(anyfunc_struct_ptr, 1, "ctx_ptr_ptr"), + "ctx_ptr", + ), + builder + .build_load( + builder.build_struct_gep(anyfunc_struct_ptr, 2, "sigindex_ptr"), + "sigindex", + ) + .into_int_value(), + ) + }; + + let truncated_table_bounds = builder.build_int_truncate( + table_bound, + intrinsics.i32_ty, + "truncated_table_bounds", + ); + + // First, check if the index is outside of the table bounds. + let index_in_bounds = builder.build_int_compare( + IntPredicate::ULT, + func_index, + truncated_table_bounds, + "index_in_bounds", + ); + + let index_in_bounds = builder + .build_call( + intrinsics.expect_i1, + &[ + index_in_bounds.as_basic_value_enum(), + intrinsics.i1_ty.const_int(1, false).as_basic_value_enum(), + ], + "index_in_bounds_expect", + ) + .try_as_basic_value() + .left() + .unwrap() + .into_int_value(); + + let in_bounds_continue_block = + context.append_basic_block(&function, "in_bounds_continue_block"); + let not_in_bounds_block = + context.append_basic_block(&function, "not_in_bounds_block"); + builder.build_conditional_branch( + index_in_bounds, + &in_bounds_continue_block, + ¬_in_bounds_block, + ); + builder.position_at_end(¬_in_bounds_block); + builder.build_call( + intrinsics.throw_trap, + &[intrinsics.trap_call_indirect_oob], + "throw", + ); + builder.build_unreachable(); + builder.position_at_end(&in_bounds_continue_block); + + // Next, check if the signature id is correct. + + let sigindices_equal = builder.build_int_compare( + IntPredicate::EQ, + expected_dynamic_sigindex, + found_dynamic_sigindex, + "sigindices_equal", + ); + + // Tell llvm that `expected_dynamic_sigindex` should equal `found_dynamic_sigindex`. + let sigindices_equal = builder + .build_call( + intrinsics.expect_i1, + &[ + sigindices_equal.as_basic_value_enum(), + intrinsics.i1_ty.const_int(1, false).as_basic_value_enum(), + ], + "sigindices_equal_expect", + ) + .try_as_basic_value() + .left() + .unwrap() + .into_int_value(); + + let continue_block = context.append_basic_block(&function, "continue_block"); + let sigindices_notequal_block = + context.append_basic_block(&function, "sigindices_notequal_block"); + builder.build_conditional_branch( + sigindices_equal, + &continue_block, + &sigindices_notequal_block, + ); + + builder.position_at_end(&sigindices_notequal_block); + builder.build_call( + intrinsics.throw_trap, + &[intrinsics.trap_call_indirect_sig], + "throw", + ); + builder.build_unreachable(); + builder.position_at_end(&continue_block); + + let wasmer_fn_sig = &info.signatures[sig_index]; + let fn_ty = signatures[sig_index]; + + let pushed_args = state.popn_save(wasmer_fn_sig.params().len())?; + + let args: Vec<_> = std::iter::once(ctx_ptr) + .chain(pushed_args.into_iter()) + .collect(); + + let typed_func_ptr = builder.build_pointer_cast( + func_ptr, + fn_ty.ptr_type(AddressSpace::Generic), + "typed_func_ptr", + ); + + let call_site = builder.build_call(typed_func_ptr, &args, "indirect_call"); + + match wasmer_fn_sig.returns() { + [] => {} + [_] => { + let value = call_site.try_as_basic_value().left().unwrap(); + state.push1(value); + } + returns @ _ => unimplemented!("multi-value returns"), + } + } + + /*************************** + * Integer Arithmetic instructions. + * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#integer-arithmetic-instructions + ***************************/ + Operator::I32Add | Operator::I64Add => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_int_add(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32Sub | Operator::I64Sub => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_int_sub(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32Mul | Operator::I64Mul => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_int_mul(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32DivS | Operator::I64DivS => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + + trap_if_zero_or_overflow(builder, intrinsics, context, &function, v1, v2); + + let res = builder.build_int_signed_div(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32DivU | Operator::I64DivU => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + + trap_if_zero(builder, intrinsics, context, &function, v2); + + let res = builder.build_int_unsigned_div(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32RemS | Operator::I64RemS => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + + trap_if_zero(builder, intrinsics, context, &function, v2); + + let res = builder.build_int_signed_rem(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32RemU | Operator::I64RemU => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + + trap_if_zero(builder, intrinsics, context, &function, v2); + + let res = builder.build_int_unsigned_rem(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32And | Operator::I64And => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_and(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32Or | Operator::I64Or => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_or(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32Xor | Operator::I64Xor => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_xor(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32Shl | Operator::I64Shl => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_left_shift(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::I32ShrS | Operator::I64ShrS => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_right_shift(v1, v2, true, &state.var_name()); + state.push1(res); + } + Operator::I32ShrU | Operator::I64ShrU => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let res = builder.build_right_shift(v1, v2, false, &state.var_name()); + state.push1(res); + } + Operator::I32Rotl => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let lhs = builder.build_left_shift(v1, v2, &state.var_name()); + let rhs = { + let int_width = intrinsics.i32_ty.const_int(32 as u64, false); + let rhs = builder.build_int_sub(int_width, v2, &state.var_name()); + builder.build_right_shift(v1, rhs, false, &state.var_name()) + }; + let res = builder.build_or(lhs, rhs, &state.var_name()); + state.push1(res); + } + Operator::I64Rotl => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let lhs = builder.build_left_shift(v1, v2, &state.var_name()); + let rhs = { + let int_width = intrinsics.i64_ty.const_int(64 as u64, false); + let rhs = builder.build_int_sub(int_width, v2, &state.var_name()); + builder.build_right_shift(v1, rhs, false, &state.var_name()) + }; + let res = builder.build_or(lhs, rhs, &state.var_name()); + state.push1(res); + } + Operator::I32Rotr => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let lhs = builder.build_right_shift(v1, v2, false, &state.var_name()); + let rhs = { + let int_width = intrinsics.i32_ty.const_int(32 as u64, false); + let rhs = builder.build_int_sub(int_width, v2, &state.var_name()); + builder.build_left_shift(v1, rhs, &state.var_name()) + }; + let res = builder.build_or(lhs, rhs, &state.var_name()); + state.push1(res); + } + Operator::I64Rotr => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let lhs = builder.build_right_shift(v1, v2, false, &state.var_name()); + let rhs = { + let int_width = intrinsics.i64_ty.const_int(64 as u64, false); + let rhs = builder.build_int_sub(int_width, v2, &state.var_name()); + builder.build_left_shift(v1, rhs, &state.var_name()) + }; + let res = builder.build_or(lhs, rhs, &state.var_name()); + state.push1(res); + } + Operator::I32Clz => { + let input = state.pop1()?; + let ensure_defined_zero = intrinsics + .i1_ty + .const_int(1 as u64, false) + .as_basic_value_enum(); + let res = builder + .build_call( + intrinsics.ctlz_i32, + &[input, ensure_defined_zero], + &state.var_name(), + ) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::I64Clz => { + let input = state.pop1()?; + let ensure_defined_zero = intrinsics + .i1_ty + .const_int(1 as u64, false) + .as_basic_value_enum(); + let res = builder + .build_call( + intrinsics.ctlz_i64, + &[input, ensure_defined_zero], + &state.var_name(), + ) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::I32Ctz => { + let input = state.pop1()?; + let ensure_defined_zero = intrinsics + .i1_ty + .const_int(1 as u64, false) + .as_basic_value_enum(); + let res = builder + .build_call( + intrinsics.cttz_i32, + &[input, ensure_defined_zero], + &state.var_name(), + ) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::I64Ctz => { + let input = state.pop1()?; + let ensure_defined_zero = intrinsics + .i1_ty + .const_int(1 as u64, false) + .as_basic_value_enum(); + let res = builder + .build_call( + intrinsics.cttz_i64, + &[input, ensure_defined_zero], + &state.var_name(), + ) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::I32Popcnt => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.ctpop_i32, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::I64Popcnt => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.ctpop_i64, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::I32Eqz => { + let input = state.pop1()?.into_int_value(); + let cond = builder.build_int_compare( + IntPredicate::EQ, + input, + intrinsics.i32_zero, + &state.var_name(), + ); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I64Eqz => { + let input = state.pop1()?.into_int_value(); + let cond = builder.build_int_compare( + IntPredicate::EQ, + input, + intrinsics.i64_zero, + &state.var_name(), + ); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + + /*************************** + * Floating-Point Arithmetic instructions. + * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#floating-point-arithmetic-instructions + ***************************/ + Operator::F32Add | Operator::F64Add => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let res = builder.build_float_add(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::F32Sub | Operator::F64Sub => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let res = builder.build_float_sub(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::F32Mul | Operator::F64Mul => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let res = builder.build_float_mul(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::F32Div | Operator::F64Div => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let res = builder.build_float_div(v1, v2, &state.var_name()); + state.push1(res); + } + Operator::F32Sqrt => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.sqrt_f32, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Sqrt => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.sqrt_f64, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F32Min => { + let (v1, v2) = state.pop2()?; + let res = builder + .build_call(intrinsics.minimum_f32, &[v1, v2], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Min => { + let (v1, v2) = state.pop2()?; + let res = builder + .build_call(intrinsics.minimum_f64, &[v1, v2], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F32Max => { + let (v1, v2) = state.pop2()?; + let res = builder + .build_call(intrinsics.maximum_f32, &[v1, v2], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Max => { + let (v1, v2) = state.pop2()?; + let res = builder + .build_call(intrinsics.maximum_f64, &[v1, v2], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F32Ceil => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.ceil_f32, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Ceil => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.ceil_f64, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F32Floor => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.floor_f32, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Floor => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.floor_f64, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F32Trunc => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.trunc_f32, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Trunc => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.trunc_f64, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F32Nearest => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.nearbyint_f32, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Nearest => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.nearbyint_f64, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F32Abs => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.fabs_f32, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Abs => { + let input = state.pop1()?; + let res = builder + .build_call(intrinsics.fabs_f64, &[input], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F32Neg | Operator::F64Neg => { + let input = state.pop1()?.into_float_value(); + let res = builder.build_float_neg(input, &state.var_name()); + state.push1(res); + } + Operator::F32Copysign => { + let (mag, sgn) = state.pop2()?; + let res = builder + .build_call(intrinsics.copysign_f32, &[mag, sgn], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + Operator::F64Copysign => { + let (msg, sgn) = state.pop2()?; + let res = builder + .build_call(intrinsics.copysign_f64, &[msg, sgn], &state.var_name()) + .try_as_basic_value() + .left() + .unwrap(); + state.push1(res); + } + + /*************************** + * Integer Comparison instructions. + * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#integer-comparison-instructions + ***************************/ + Operator::I32Eq | Operator::I64Eq => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let cond = builder.build_int_compare(IntPredicate::EQ, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32Ne | Operator::I64Ne => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let cond = builder.build_int_compare(IntPredicate::NE, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32LtS | Operator::I64LtS => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let cond = builder.build_int_compare(IntPredicate::SLT, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32LtU | Operator::I64LtU => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let cond = builder.build_int_compare(IntPredicate::ULT, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32LeS | Operator::I64LeS => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let cond = builder.build_int_compare(IntPredicate::SLE, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32LeU | Operator::I64LeU => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let cond = builder.build_int_compare(IntPredicate::ULE, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32GtS | Operator::I64GtS => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let cond = builder.build_int_compare(IntPredicate::SGT, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32GtU | Operator::I64GtU => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let cond = builder.build_int_compare(IntPredicate::UGT, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32GeS | Operator::I64GeS => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let cond = builder.build_int_compare(IntPredicate::SGE, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32GeU | Operator::I64GeU => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_int_value(), v2.into_int_value()); + let cond = builder.build_int_compare(IntPredicate::UGE, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + + /*************************** + * Floating-Point Comparison instructions. + * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#floating-point-comparison-instructions + ***************************/ + Operator::F32Eq | Operator::F64Eq => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let cond = + builder.build_float_compare(FloatPredicate::OEQ, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::F32Ne | Operator::F64Ne => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let cond = + builder.build_float_compare(FloatPredicate::UNE, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::F32Lt | Operator::F64Lt => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let cond = + builder.build_float_compare(FloatPredicate::OLT, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::F32Le | Operator::F64Le => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let cond = + builder.build_float_compare(FloatPredicate::OLE, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::F32Gt | Operator::F64Gt => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let cond = + builder.build_float_compare(FloatPredicate::OGT, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::F32Ge | Operator::F64Ge => { + let (v1, v2) = state.pop2()?; + let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); + let cond = + builder.build_float_compare(FloatPredicate::OGE, v1, v2, &state.var_name()); + let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + + /*************************** + * Conversion instructions. + * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#conversion-instructions + ***************************/ + Operator::I32WrapI64 => { + let v1 = state.pop1()?.into_int_value(); + let res = builder.build_int_truncate(v1, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I64ExtendSI32 => { + let v1 = state.pop1()?.into_int_value(); + let res = builder.build_int_s_extend(v1, intrinsics.i64_ty, &state.var_name()); + state.push1(res); + } + Operator::I64ExtendUI32 => { + let v1 = state.pop1()?.into_int_value(); + let res = builder.build_int_z_extend(v1, intrinsics.i64_ty, &state.var_name()); + state.push1(res); + } + Operator::I32TruncSF32 => { + let v1 = state.pop1()?.into_float_value(); + trap_if_not_representatable_as_int( + builder, + intrinsics, + context, + &function, + -2147483904.0, + 2147483648.0, + v1, + ); + let res = + builder.build_float_to_signed_int(v1, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32TruncSF64 => { + let v1 = state.pop1()?.into_float_value(); + trap_if_not_representatable_as_int( + builder, + intrinsics, + context, + &function, + -2147483649.0, + 2147483648.0, + v1, + ); + let res = + builder.build_float_to_signed_int(v1, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32TruncSSatF32 | Operator::I32TruncSSatF64 => { + let v1 = state.pop1()?.into_float_value(); + let res = + builder.build_float_to_signed_int(v1, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I64TruncSF32 => { + let v1 = state.pop1()?.into_float_value(); + trap_if_not_representatable_as_int( + builder, + intrinsics, + context, + &function, + -9223373136366403584.0, + 9223372036854775808.0, + v1, + ); + let res = + builder.build_float_to_signed_int(v1, intrinsics.i64_ty, &state.var_name()); + state.push1(res); + } + Operator::I64TruncSF64 => { + let v1 = state.pop1()?.into_float_value(); + trap_if_not_representatable_as_int( + builder, + intrinsics, + context, + &function, + -9223372036854777856.0, + 9223372036854775808.0, + v1, + ); + let res = + builder.build_float_to_signed_int(v1, intrinsics.i64_ty, &state.var_name()); + state.push1(res); + } + Operator::I64TruncSSatF32 | Operator::I64TruncSSatF64 => { + let v1 = state.pop1()?.into_float_value(); + let res = + builder.build_float_to_signed_int(v1, intrinsics.i64_ty, &state.var_name()); + state.push1(res); + } + Operator::I32TruncUF32 => { + let v1 = state.pop1()?.into_float_value(); + trap_if_not_representatable_as_int( + builder, + intrinsics, + context, + &function, + -1.0, + 4294967296.0, + v1, + ); + let res = + builder.build_float_to_unsigned_int(v1, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32TruncUF64 => { + let v1 = state.pop1()?.into_float_value(); + trap_if_not_representatable_as_int( + builder, + intrinsics, + context, + &function, + -1.0, + 4294967296.0, + v1, + ); + let res = + builder.build_float_to_unsigned_int(v1, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I32TruncUSatF32 | Operator::I32TruncUSatF64 => { + let v1 = state.pop1()?.into_float_value(); + let res = + builder.build_float_to_unsigned_int(v1, intrinsics.i32_ty, &state.var_name()); + state.push1(res); + } + Operator::I64TruncUF32 => { + let v1 = state.pop1()?.into_float_value(); + trap_if_not_representatable_as_int( + builder, + intrinsics, + context, + &function, + -1.0, + 18446744073709551616.0, + v1, + ); + let res = + builder.build_float_to_unsigned_int(v1, intrinsics.i64_ty, &state.var_name()); + state.push1(res); + } + Operator::I64TruncUF64 => { + let v1 = state.pop1()?.into_float_value(); + trap_if_not_representatable_as_int( + builder, + intrinsics, + context, + &function, + -1.0, + 18446744073709551616.0, + v1, + ); + let res = + builder.build_float_to_unsigned_int(v1, intrinsics.i64_ty, &state.var_name()); + state.push1(res); + } + Operator::I64TruncUSatF32 | Operator::I64TruncUSatF64 => { + let v1 = state.pop1()?.into_float_value(); + let res = + builder.build_float_to_unsigned_int(v1, intrinsics.i64_ty, &state.var_name()); + state.push1(res); + } + Operator::F32DemoteF64 => { + let v1 = state.pop1()?.into_float_value(); + let res = builder.build_float_trunc(v1, intrinsics.f32_ty, &state.var_name()); + state.push1(res); + } + Operator::F64PromoteF32 => { + let v1 = state.pop1()?.into_float_value(); + let res = builder.build_float_ext(v1, intrinsics.f64_ty, &state.var_name()); + state.push1(res); + } + Operator::F32ConvertSI32 | Operator::F32ConvertSI64 => { + let v1 = state.pop1()?.into_int_value(); + let res = + builder.build_signed_int_to_float(v1, intrinsics.f32_ty, &state.var_name()); + state.push1(res); + } + Operator::F64ConvertSI32 | Operator::F64ConvertSI64 => { + let v1 = state.pop1()?.into_int_value(); + let res = + builder.build_signed_int_to_float(v1, intrinsics.f64_ty, &state.var_name()); + state.push1(res); + } + Operator::F32ConvertUI32 | Operator::F32ConvertUI64 => { + let v1 = state.pop1()?.into_int_value(); + let res = + builder.build_unsigned_int_to_float(v1, intrinsics.f32_ty, &state.var_name()); + state.push1(res); + } + Operator::F64ConvertUI32 | Operator::F64ConvertUI64 => { + let v1 = state.pop1()?.into_int_value(); + let res = + builder.build_unsigned_int_to_float(v1, intrinsics.f64_ty, &state.var_name()); + state.push1(res); + } + Operator::I32ReinterpretF32 => { + let v = state.pop1()?; + let space = + builder.build_alloca(intrinsics.i32_ty.as_basic_type_enum(), &state.var_name()); + let f32_space = + builder.build_pointer_cast(space, intrinsics.f32_ptr_ty, &state.var_name()); + builder.build_store(f32_space, v); + let int = builder.build_load(space, &state.var_name()); + state.push1(int); + } + Operator::I64ReinterpretF64 => { + let v = state.pop1()?; + let space = + builder.build_alloca(intrinsics.i64_ty.as_basic_type_enum(), &state.var_name()); + let f64_space = + builder.build_pointer_cast(space, intrinsics.f64_ptr_ty, &state.var_name()); + builder.build_store(f64_space, v); + let int = builder.build_load(space, &state.var_name()); + state.push1(int); + } + Operator::F32ReinterpretI32 => { + let v = state.pop1()?; + let space = + builder.build_alloca(intrinsics.f32_ty.as_basic_type_enum(), &state.var_name()); + let i32_space = + builder.build_pointer_cast(space, intrinsics.i32_ptr_ty, &state.var_name()); + builder.build_store(i32_space, v); + let f = builder.build_load(space, &state.var_name()); + state.push1(f); + } + Operator::F64ReinterpretI64 => { + let v = state.pop1()?; + let space = + builder.build_alloca(intrinsics.f64_ty.as_basic_type_enum(), &state.var_name()); + let i64_space = + builder.build_pointer_cast(space, intrinsics.i64_ptr_ty, &state.var_name()); + builder.build_store(i64_space, v); + let f = builder.build_load(space, &state.var_name()); + state.push1(f); + } + + /*************************** + * Sign-extension operators. + * https://github.com/WebAssembly/sign-extension-ops/blob/master/proposals/sign-extension-ops/Overview.md + ***************************/ + Operator::I32Extend8S => { + let value = state.pop1()?.into_int_value(); + let narrow_value = + builder.build_int_truncate(value, intrinsics.i8_ty, &state.var_name()); + let extended_value = + builder.build_int_s_extend(narrow_value, intrinsics.i32_ty, &state.var_name()); + state.push1(extended_value); + } + Operator::I32Extend16S => { + let value = state.pop1()?.into_int_value(); + let narrow_value = + builder.build_int_truncate(value, intrinsics.i16_ty, &state.var_name()); + let extended_value = + builder.build_int_s_extend(narrow_value, intrinsics.i32_ty, &state.var_name()); + state.push1(extended_value); + } + Operator::I64Extend8S => { + let value = state.pop1()?.into_int_value(); + let narrow_value = + builder.build_int_truncate(value, intrinsics.i8_ty, &state.var_name()); + let extended_value = + builder.build_int_s_extend(narrow_value, intrinsics.i64_ty, &state.var_name()); + state.push1(extended_value); + } + Operator::I64Extend16S => { + let value = state.pop1()?.into_int_value(); + let narrow_value = + builder.build_int_truncate(value, intrinsics.i16_ty, &state.var_name()); + let extended_value = + builder.build_int_s_extend(narrow_value, intrinsics.i64_ty, &state.var_name()); + state.push1(extended_value); + } + Operator::I64Extend32S => { + let value = state.pop1()?.into_int_value(); + let narrow_value = + builder.build_int_truncate(value, intrinsics.i32_ty, &state.var_name()); + let extended_value = + builder.build_int_s_extend(narrow_value, intrinsics.i64_ty, &state.var_name()); + state.push1(extended_value); + } + + /*************************** + * Load and Store instructions. + * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#load-and-store-instructions + ***************************/ + Operator::I32Load { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i32_ptr_ty, + )?; + let result = builder.build_load(effective_address, &state.var_name()); + state.push1(result); + } + Operator::I64Load { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i64_ptr_ty, + )?; + let result = builder.build_load(effective_address, &state.var_name()); + state.push1(result); + } + Operator::F32Load { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.f32_ptr_ty, + )?; + let result = builder.build_load(effective_address, &state.var_name()); + state.push1(result); + } + Operator::F64Load { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.f64_ptr_ty, + )?; + let result = builder.build_load(effective_address, &state.var_name()); + state.push1(result); + } + + Operator::I32Store { memarg } => { + let value = state.pop1()?; + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i32_ptr_ty, + )?; + builder.build_store(effective_address, value); + } + Operator::I64Store { memarg } => { + let value = state.pop1()?; + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i64_ptr_ty, + )?; + builder.build_store(effective_address, value); + } + Operator::F32Store { memarg } => { + let value = state.pop1()?; + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.f32_ptr_ty, + )?; + builder.build_store(effective_address, value); + } + Operator::F64Store { memarg } => { + let value = state.pop1()?; + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.f64_ptr_ty, + )?; + builder.build_store(effective_address, value); + } + + Operator::I32Load8S { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i8_ptr_ty, + )?; + let narrow_result = builder + .build_load(effective_address, &state.var_name()) + .into_int_value(); + let result = + builder.build_int_s_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); + state.push1(result); + } + Operator::I32Load16S { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i16_ptr_ty, + )?; + let narrow_result = builder + .build_load(effective_address, &state.var_name()) + .into_int_value(); + let result = + builder.build_int_s_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); + state.push1(result); + } + Operator::I64Load8S { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i8_ptr_ty, + )?; + let narrow_result = builder + .build_load(effective_address, &state.var_name()) + .into_int_value(); + let result = + builder.build_int_s_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); + state.push1(result); + } + Operator::I64Load16S { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i16_ptr_ty, + )?; + let narrow_result = builder + .build_load(effective_address, &state.var_name()) + .into_int_value(); + let result = + builder.build_int_s_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); + state.push1(result); + } + Operator::I64Load32S { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i32_ptr_ty, + )?; + let narrow_result = builder + .build_load(effective_address, &state.var_name()) + .into_int_value(); + let result = + builder.build_int_s_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); + state.push1(result); + } + + Operator::I32Load8U { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i8_ptr_ty, + )?; + let narrow_result = builder + .build_load(effective_address, &state.var_name()) + .into_int_value(); + let result = + builder.build_int_z_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); + state.push1(result); + } + Operator::I32Load16U { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i16_ptr_ty, + )?; + let narrow_result = builder + .build_load(effective_address, &state.var_name()) + .into_int_value(); + let result = + builder.build_int_z_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); + state.push1(result); + } + Operator::I64Load8U { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i8_ptr_ty, + )?; + let narrow_result = builder + .build_load(effective_address, &state.var_name()) + .into_int_value(); + let result = + builder.build_int_z_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); + state.push1(result); + } + Operator::I64Load16U { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i16_ptr_ty, + )?; + let narrow_result = builder + .build_load(effective_address, &state.var_name()) + .into_int_value(); + let result = + builder.build_int_z_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); + state.push1(result); + } + Operator::I64Load32U { memarg } => { + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i32_ptr_ty, + )?; + let narrow_result = builder + .build_load(effective_address, &state.var_name()) + .into_int_value(); + let result = + builder.build_int_z_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); + state.push1(result); + } + + Operator::I32Store8 { memarg } | Operator::I64Store8 { memarg } => { + let value = state.pop1()?.into_int_value(); + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i8_ptr_ty, + )?; + let narrow_value = + builder.build_int_truncate(value, intrinsics.i8_ty, &state.var_name()); + builder.build_store(effective_address, narrow_value); + } + Operator::I32Store16 { memarg } | Operator::I64Store16 { memarg } => { + let value = state.pop1()?.into_int_value(); + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i16_ptr_ty, + )?; + let narrow_value = + builder.build_int_truncate(value, intrinsics.i16_ty, &state.var_name()); + builder.build_store(effective_address, narrow_value); + } + Operator::I64Store32 { memarg } => { + let value = state.pop1()?.into_int_value(); + let effective_address = resolve_memory_ptr( + builder, + intrinsics, + context, + &function, + &mut state, + &mut ctx, + memarg, + intrinsics.i32_ptr_ty, + )?; + let narrow_value = + builder.build_int_truncate(value, intrinsics.i32_ty, &state.var_name()); + builder.build_store(effective_address, narrow_value); + } + + Operator::MemoryGrow { reserved } => { + let memory_index = MemoryIndex::new(reserved as usize); + let func_value = match memory_index.local_or_import(info) { + LocalOrImport::Local(local_mem_index) => { + let mem_desc = &info.memories[local_mem_index]; + match mem_desc.memory_type() { + MemoryType::Dynamic => intrinsics.memory_grow_dynamic_local, + MemoryType::Static => intrinsics.memory_grow_static_local, + MemoryType::SharedStatic => intrinsics.memory_grow_shared_local, + } + } + LocalOrImport::Import(import_mem_index) => { + let mem_desc = &info.imported_memories[import_mem_index].1; + match mem_desc.memory_type() { + MemoryType::Dynamic => intrinsics.memory_grow_dynamic_import, + MemoryType::Static => intrinsics.memory_grow_static_import, + MemoryType::SharedStatic => intrinsics.memory_grow_shared_import, + } + } + }; + + let memory_index_const = intrinsics + .i32_ty + .const_int(reserved as u64, false) + .as_basic_value_enum(); + let delta = state.pop1()?; + + let result = builder.build_call( + func_value, + &[ctx.basic(), memory_index_const, delta], + &state.var_name(), + ); + state.push1(result.try_as_basic_value().left().unwrap()); + } + Operator::MemorySize { reserved } => { + let memory_index = MemoryIndex::new(reserved as usize); + let func_value = match memory_index.local_or_import(info) { + LocalOrImport::Local(local_mem_index) => { + let mem_desc = &info.memories[local_mem_index]; + match mem_desc.memory_type() { + MemoryType::Dynamic => intrinsics.memory_size_dynamic_local, + MemoryType::Static => intrinsics.memory_size_static_local, + MemoryType::SharedStatic => intrinsics.memory_size_shared_local, + } + } + LocalOrImport::Import(import_mem_index) => { + let mem_desc = &info.imported_memories[import_mem_index].1; + match mem_desc.memory_type() { + MemoryType::Dynamic => intrinsics.memory_size_dynamic_import, + MemoryType::Static => intrinsics.memory_size_static_import, + MemoryType::SharedStatic => intrinsics.memory_size_shared_import, + } + } + }; + + let memory_index_const = intrinsics + .i32_ty + .const_int(reserved as u64, false) + .as_basic_value_enum(); + let result = builder.build_call( + func_value, + &[ctx.basic(), memory_index_const], + &state.var_name(), + ); + state.push1(result.try_as_basic_value().left().unwrap()); + } + op @ _ => { + unimplemented!("{:?}", op); + } + } + } + + let results = state.popn_save(func_sig.returns().len())?; + + match results.as_slice() { + [] => { + builder.build_return(None); + } + [one_value] => { + builder.build_return(Some(one_value)); + } + returns @ _ => { + // let struct_ty = llvm_sig.get_return_type().as_struct_type(); + // let ret_struct = struct_ty.const_zero(); + unimplemented!("multi-value returns not yet implemented") + } + } + + Ok(()) +} + +fn trap_if_not_representatable_as_int( + builder: &Builder, + intrinsics: &Intrinsics, + context: &Context, + function: &FunctionValue, + lower_bounds: f64, + upper_bound: f64, + value: FloatValue, +) { + enum FloatSize { + Bits32, + Bits64, + } + + let failure_block = context.append_basic_block(function, "conversion_failure_block"); + let continue_block = context.append_basic_block(function, "conversion_success_block"); + + let float_ty = value.get_type(); + let (int_ty, float_ptr_ty, float_size) = if float_ty == intrinsics.f32_ty { + (intrinsics.i32_ty, intrinsics.f32_ptr_ty, FloatSize::Bits32) + } else if float_ty == intrinsics.f64_ty { + (intrinsics.i64_ty, intrinsics.f64_ptr_ty, FloatSize::Bits64) + } else { + unreachable!() + }; + + let (exponent, invalid_exponent) = { + let float_bits = { + let space = builder.build_alloca(int_ty, "space"); + let float_ptr = builder.build_pointer_cast(space, float_ptr_ty, "float_ptr"); + builder.build_store(float_ptr, value); + builder.build_load(space, "float_bits").into_int_value() + }; + + let (shift_amount, exponent_mask, invalid_exponent) = match float_size { + FloatSize::Bits32 => (23, 0b01111111100000000000000000000000, 0b11111111), + FloatSize::Bits64 => ( + 52, + 0b0111111111110000000000000000000000000000000000000000000000000000, + 0b11111111111, + ), + }; + + let masked = builder.build_and( + float_bits, + int_ty.const_int(exponent_mask, false), + "masked_bits", + ); + + ( + builder.build_right_shift( + float_bits, + int_ty.const_int(shift_amount, false), + false, + "exponent", + ), + invalid_exponent, + ) + }; + + let is_invalid_float = builder.build_or( + builder.build_int_compare( + IntPredicate::EQ, + exponent, + int_ty.const_int(invalid_exponent, false), + "is_not_normal", + ), + builder.build_or( + builder.build_float_compare( + FloatPredicate::ULT, + value, + float_ty.const_float(lower_bounds), + "less_than_lower_bounds", + ), + builder.build_float_compare( + FloatPredicate::UGT, + value, + float_ty.const_float(upper_bound), + "greater_than_upper_bounds", + ), + "float_not_in_bounds", + ), + "is_invalid_float", + ); + + let is_invalid_float = builder + .build_call( + intrinsics.expect_i1, + &[ + is_invalid_float.as_basic_value_enum(), + intrinsics.i1_ty.const_int(0, false).as_basic_value_enum(), + ], + "is_invalid_float_expect", + ) + .try_as_basic_value() + .left() + .unwrap() + .into_int_value(); + + builder.build_conditional_branch(is_invalid_float, &failure_block, &continue_block); + builder.position_at_end(&failure_block); + builder.build_call( + intrinsics.throw_trap, + &[intrinsics.trap_illegal_arithmetic], + "throw", + ); + builder.build_unreachable(); + builder.position_at_end(&continue_block); +} + +fn trap_if_zero_or_overflow( + builder: &Builder, + intrinsics: &Intrinsics, + context: &Context, + function: &FunctionValue, + left: IntValue, + right: IntValue, +) { + let int_type = left.get_type(); + + let (min_value, neg_one_value) = if int_type == intrinsics.i32_ty { + let min_value = int_type.const_int(i32::min_value() as u64, false); + let neg_one_value = int_type.const_int(-1i32 as u32 as u64, false); + (min_value, neg_one_value) + } else if int_type == intrinsics.i64_ty { + let min_value = int_type.const_int(i64::min_value() as u64, false); + let neg_one_value = int_type.const_int(-1i64 as u64, false); + (min_value, neg_one_value) + } else { + unreachable!() + }; + + let should_trap = builder.build_or( + builder.build_int_compare( + IntPredicate::EQ, + right, + int_type.const_int(0, false), + "divisor_is_zero", + ), + builder.build_and( + builder.build_int_compare(IntPredicate::EQ, left, min_value, "left_is_min"), + builder.build_int_compare(IntPredicate::EQ, right, neg_one_value, "right_is_neg_one"), + "div_will_overflow", + ), + "div_should_trap", + ); + + let should_trap = builder + .build_call( + intrinsics.expect_i1, + &[ + should_trap.as_basic_value_enum(), + intrinsics.i1_ty.const_int(0, false).as_basic_value_enum(), + ], + "should_trap_expect", + ) + .try_as_basic_value() + .left() + .unwrap() + .into_int_value(); + + let shouldnt_trap_block = context.append_basic_block(function, "shouldnt_trap_block"); + let should_trap_block = context.append_basic_block(function, "should_trap_block"); + builder.build_conditional_branch(should_trap, &should_trap_block, &shouldnt_trap_block); + builder.position_at_end(&should_trap_block); + builder.build_call( + intrinsics.throw_trap, + &[intrinsics.trap_illegal_arithmetic], + "throw", + ); + builder.build_unreachable(); + builder.position_at_end(&shouldnt_trap_block); +} + +fn trap_if_zero( + builder: &Builder, + intrinsics: &Intrinsics, + context: &Context, + function: &FunctionValue, + value: IntValue, +) { + let int_type = value.get_type(); + let should_trap = builder.build_int_compare( + IntPredicate::EQ, + value, + int_type.const_int(0, false), + "divisor_is_zero", + ); + + let should_trap = builder + .build_call( + intrinsics.expect_i1, + &[ + should_trap.as_basic_value_enum(), + intrinsics.i1_ty.const_int(0, false).as_basic_value_enum(), + ], + "should_trap_expect", + ) + .try_as_basic_value() + .left() + .unwrap() + .into_int_value(); + + let shouldnt_trap_block = context.append_basic_block(function, "shouldnt_trap_block"); + let should_trap_block = context.append_basic_block(function, "should_trap_block"); + builder.build_conditional_branch(should_trap, &should_trap_block, &shouldnt_trap_block); + builder.position_at_end(&should_trap_block); + builder.build_call( + intrinsics.throw_trap, + &[intrinsics.trap_illegal_arithmetic], + "throw", + ); + builder.build_unreachable(); + builder.position_at_end(&shouldnt_trap_block); +} + +fn resolve_memory_ptr( + builder: &Builder, + intrinsics: &Intrinsics, + context: &Context, + function: &FunctionValue, + state: &mut State, + ctx: &mut CtxType, + memarg: MemoryImmediate, + ptr_ty: PointerType, +) -> Result { + // Ignore alignment hint for the time being. + let imm_offset = intrinsics.i64_ty.const_int(memarg.offset as u64, false); + let var_offset_i32 = state.pop1()?.into_int_value(); + let var_offset = + builder.build_int_z_extend(var_offset_i32, intrinsics.i64_ty, &state.var_name()); + let effective_offset = builder.build_int_add(var_offset, imm_offset, &state.var_name()); + let memory_cache = ctx.memory(MemoryIndex::new(0)); + + let mem_base_int = match memory_cache { + MemoryCache::Dynamic { + ptr_to_base_ptr, + ptr_to_bounds, + } => { + let base = builder + .build_load(ptr_to_base_ptr, "base") + .into_pointer_value(); + let bounds = builder.build_load(ptr_to_bounds, "bounds").into_int_value(); + + let base_as_int = builder.build_ptr_to_int(base, intrinsics.i64_ty, "base_as_int"); + + let base_in_bounds = builder.build_int_compare( + IntPredicate::ULT, + effective_offset, + bounds, + "base_in_bounds", + ); + + let base_in_bounds = builder + .build_call( + intrinsics.expect_i1, + &[ + base_in_bounds.as_basic_value_enum(), + intrinsics.i1_ty.const_int(1, false).as_basic_value_enum(), + ], + "base_in_bounds_expect", + ) + .try_as_basic_value() + .left() + .unwrap() + .into_int_value(); + + let in_bounds_continue_block = + context.append_basic_block(function, "in_bounds_continue_block"); + let not_in_bounds_block = context.append_basic_block(function, "not_in_bounds_block"); + builder.build_conditional_branch( + base_in_bounds, + &in_bounds_continue_block, + ¬_in_bounds_block, + ); + builder.position_at_end(¬_in_bounds_block); + builder.build_call( + intrinsics.throw_trap, + &[intrinsics.trap_memory_oob], + "throw", + ); + builder.build_unreachable(); + builder.position_at_end(&in_bounds_continue_block); + + base_as_int + } + MemoryCache::Static { + base_ptr, + bounds: _, + } => builder.build_ptr_to_int(base_ptr, intrinsics.i64_ty, "base_as_int"), + }; + + let effective_address_int = + builder.build_int_add(mem_base_int, effective_offset, &state.var_name()); + Ok(builder.build_int_to_ptr(effective_address_int, ptr_ty, &state.var_name())) +} diff --git a/lib/llvm-backend/src/example.rs b/lib/llvm-backend/src/example.rs new file mode 100644 index 000000000..5ce3c9a5f --- /dev/null +++ b/lib/llvm-backend/src/example.rs @@ -0,0 +1,61 @@ +use inkwell::OptimizationLevel; +use inkwell::builder::Builder; +use inkwell::context::Context; +use inkwell::execution_engine::{ExecutionEngine, JitFunction}; +use inkwell::module::Module; +use inkwell::targets::{InitializationConfig, Target}; +use std::error::Error; + +/// Convenience type alias for the `sum` function. +/// +/// Calling this is innately `unsafe` because there's no guarantee it doesn't +/// do `unsafe` operations internally. +type SumFunc = unsafe extern "C" fn(u64, u64, u64) -> u64; + +#[test] +fn test_sum() -> Result<(), Box> { + let context = Context::create(); + let module = context.create_module("sum"); + let builder = context.create_builder(); + let execution_engine = module.create_jit_execution_engine(OptimizationLevel::Aggressive)?; + + let sum = jit_compile_sum(&context, &module, &builder, &execution_engine) + .ok_or("Unable to JIT compile `sum`")?; + + let x = 1u64; + let y = 2u64; + let z = 3u64; + + unsafe { + println!("{} + {} + {} = {}", x, y, z, sum.call(x, y, z)); + assert_eq!(sum.call(x, y, z), x + y + z); + } + + Ok(()) +} + +fn jit_compile_sum( + context: &Context, + module: &Module, + builder: &Builder, + execution_engine: &ExecutionEngine, +) -> Option> { + let i64_type = context.i64_type(); + let fn_type = i64_type.fn_type(&[i64_type.into(), i64_type.into(), i64_type.into()], false); + + let function = module.add_function("sum", fn_type, None); + let basic_block = context.append_basic_block(&function, "entry"); + + builder.position_at_end(&basic_block); + + let x = function.get_nth_param(0)?.into_int_value(); + let y = function.get_nth_param(1)?.into_int_value(); + let z = function.get_nth_param(2)?.into_int_value(); + + let sum = builder.build_int_add(x, y, "sum"); + let sum = builder.build_int_add(sum, z, "sum"); + + builder.build_return(Some(&sum)); + + unsafe { execution_engine.get_function("sum").ok() } +} diff --git a/lib/llvm-backend/src/intrinsics.rs b/lib/llvm-backend/src/intrinsics.rs new file mode 100644 index 000000000..ba1e3abd3 --- /dev/null +++ b/lib/llvm-backend/src/intrinsics.rs @@ -0,0 +1,807 @@ +use hashbrown::HashMap; +use inkwell::{ + builder::Builder, + context::Context, + module::Module, + types::{BasicType, FloatType, FunctionType, IntType, PointerType, StructType, VoidType}, + values::{ + BasicValue, BasicValueEnum, FloatValue, FunctionValue, InstructionValue, IntValue, + PointerValue, + }, + AddressSpace, +}; +use std::marker::PhantomData; +use wasmer_runtime_core::{ + memory::MemoryType, + module::ModuleInfo, + structures::TypedIndex, + types::{ + GlobalIndex, ImportedFuncIndex, LocalFuncIndex, LocalOrImport, MemoryIndex, SigIndex, + TableIndex, Type, + }, +}; + +fn type_to_llvm_ptr(intrinsics: &Intrinsics, ty: Type) -> PointerType { + match ty { + Type::I32 => intrinsics.i32_ptr_ty, + Type::I64 => intrinsics.i64_ptr_ty, + Type::F32 => intrinsics.f32_ptr_ty, + Type::F64 => intrinsics.f64_ptr_ty, + } +} + +pub struct Intrinsics { + pub ctlz_i32: FunctionValue, + pub ctlz_i64: FunctionValue, + + pub cttz_i32: FunctionValue, + pub cttz_i64: FunctionValue, + + pub ctpop_i32: FunctionValue, + pub ctpop_i64: FunctionValue, + + pub sqrt_f32: FunctionValue, + pub sqrt_f64: FunctionValue, + + pub minimum_f32: FunctionValue, + pub minimum_f64: FunctionValue, + + pub maximum_f32: FunctionValue, + pub maximum_f64: FunctionValue, + + pub ceil_f32: FunctionValue, + pub ceil_f64: FunctionValue, + + pub floor_f32: FunctionValue, + pub floor_f64: FunctionValue, + + pub trunc_f32: FunctionValue, + pub trunc_f64: FunctionValue, + + pub nearbyint_f32: FunctionValue, + pub nearbyint_f64: FunctionValue, + + pub fabs_f32: FunctionValue, + pub fabs_f64: FunctionValue, + + pub copysign_f32: FunctionValue, + pub copysign_f64: FunctionValue, + + pub expect_i1: FunctionValue, + pub trap: FunctionValue, + + pub void_ty: VoidType, + pub i1_ty: IntType, + pub i8_ty: IntType, + pub i16_ty: IntType, + pub i32_ty: IntType, + pub i64_ty: IntType, + pub f32_ty: FloatType, + pub f64_ty: FloatType, + + pub i8_ptr_ty: PointerType, + pub i16_ptr_ty: PointerType, + pub i32_ptr_ty: PointerType, + pub i64_ptr_ty: PointerType, + pub f32_ptr_ty: PointerType, + pub f64_ptr_ty: PointerType, + + pub anyfunc_ty: StructType, + + pub i1_zero: IntValue, + pub i32_zero: IntValue, + pub i64_zero: IntValue, + pub f32_zero: FloatValue, + pub f64_zero: FloatValue, + + pub trap_unreachable: BasicValueEnum, + pub trap_call_indirect_sig: BasicValueEnum, + pub trap_call_indirect_oob: BasicValueEnum, + pub trap_memory_oob: BasicValueEnum, + pub trap_illegal_arithmetic: BasicValueEnum, + + // VM intrinsics. + pub memory_grow_dynamic_local: FunctionValue, + pub memory_grow_static_local: FunctionValue, + pub memory_grow_shared_local: FunctionValue, + pub memory_grow_dynamic_import: FunctionValue, + pub memory_grow_static_import: FunctionValue, + pub memory_grow_shared_import: FunctionValue, + + pub memory_size_dynamic_local: FunctionValue, + pub memory_size_static_local: FunctionValue, + pub memory_size_shared_local: FunctionValue, + pub memory_size_dynamic_import: FunctionValue, + pub memory_size_static_import: FunctionValue, + pub memory_size_shared_import: FunctionValue, + + pub throw_trap: FunctionValue, + + ctx_ty: StructType, + pub ctx_ptr_ty: PointerType, +} + +impl Intrinsics { + pub fn declare(module: &Module, context: &Context) -> Self { + let void_ty = context.void_type(); + let i1_ty = context.bool_type(); + let i8_ty = context.i8_type(); + let i16_ty = context.i16_type(); + let i32_ty = context.i32_type(); + let i64_ty = context.i64_type(); + let f32_ty = context.f32_type(); + let f64_ty = context.f64_type(); + + let i8_ptr_ty = i8_ty.ptr_type(AddressSpace::Generic); + let i16_ptr_ty = i16_ty.ptr_type(AddressSpace::Generic); + let i32_ptr_ty = i32_ty.ptr_type(AddressSpace::Generic); + let i64_ptr_ty = i64_ty.ptr_type(AddressSpace::Generic); + let f32_ptr_ty = f32_ty.ptr_type(AddressSpace::Generic); + let f64_ptr_ty = f64_ty.ptr_type(AddressSpace::Generic); + + let i1_zero = i1_ty.const_int(0, false); + let i32_zero = i32_ty.const_int(0, false); + let i64_zero = i64_ty.const_int(0, false); + let f32_zero = f32_ty.const_float(0.0); + let f64_zero = f64_ty.const_float(0.0); + + let i1_ty_basic = i1_ty.as_basic_type_enum(); + let i32_ty_basic = i32_ty.as_basic_type_enum(); + let i64_ty_basic = i64_ty.as_basic_type_enum(); + let f32_ty_basic = f32_ty.as_basic_type_enum(); + let f64_ty_basic = f64_ty.as_basic_type_enum(); + let i8_ptr_ty_basic = i8_ptr_ty.as_basic_type_enum(); + + let ctx_ty = context.opaque_struct_type("ctx"); + let ctx_ptr_ty = ctx_ty.ptr_type(AddressSpace::Generic); + + let local_memory_ty = + context.struct_type(&[i8_ptr_ty_basic, i64_ty_basic, i8_ptr_ty_basic], false); + let local_table_ty = local_memory_ty; + let local_global_ty = i64_ty; + let imported_func_ty = + context.struct_type(&[i8_ptr_ty_basic, ctx_ptr_ty.as_basic_type_enum()], false); + let sigindex_ty = i32_ty; + let local_function_ty = i8_ptr_ty; + + let anyfunc_ty = context.struct_type( + &[ + i8_ptr_ty_basic, + ctx_ptr_ty.as_basic_type_enum(), + sigindex_ty.as_basic_type_enum(), + ], + false, + ); + + ctx_ty.set_body( + &[ + local_memory_ty + .ptr_type(AddressSpace::Generic) + .ptr_type(AddressSpace::Generic) + .as_basic_type_enum(), + local_table_ty + .ptr_type(AddressSpace::Generic) + .ptr_type(AddressSpace::Generic) + .as_basic_type_enum(), + local_global_ty + .ptr_type(AddressSpace::Generic) + .ptr_type(AddressSpace::Generic) + .as_basic_type_enum(), + local_memory_ty + .ptr_type(AddressSpace::Generic) + .ptr_type(AddressSpace::Generic) + .as_basic_type_enum(), + local_table_ty + .ptr_type(AddressSpace::Generic) + .ptr_type(AddressSpace::Generic) + .as_basic_type_enum(), + local_global_ty + .ptr_type(AddressSpace::Generic) + .ptr_type(AddressSpace::Generic) + .as_basic_type_enum(), + imported_func_ty + .ptr_type(AddressSpace::Generic) + .as_basic_type_enum(), + sigindex_ty + .ptr_type(AddressSpace::Generic) + .as_basic_type_enum(), + local_function_ty + .ptr_type(AddressSpace::Generic) + .as_basic_type_enum(), + ], + false, + ); + + let ret_i32_take_i32_i1 = i32_ty.fn_type(&[i32_ty_basic, i1_ty_basic], false); + let ret_i64_take_i64_i1 = i64_ty.fn_type(&[i64_ty_basic, i1_ty_basic], false); + + let ret_i32_take_i32 = i32_ty.fn_type(&[i32_ty_basic], false); + let ret_i64_take_i64 = i64_ty.fn_type(&[i64_ty_basic], false); + + let ret_f32_take_f32 = f32_ty.fn_type(&[f32_ty_basic], false); + let ret_f64_take_f64 = f64_ty.fn_type(&[f64_ty_basic], false); + + let ret_f32_take_f32_f32 = f32_ty.fn_type(&[f32_ty_basic, f32_ty_basic], false); + let ret_f64_take_f64_f64 = f64_ty.fn_type(&[f64_ty_basic, f64_ty_basic], false); + + let ret_i32_take_ctx_i32_i32 = i32_ty.fn_type( + &[ctx_ptr_ty.as_basic_type_enum(), i32_ty_basic, i32_ty_basic], + false, + ); + let ret_i32_take_ctx_i32 = + i32_ty.fn_type(&[ctx_ptr_ty.as_basic_type_enum(), i32_ty_basic], false); + + let ret_i1_take_i1_i1 = i1_ty.fn_type(&[i1_ty_basic, i1_ty_basic], false); + + Self { + ctlz_i32: module.add_function("llvm.ctlz.i32", ret_i32_take_i32_i1, None), + ctlz_i64: module.add_function("llvm.ctlz.i64", ret_i64_take_i64_i1, None), + + cttz_i32: module.add_function("llvm.cttz.i32", ret_i32_take_i32_i1, None), + cttz_i64: module.add_function("llvm.cttz.i64", ret_i64_take_i64_i1, None), + + ctpop_i32: module.add_function("llvm.ctpop.i32", ret_i32_take_i32, None), + ctpop_i64: module.add_function("llvm.ctpop.i64", ret_i64_take_i64, None), + + sqrt_f32: module.add_function("llvm.sqrt.f32", ret_f32_take_f32, None), + sqrt_f64: module.add_function("llvm.sqrt.f64", ret_f64_take_f64, None), + + minimum_f32: module.add_function("llvm.minnum.f32", ret_f32_take_f32_f32, None), + minimum_f64: module.add_function("llvm.minnum.f64", ret_f64_take_f64_f64, None), + + maximum_f32: module.add_function("llvm.maxnum.f32", ret_f32_take_f32_f32, None), + maximum_f64: module.add_function("llvm.maxnum.f64", ret_f64_take_f64_f64, None), + + ceil_f32: module.add_function("llvm.ceil.f32", ret_f32_take_f32, None), + ceil_f64: module.add_function("llvm.ceil.f64", ret_f64_take_f64, None), + + floor_f32: module.add_function("llvm.floor.f32", ret_f32_take_f32, None), + floor_f64: module.add_function("llvm.floor.f64", ret_f64_take_f64, None), + + trunc_f32: module.add_function("llvm.trunc.f32", ret_f32_take_f32, None), + trunc_f64: module.add_function("llvm.trunc.f64", ret_f64_take_f64, None), + + nearbyint_f32: module.add_function("llvm.nearbyint.f32", ret_f32_take_f32, None), + nearbyint_f64: module.add_function("llvm.nearbyint.f64", ret_f64_take_f64, None), + + fabs_f32: module.add_function("llvm.fabs.f32", ret_f32_take_f32, None), + fabs_f64: module.add_function("llvm.fabs.f64", ret_f64_take_f64, None), + + copysign_f32: module.add_function("llvm.copysign.f32", ret_f32_take_f32_f32, None), + copysign_f64: module.add_function("llvm.copysign.f64", ret_f64_take_f64_f64, None), + + expect_i1: module.add_function("llvm.expect.i1", ret_i1_take_i1_i1, None), + trap: module.add_function("llvm.trap", void_ty.fn_type(&[], false), None), + + void_ty, + i1_ty, + i8_ty, + i16_ty, + i32_ty, + i64_ty, + f32_ty, + f64_ty, + + i8_ptr_ty, + i16_ptr_ty, + i32_ptr_ty, + i64_ptr_ty, + f32_ptr_ty, + f64_ptr_ty, + + anyfunc_ty, + + i1_zero, + i32_zero, + i64_zero, + f32_zero, + f64_zero, + + trap_unreachable: i32_zero.as_basic_value_enum(), + trap_call_indirect_sig: i32_ty.const_int(1, false).as_basic_value_enum(), + trap_call_indirect_oob: i32_ty.const_int(3, false).as_basic_value_enum(), + trap_memory_oob: i32_ty.const_int(2, false).as_basic_value_enum(), + trap_illegal_arithmetic: i32_ty.const_int(4, false).as_basic_value_enum(), + + // VM intrinsics. + memory_grow_dynamic_local: module.add_function( + "vm.memory.grow.dynamic.local", + ret_i32_take_ctx_i32_i32, + None, + ), + memory_grow_static_local: module.add_function( + "vm.memory.grow.static.local", + ret_i32_take_ctx_i32_i32, + None, + ), + memory_grow_shared_local: module.add_function( + "vm.memory.grow.shared.local", + ret_i32_take_ctx_i32_i32, + None, + ), + memory_grow_dynamic_import: module.add_function( + "vm.memory.grow.dynamic.import", + ret_i32_take_ctx_i32_i32, + None, + ), + memory_grow_static_import: module.add_function( + "vm.memory.grow.static.import", + ret_i32_take_ctx_i32_i32, + None, + ), + memory_grow_shared_import: module.add_function( + "vm.memory.grow.shared.import", + ret_i32_take_ctx_i32_i32, + None, + ), + + memory_size_dynamic_local: module.add_function( + "vm.memory.size.dynamic.local", + ret_i32_take_ctx_i32, + None, + ), + memory_size_static_local: module.add_function( + "vm.memory.size.static.local", + ret_i32_take_ctx_i32, + None, + ), + memory_size_shared_local: module.add_function( + "vm.memory.size.shared.local", + ret_i32_take_ctx_i32, + None, + ), + memory_size_dynamic_import: module.add_function( + "vm.memory.size.dynamic.import", + ret_i32_take_ctx_i32, + None, + ), + memory_size_static_import: module.add_function( + "vm.memory.size.static.import", + ret_i32_take_ctx_i32, + None, + ), + memory_size_shared_import: module.add_function( + "vm.memory.size.shared.import", + ret_i32_take_ctx_i32, + None, + ), + throw_trap: module.add_function( + "vm.exception.trap", + void_ty.fn_type(&[i32_ty_basic], false), + None, + ), + ctx_ty, + ctx_ptr_ty, + } + } + + pub fn ctx<'a>( + &'a self, + info: &'a ModuleInfo, + builder: &'a Builder, + func_value: &'a FunctionValue, + cache_builder: Builder, + ) -> CtxType<'a> { + CtxType { + ctx_ty: self.ctx_ty, + ctx_ptr_ty: self.ctx_ptr_ty, + + ctx_ptr_value: func_value.get_nth_param(0).unwrap().into_pointer_value(), + + builder, + intrinsics: self, + info, + cache_builder, + + cached_memories: HashMap::new(), + cached_tables: HashMap::new(), + cached_sigindices: HashMap::new(), + cached_globals: HashMap::new(), + cached_imported_functions: HashMap::new(), + + _phantom: PhantomData, + } + } +} + +#[derive(Clone, Copy)] +pub enum MemoryCache { + /// The memory moves around. + Dynamic { + ptr_to_base_ptr: PointerValue, + ptr_to_bounds: PointerValue, + }, + /// The memory is always in the same place. + Static { + base_ptr: PointerValue, + bounds: IntValue, + }, +} + +struct TableCache { + ptr_to_base_ptr: PointerValue, + ptr_to_bounds: PointerValue, +} + +#[derive(Clone, Copy)] +pub enum GlobalCache { + Mut { ptr_to_value: PointerValue }, + Const { value: BasicValueEnum }, +} + +struct ImportedFuncCache { + func_ptr: PointerValue, + ctx_ptr: PointerValue, +} + +pub struct CtxType<'a> { + ctx_ty: StructType, + ctx_ptr_ty: PointerType, + + ctx_ptr_value: PointerValue, + + builder: &'a Builder, + intrinsics: &'a Intrinsics, + info: &'a ModuleInfo, + cache_builder: Builder, + + cached_memories: HashMap, + cached_tables: HashMap, + cached_sigindices: HashMap, + cached_globals: HashMap, + cached_imported_functions: HashMap, + + _phantom: PhantomData<&'a FunctionValue>, +} + +impl<'a> CtxType<'a> { + pub fn basic(&self) -> BasicValueEnum { + self.ctx_ptr_value.as_basic_value_enum() + } + + pub fn memory(&mut self, index: MemoryIndex) -> MemoryCache { + let (cached_memories, builder, info, ctx_ptr_value, intrinsics, cache_builder) = ( + &mut self.cached_memories, + self.builder, + self.info, + self.ctx_ptr_value, + self.intrinsics, + &self.cache_builder, + ); + + *cached_memories.entry(index).or_insert_with(|| { + let (memory_array_ptr_ptr, index, memory_type) = match index.local_or_import(info) { + LocalOrImport::Local(local_mem_index) => ( + unsafe { + cache_builder.build_struct_gep(ctx_ptr_value, 0, "memory_array_ptr_ptr") + }, + local_mem_index.index() as u64, + info.memories[local_mem_index].memory_type(), + ), + LocalOrImport::Import(import_mem_index) => ( + unsafe { + cache_builder.build_struct_gep(ctx_ptr_value, 3, "memory_array_ptr_ptr") + }, + import_mem_index.index() as u64, + info.imported_memories[import_mem_index].1.memory_type(), + ), + }; + + let memory_array_ptr = cache_builder + .build_load(memory_array_ptr_ptr, "memory_array_ptr") + .into_pointer_value(); + let const_index = intrinsics.i32_ty.const_int(index, false); + let memory_ptr_ptr = unsafe { + cache_builder.build_in_bounds_gep( + memory_array_ptr, + &[const_index], + "memory_ptr_ptr", + ) + }; + let memory_ptr = cache_builder + .build_load(memory_ptr_ptr, "memory_ptr") + .into_pointer_value(); + + let (ptr_to_base_ptr, ptr_to_bounds) = unsafe { + ( + cache_builder.build_struct_gep(memory_ptr, 0, "base_ptr"), + cache_builder.build_struct_gep(memory_ptr, 1, "bounds_ptr"), + ) + }; + + match memory_type { + MemoryType::Dynamic => MemoryCache::Dynamic { + ptr_to_base_ptr, + ptr_to_bounds, + }, + MemoryType::Static | MemoryType::SharedStatic => MemoryCache::Static { + base_ptr: cache_builder + .build_load(ptr_to_base_ptr, "base") + .into_pointer_value(), + bounds: cache_builder + .build_load(ptr_to_bounds, "bounds") + .into_int_value(), + }, + } + }) + } + + pub fn table(&mut self, index: TableIndex) -> (PointerValue, IntValue) { + let (cached_tables, builder, info, ctx_ptr_value, intrinsics, cache_builder) = ( + &mut self.cached_tables, + self.builder, + self.info, + self.ctx_ptr_value, + self.intrinsics, + &self.cache_builder, + ); + + let TableCache { + ptr_to_base_ptr, + ptr_to_bounds, + } = *cached_tables.entry(index).or_insert_with(|| { + let (table_array_ptr_ptr, index) = match index.local_or_import(info) { + LocalOrImport::Local(local_table_index) => ( + unsafe { + cache_builder.build_struct_gep(ctx_ptr_value, 1, "table_array_ptr_ptr") + }, + local_table_index.index() as u64, + ), + LocalOrImport::Import(import_table_index) => ( + unsafe { + cache_builder.build_struct_gep(ctx_ptr_value, 4, "table_array_ptr_ptr") + }, + import_table_index.index() as u64, + ), + }; + + let table_array_ptr = cache_builder + .build_load(table_array_ptr_ptr, "table_array_ptr") + .into_pointer_value(); + let const_index = intrinsics.i32_ty.const_int(index, false); + let table_ptr_ptr = unsafe { + cache_builder.build_in_bounds_gep(table_array_ptr, &[const_index], "table_ptr_ptr") + }; + let table_ptr = cache_builder + .build_load(table_ptr_ptr, "table_ptr") + .into_pointer_value(); + + let (ptr_to_base_ptr, ptr_to_bounds) = unsafe { + ( + cache_builder.build_struct_gep(table_ptr, 0, "base_ptr"), + cache_builder.build_struct_gep(table_ptr, 1, "bounds_ptr"), + ) + }; + + TableCache { + ptr_to_base_ptr, + ptr_to_bounds, + } + }); + + ( + builder + .build_load(ptr_to_base_ptr, "base_ptr") + .into_pointer_value(), + builder.build_load(ptr_to_bounds, "bounds").into_int_value(), + ) + } + + pub fn local_func(&mut self, index: LocalFuncIndex, fn_ty: FunctionType) -> PointerValue { + let local_func_array_ptr_ptr = unsafe { + self.builder + .build_struct_gep(self.ctx_ptr_value, 8, "local_func_array_ptr_ptr") + }; + let local_func_array_ptr = self + .builder + .build_load(local_func_array_ptr_ptr, "local_func_array_ptr") + .into_pointer_value(); + let local_func_ptr_ptr = unsafe { + self.builder.build_in_bounds_gep( + local_func_array_ptr, + &[self + .intrinsics + .i32_ty + .const_int(index.index() as u64, false)], + "local_func_ptr_ptr", + ) + }; + let local_func_ptr = self + .builder + .build_load(local_func_ptr_ptr, "local_func_ptr") + .into_pointer_value(); + self.builder.build_pointer_cast( + local_func_ptr, + fn_ty.ptr_type(AddressSpace::Generic), + "local_func_ptr", + ) + } + + pub fn dynamic_sigindex(&mut self, index: SigIndex) -> IntValue { + let (cached_sigindices, builder, info, ctx_ptr_value, intrinsics, cache_builder) = ( + &mut self.cached_sigindices, + self.builder, + self.info, + self.ctx_ptr_value, + self.intrinsics, + &self.cache_builder, + ); + + *cached_sigindices.entry(index).or_insert_with(|| { + let sigindex_array_ptr_ptr = unsafe { + cache_builder.build_struct_gep(ctx_ptr_value, 7, "sigindex_array_ptr_ptr") + }; + let sigindex_array_ptr = cache_builder + .build_load(sigindex_array_ptr_ptr, "sigindex_array_ptr") + .into_pointer_value(); + let const_index = intrinsics.i32_ty.const_int(index.index() as u64, false); + + let sigindex_ptr = unsafe { + cache_builder.build_in_bounds_gep( + sigindex_array_ptr, + &[const_index], + "sigindex_ptr", + ) + }; + + cache_builder + .build_load(sigindex_ptr, "sigindex") + .into_int_value() + }) + } + + pub fn global_cache(&mut self, index: GlobalIndex) -> GlobalCache { + let (cached_globals, builder, ctx_ptr_value, info, intrinsics, cache_builder) = ( + &mut self.cached_globals, + self.builder, + self.ctx_ptr_value, + self.info, + self.intrinsics, + &self.cache_builder, + ); + + *cached_globals.entry(index).or_insert_with(|| { + let (globals_array_ptr_ptr, index, mutable, wasmer_ty) = + match index.local_or_import(info) { + LocalOrImport::Local(local_global_index) => { + let desc = info.globals[local_global_index].desc; + ( + unsafe { + cache_builder.build_struct_gep( + ctx_ptr_value, + 2, + "globals_array_ptr_ptr", + ) + }, + local_global_index.index() as u64, + desc.mutable, + desc.ty, + ) + } + LocalOrImport::Import(import_global_index) => { + let desc = info.imported_globals[import_global_index].1; + ( + unsafe { + cache_builder.build_struct_gep( + ctx_ptr_value, + 5, + "globals_array_ptr_ptr", + ) + }, + import_global_index.index() as u64, + desc.mutable, + desc.ty, + ) + } + }; + + let llvm_ptr_ty = type_to_llvm_ptr(intrinsics, wasmer_ty); + + let global_array_ptr = cache_builder + .build_load(globals_array_ptr_ptr, "global_array_ptr") + .into_pointer_value(); + let const_index = intrinsics.i32_ty.const_int(index, false); + let global_ptr_ptr = unsafe { + cache_builder.build_in_bounds_gep( + global_array_ptr, + &[const_index], + "global_ptr_ptr", + ) + }; + let global_ptr = cache_builder + .build_load(global_ptr_ptr, "global_ptr") + .into_pointer_value(); + + let global_ptr_typed = + cache_builder.build_pointer_cast(global_ptr, llvm_ptr_ty, "global_ptr_typed"); + + if mutable { + GlobalCache::Mut { + ptr_to_value: global_ptr_typed, + } + } else { + GlobalCache::Const { + value: cache_builder.build_load(global_ptr_typed, "global_value"), + } + } + }) + } + + pub fn imported_func(&mut self, index: ImportedFuncIndex) -> (PointerValue, PointerValue) { + let (cached_imported_functions, builder, ctx_ptr_value, intrinsics, cache_builder) = ( + &mut self.cached_imported_functions, + self.builder, + self.ctx_ptr_value, + self.intrinsics, + &self.cache_builder, + ); + + let imported_func_cache = cached_imported_functions.entry(index).or_insert_with(|| { + let func_array_ptr_ptr = unsafe { + cache_builder.build_struct_gep(ctx_ptr_value, 6, "imported_func_array_ptr_ptr") + }; + let func_array_ptr = cache_builder + .build_load(func_array_ptr_ptr, "func_array_ptr") + .into_pointer_value(); + let const_index = intrinsics.i32_ty.const_int(index.index() as u64, false); + let imported_func_ptr = unsafe { + cache_builder.build_in_bounds_gep( + func_array_ptr, + &[const_index], + "imported_func_ptr", + ) + }; + let (func_ptr_ptr, ctx_ptr_ptr) = unsafe { + ( + cache_builder.build_struct_gep(imported_func_ptr, 0, "func_ptr_ptr"), + cache_builder.build_struct_gep(imported_func_ptr, 1, "ctx_ptr_ptr"), + ) + }; + + let func_ptr = cache_builder + .build_load(func_ptr_ptr, "func_ptr") + .into_pointer_value(); + let ctx_ptr = cache_builder + .build_load(ctx_ptr_ptr, "ctx_ptr") + .into_pointer_value(); + + ImportedFuncCache { func_ptr, ctx_ptr } + }); + + (imported_func_cache.func_ptr, imported_func_cache.ctx_ptr) + } + + pub fn build_trap(&self) { + self.builder.build_call(self.intrinsics.trap, &[], "trap"); + } +} + +// pub struct Ctx { +// /// A pointer to an array of locally-defined memories, indexed by `MemoryIndex`. +// pub(crate) memories: *mut *mut LocalMemory, + +// /// A pointer to an array of locally-defined tables, indexed by `TableIndex`. +// pub(crate) tables: *mut *mut LocalTable, + +// /// A pointer to an array of locally-defined globals, indexed by `GlobalIndex`. +// pub(crate) globals: *mut *mut LocalGlobal, + +// /// A pointer to an array of imported memories, indexed by `MemoryIndex, +// pub(crate) imported_memories: *mut *mut LocalMemory, + +// /// A pointer to an array of imported tables, indexed by `TableIndex`. +// pub(crate) imported_tables: *mut *mut LocalTable, + +// /// A pointer to an array of imported globals, indexed by `GlobalIndex`. +// pub(crate) imported_globals: *mut *mut LocalGlobal, + +// /// A pointer to an array of imported functions, indexed by `FuncIndex`. +// pub(crate) imported_funcs: *mut ImportedFunc, + +// local_backing: *mut LocalBacking, +// import_backing: *mut ImportBacking, +// module: *const ModuleInner, + +// pub data: *mut c_void, +// pub data_finalizer: Option, +// } diff --git a/lib/llvm-backend/src/lib.rs b/lib/llvm-backend/src/lib.rs new file mode 100644 index 000000000..85bcc7e19 --- /dev/null +++ b/lib/llvm-backend/src/lib.rs @@ -0,0 +1,139 @@ +#![cfg_attr(nightly, feature(unwind_attributes))] + +use inkwell::{ + execution_engine::JitFunction, + targets::{CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine}, + OptimizationLevel, +}; +use wasmer_runtime_core::{ + backend::{Compiler, Token}, + cache::{Artifact, Error as CacheError}, + error::CompileError, + module::ModuleInner, +}; +use wasmparser::{self, WasmDecoder}; + +mod backend; +mod code; +mod intrinsics; +mod platform; +mod read_info; +mod state; +mod trampolines; + +pub struct LLVMCompiler { + _private: (), +} + +impl LLVMCompiler { + pub fn new() -> Self { + Self { _private: () } + } +} + +impl Compiler for LLVMCompiler { + fn compile(&self, wasm: &[u8], _: Token) -> Result { + validate(wasm)?; + + let (info, code_reader) = read_info::read_module(wasm).unwrap(); + let (module, intrinsics) = code::parse_function_bodies(&info, code_reader).unwrap(); + + let (backend, protected_caller) = backend::LLVMBackend::new(module, intrinsics); + + // Create placeholder values here. + let cache_gen = { + use wasmer_runtime_core::backend::{ + sys::Memory, CacheGen, ProtectedCaller, UserTrapper, + }; + use wasmer_runtime_core::cache::Error as CacheError; + use wasmer_runtime_core::error::RuntimeResult; + use wasmer_runtime_core::module::ModuleInfo; + use wasmer_runtime_core::types::{FuncIndex, Value}; + use wasmer_runtime_core::vm; + struct Placeholder; + impl CacheGen for Placeholder { + fn generate_cache( + &self, + module: &ModuleInner, + ) -> Result<(Box, Box<[u8]>, Memory), CacheError> { + unimplemented!() + } + } + + Box::new(Placeholder) + }; + + Ok(ModuleInner { + func_resolver: Box::new(backend), + protected_caller: Box::new(protected_caller), + cache_gen, + + info, + }) + } + + unsafe fn from_cache(&self, _artifact: Artifact, _: Token) -> Result { + unimplemented!("the llvm backend doesn't support caching yet") + } +} + +fn validate(bytes: &[u8]) -> Result<(), CompileError> { + let mut parser = wasmparser::ValidatingParser::new( + bytes, + Some(wasmparser::ValidatingParserConfig { + operator_config: wasmparser::OperatorValidatorConfig { + enable_threads: false, + enable_reference_types: false, + enable_simd: false, + enable_bulk_memory: false, + }, + mutable_global_imports: false, + }), + ); + + loop { + let state = parser.read(); + match *state { + wasmparser::ParserState::EndWasm => break Ok(()), + wasmparser::ParserState::Error(err) => Err(CompileError::ValidationError { + msg: err.message.to_string(), + })?, + _ => {} + } + } +} + +#[test] +fn test_read_module() { + use std::mem::transmute; + use wabt::wat2wasm; + use wasmer_runtime_core::{structures::TypedIndex, types::LocalFuncIndex, vm, vmcalls}; + // let wasm = include_bytes!("../../spectests/examples/simple/simple.wasm") as &[u8]; + let wat = r#" + (module + (type $t0 (func (param i32) (result i32))) + (type $t1 (func (result i32))) + (memory 1) + (global $g0 (mut i32) (i32.const 0)) + (func $foo (type $t0) (param i32) (result i32) + get_local 0 + )) + "#; + let wasm = wat2wasm(wat).unwrap(); + + let (info, code_reader) = read_info::read_module(&wasm).unwrap(); + + let (module, intrinsics) = code::parse_function_bodies(&info, code_reader).unwrap(); + + let (backend, _caller) = backend::LLVMBackend::new(module, intrinsics); + + let func_ptr = backend.get_func(&info, LocalFuncIndex::new(0)).unwrap(); + + println!("func_ptr: {:p}", func_ptr.as_ptr()); + + unsafe { + let func: unsafe extern "C" fn(*mut vm::Ctx, i32) -> i32 = transmute(func_ptr); + let result = func(0 as _, 42); + println!("result: {}", result); + } +} diff --git a/lib/llvm-backend/src/platform/mod.rs b/lib/llvm-backend/src/platform/mod.rs new file mode 100644 index 000000000..01b81b022 --- /dev/null +++ b/lib/llvm-backend/src/platform/mod.rs @@ -0,0 +1,7 @@ +#[cfg(unix)] +mod unix; +#[cfg(unix)] +pub use self::unix::*; + +#[cfg(target_family = "windows")] +compile_error!("windows not yet supported for the llvm-based compiler backend"); diff --git a/lib/llvm-backend/src/platform/unix.rs b/lib/llvm-backend/src/platform/unix.rs new file mode 100644 index 000000000..2267ab129 --- /dev/null +++ b/lib/llvm-backend/src/platform/unix.rs @@ -0,0 +1,74 @@ +use libc::{c_void, siginfo_t}; +use nix::sys::signal::{ + sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal, SIGBUS, SIGFPE, SIGILL, SIGSEGV, +}; + +/// `__register_frame` and `__deregister_frame` on macos take a single fde as an +/// argument, so we need to parse the fde table here. +/// +/// This is a pretty direct port of llvm's fde handling code: +/// https://llvm.org/doxygen/RTDyldMemoryManager_8cpp_source.html. +#[allow(clippy::cast_ptr_alignment)] +#[cfg(target_os = "macos")] +pub unsafe fn visit_fde(addr: *mut u8, size: usize, visitor: extern "C" fn(*mut u8)) { + unsafe fn process_fde(entry: *mut u8, visitor: extern "C" fn(*mut u8)) -> *mut u8 { + let mut p = entry; + let length = (p as *const u32).read_unaligned(); + p = p.add(4); + let offset = (p as *const u32).read_unaligned(); + + if offset != 0 { + visitor(entry); + } + p.add(length as usize) + } + + let mut p = addr; + let end = p.add(size); + + loop { + if p >= end { + break; + } + + p = process_fde(p, visitor); + } +} + +#[cfg(not(target_os = "macos"))] +pub unsafe fn visit_fde(addr: *mut u8, size: usize, visitor: extern "C" fn(*mut u8)) { + visitor(addr); +} + +extern "C" { + #[cfg_attr(nightly, unwind(allowed))] + fn throw_trap(ty: i32) -> !; +} + +pub unsafe fn install_signal_handler() { + let sa = SigAction::new( + SigHandler::SigAction(signal_trap_handler), + SaFlags::SA_ONSTACK | SaFlags::SA_SIGINFO, + SigSet::empty(), + ); + // sigaction(SIGFPE, &sa).unwrap(); + // sigaction(SIGILL, &sa).unwrap(); + sigaction(SIGSEGV, &sa).unwrap(); + sigaction(SIGBUS, &sa).unwrap(); +} + +#[cfg_attr(nightly, unwind(allowed))] +extern "C" fn signal_trap_handler( + signum: ::nix::libc::c_int, + siginfo: *mut siginfo_t, + ucontext: *mut c_void, +) { + unsafe { + /// Apparently, we can unwind from arbitary instructions, as long + /// as we don't need to catch the exception inside the function that + /// was interrupted. + /// + /// This works on macos, not sure about linux. + throw_trap(2); + } +} diff --git a/lib/llvm-backend/src/read_info.rs b/lib/llvm-backend/src/read_info.rs new file mode 100644 index 000000000..a974beb59 --- /dev/null +++ b/lib/llvm-backend/src/read_info.rs @@ -0,0 +1,341 @@ +use wasmer_runtime_core::{ + backend::Backend, + module::{ + DataInitializer, ExportIndex, ImportName, ModuleInfo, StringTable, StringTableBuilder, + TableInitializer, + }, + structures::{Map, TypedIndex}, + types::{ + ElementType, FuncIndex, FuncSig, GlobalDescriptor, GlobalIndex, GlobalInit, + ImportedGlobalIndex, Initializer, MemoryDescriptor, MemoryIndex, SigIndex, TableDescriptor, + TableIndex, Type, Value, + }, + units::Pages, +}; +use wasmparser::{ + BinaryReaderError, CodeSectionReader, Data, DataKind, Element, ElementKind, Export, + ExternalKind, FuncType, Import, ImportSectionEntryType, InitExpr, ModuleReader, Operator, + SectionCode, Type as WpType, +}; + +pub fn read_module(wasm: &[u8]) -> Result<(ModuleInfo, CodeSectionReader), BinaryReaderError> { + let mut info = ModuleInfo { + memories: Map::new(), + globals: Map::new(), + tables: Map::new(), + + imported_functions: Map::new(), + imported_memories: Map::new(), + imported_tables: Map::new(), + imported_globals: Map::new(), + + exports: Default::default(), + + data_initializers: Vec::new(), + elem_initializers: Vec::new(), + + start_func: None, + + func_assoc: Map::new(), + signatures: Map::new(), + backend: Backend::LLVM, + + namespace_table: StringTable::new(), + name_table: StringTable::new(), + }; + + let mut reader = ModuleReader::new(wasm)?; + let mut code_reader = None; + + loop { + if reader.eof() { + return Ok((info, code_reader.unwrap())); + } + + let section = reader.read()?; + + match section.code { + SectionCode::Type => { + let type_reader = section.get_type_section_reader()?; + + for ty in type_reader { + let ty = ty?; + info.signatures.push(func_type_to_func_sig(ty)?); + } + } + SectionCode::Import => { + let import_reader = section.get_import_section_reader()?; + let mut namespace_builder = StringTableBuilder::new(); + let mut name_builder = StringTableBuilder::new(); + + for import in import_reader { + let Import { module, field, ty } = import?; + + let namespace_index = namespace_builder.register(module); + let name_index = name_builder.register(field); + let import_name = ImportName { + namespace_index, + name_index, + }; + + match ty { + ImportSectionEntryType::Function(sigindex) => { + let sigindex = SigIndex::new(sigindex as usize); + info.imported_functions.push(import_name); + info.func_assoc.push(sigindex); + } + ImportSectionEntryType::Table(table_ty) => { + assert_eq!(table_ty.element_type, WpType::AnyFunc); + let table_desc = TableDescriptor { + element: ElementType::Anyfunc, + minimum: table_ty.limits.initial, + maximum: table_ty.limits.maximum, + }; + + info.imported_tables.push((import_name, table_desc)); + } + ImportSectionEntryType::Memory(memory_ty) => { + let mem_desc = MemoryDescriptor { + minimum: Pages(memory_ty.limits.initial), + maximum: memory_ty.limits.maximum.map(|max| Pages(max)), + shared: memory_ty.shared, + }; + info.imported_memories.push((import_name, mem_desc)); + } + ImportSectionEntryType::Global(global_ty) => { + let global_desc = GlobalDescriptor { + mutable: global_ty.mutable, + ty: type_to_type(global_ty.content_type)?, + }; + info.imported_globals.push((import_name, global_desc)); + } + } + } + + info.namespace_table = namespace_builder.finish(); + info.name_table = name_builder.finish(); + } + SectionCode::Function => { + let func_decl_reader = section.get_function_section_reader()?; + + for sigindex in func_decl_reader { + let sigindex = sigindex?; + + let sigindex = SigIndex::new(sigindex as usize); + info.func_assoc.push(sigindex); + } + } + SectionCode::Table => { + let table_decl_reader = section.get_table_section_reader()?; + + for table_ty in table_decl_reader { + let table_ty = table_ty?; + + let table_desc = TableDescriptor { + element: ElementType::Anyfunc, + minimum: table_ty.limits.initial, + maximum: table_ty.limits.maximum, + }; + + info.tables.push(table_desc); + } + } + SectionCode::Memory => { + let mem_decl_reader = section.get_memory_section_reader()?; + + for memory_ty in mem_decl_reader { + let memory_ty = memory_ty?; + + let mem_desc = MemoryDescriptor { + minimum: Pages(memory_ty.limits.initial), + maximum: memory_ty.limits.maximum.map(|max| Pages(max)), + shared: memory_ty.shared, + }; + + info.memories.push(mem_desc); + } + } + SectionCode::Global => { + let global_decl_reader = section.get_global_section_reader()?; + + for global in global_decl_reader { + let global = global?; + + let desc = GlobalDescriptor { + mutable: global.ty.mutable, + ty: type_to_type(global.ty.content_type)?, + }; + + let global_init = GlobalInit { + desc, + init: eval_init_expr(&global.init_expr)?, + }; + + info.globals.push(global_init); + } + } + SectionCode::Export => { + let export_reader = section.get_export_section_reader()?; + + for export in export_reader { + let Export { field, kind, index } = export?; + + let export_index = match kind { + ExternalKind::Function => ExportIndex::Func(FuncIndex::new(index as usize)), + ExternalKind::Table => ExportIndex::Table(TableIndex::new(index as usize)), + ExternalKind::Memory => { + ExportIndex::Memory(MemoryIndex::new(index as usize)) + } + ExternalKind::Global => { + ExportIndex::Global(GlobalIndex::new(index as usize)) + } + }; + + info.exports.insert(field.to_string(), export_index); + } + } + SectionCode::Start => { + let start_index = section.get_start_section_content()?; + + info.start_func = Some(FuncIndex::new(start_index as usize)); + } + SectionCode::Element => { + let element_reader = section.get_element_section_reader()?; + + for element in element_reader { + let Element { kind, items } = element?; + + match kind { + ElementKind::Active { + table_index, + init_expr, + } => { + let table_index = TableIndex::new(table_index as usize); + let base = eval_init_expr(&init_expr)?; + let items_reader = items.get_items_reader()?; + + let elements: Vec<_> = items_reader + .into_iter() + .map(|res| res.map(|index| FuncIndex::new(index as usize))) + .collect::>()?; + + let table_init = TableInitializer { + table_index, + base, + elements, + }; + + info.elem_initializers.push(table_init); + } + ElementKind::Passive(_ty) => { + return Err(BinaryReaderError { + message: "passive tables are not yet supported", + offset: -1isize as usize, + }); + } + } + } + } + SectionCode::Code => { + code_reader = Some(section.get_code_section_reader()?); + } + SectionCode::Data => { + let data_reader = section.get_data_section_reader()?; + + for data in data_reader { + let Data { kind, data } = data?; + + match kind { + DataKind::Active { + memory_index, + init_expr, + } => { + let memory_index = MemoryIndex::new(memory_index as usize); + let base = eval_init_expr(&init_expr)?; + + let data_init = DataInitializer { + memory_index, + base, + data: data.to_vec(), + }; + + info.data_initializers.push(data_init); + } + DataKind::Passive => { + return Err(BinaryReaderError { + message: "passive memories are not yet supported", + offset: -1isize as usize, + }); + } + } + } + } + SectionCode::DataCount => {} + SectionCode::Custom { .. } => {} + } + } +} + +pub fn type_to_type(ty: WpType) -> Result { + Ok(match ty { + WpType::I32 => Type::I32, + WpType::I64 => Type::I64, + WpType::F32 => Type::F32, + WpType::F64 => Type::F64, + WpType::V128 => { + return Err(BinaryReaderError { + message: "the wasmer llvm backend does not yet support the simd extension", + offset: -1isize as usize, + }); + } + _ => { + return Err(BinaryReaderError { + message: "that type is not supported as a wasmer type", + offset: -1isize as usize, + }); + } + }) +} + +fn func_type_to_func_sig(func_ty: FuncType) -> Result { + assert_eq!(func_ty.form, WpType::Func); + + Ok(FuncSig::new( + func_ty + .params + .iter() + .cloned() + .map(type_to_type) + .collect::, _>>()?, + func_ty + .returns + .iter() + .cloned() + .map(type_to_type) + .collect::, _>>()?, + )) +} + +fn eval_init_expr(expr: &InitExpr) -> Result { + let mut reader = expr.get_operators_reader(); + let (op, offset) = reader.read_with_offset()?; + Ok(match op { + Operator::GetGlobal { global_index } => { + Initializer::GetGlobal(ImportedGlobalIndex::new(global_index as usize)) + } + Operator::I32Const { value } => Initializer::Const(Value::I32(value)), + Operator::I64Const { value } => Initializer::Const(Value::I64(value)), + Operator::F32Const { value } => { + Initializer::Const(Value::F32(f32::from_bits(value.bits()))) + } + Operator::F64Const { value } => { + Initializer::Const(Value::F64(f64::from_bits(value.bits()))) + } + _ => { + return Err(BinaryReaderError { + message: "init expr evaluation failed: unsupported opcode", + offset, + }); + } + }) +} diff --git a/lib/llvm-backend/src/state.rs b/lib/llvm-backend/src/state.rs new file mode 100644 index 000000000..47da54a6d --- /dev/null +++ b/lib/llvm-backend/src/state.rs @@ -0,0 +1,244 @@ +use inkwell::{ + basic_block::BasicBlock, + values::{BasicValue, BasicValueEnum, PhiValue}, +}; +use smallvec::SmallVec; +use std::cell::Cell; +use wasmparser::BinaryReaderError; + +#[derive(Debug)] +pub enum ControlFrame { + Block { + next: BasicBlock, + phis: SmallVec<[PhiValue; 1]>, + stack_size_snapshot: usize, + }, + Loop { + body: BasicBlock, + next: BasicBlock, + phis: SmallVec<[PhiValue; 1]>, + stack_size_snapshot: usize, + }, + IfElse { + if_then: BasicBlock, + if_else: BasicBlock, + next: BasicBlock, + phis: SmallVec<[PhiValue; 1]>, + stack_size_snapshot: usize, + if_else_state: IfElseState, + }, +} + +#[derive(Debug)] +pub enum IfElseState { + If, + Else, +} + +impl ControlFrame { + pub fn code_after(&self) -> &BasicBlock { + match self { + ControlFrame::Block { ref next, .. } + | ControlFrame::Loop { ref next, .. } + | ControlFrame::IfElse { ref next, .. } => next, + } + } + + pub fn br_dest(&self) -> &BasicBlock { + match self { + ControlFrame::Block { ref next, .. } | ControlFrame::IfElse { ref next, .. } => next, + ControlFrame::Loop { ref body, .. } => body, + } + } + + pub fn phis(&self) -> &[PhiValue] { + match self { + ControlFrame::Block { ref phis, .. } + | ControlFrame::Loop { ref phis, .. } + | ControlFrame::IfElse { ref phis, .. } => phis.as_slice(), + } + } + + pub fn is_loop(&self) -> bool { + match self { + ControlFrame::Loop { .. } => true, + _ => false, + } + } +} + +#[derive(Debug)] +pub struct State { + stack: Vec, + control_stack: Vec, + value_counter: Cell, + + pub reachable: bool, +} + +impl State { + pub fn new() -> Self { + Self { + stack: vec![], + control_stack: vec![], + value_counter: Cell::new(0), + reachable: true, + } + } + + pub fn reset_stack(&mut self, frame: &ControlFrame) { + let stack_size_snapshot = match frame { + ControlFrame::Block { + stack_size_snapshot, + .. + } + | ControlFrame::Loop { + stack_size_snapshot, + .. + } + | ControlFrame::IfElse { + stack_size_snapshot, + .. + } => *stack_size_snapshot, + }; + self.stack.truncate(stack_size_snapshot); + } + + pub fn outermost_frame(&self) -> Result<&ControlFrame, BinaryReaderError> { + self.control_stack.get(0).ok_or(BinaryReaderError { + message: "invalid control stack depth", + offset: -1isize as usize, + }) + } + + pub fn frame_at_depth(&self, depth: u32) -> Result<&ControlFrame, BinaryReaderError> { + let index = self.control_stack.len() - 1 - (depth as usize); + self.control_stack.get(index).ok_or(BinaryReaderError { + message: "invalid control stack depth", + offset: -1isize as usize, + }) + } + + pub fn frame_at_depth_mut( + &mut self, + depth: u32, + ) -> Result<&mut ControlFrame, BinaryReaderError> { + let index = self.control_stack.len() - 1 - (depth as usize); + self.control_stack.get_mut(index).ok_or(BinaryReaderError { + message: "invalid control stack depth", + offset: -1isize as usize, + }) + } + + pub fn pop_frame(&mut self) -> Result { + self.control_stack.pop().ok_or(BinaryReaderError { + message: "cannot pop from control stack", + offset: -1isize as usize, + }) + } + + pub fn var_name(&self) -> String { + let counter = self.value_counter.get(); + let s = format!("s{}", counter); + self.value_counter.set(counter + 1); + s + } + + pub fn push1(&mut self, value: T) { + self.stack.push(value.as_basic_value_enum()) + } + + pub fn pop1(&mut self) -> Result { + self.stack.pop().ok_or(BinaryReaderError { + message: "invalid value stack", + offset: -1isize as usize, + }) + } + + pub fn pop2(&mut self) -> Result<(BasicValueEnum, BasicValueEnum), BinaryReaderError> { + let v2 = self.pop1()?; + let v1 = self.pop1()?; + Ok((v1, v2)) + } + + pub fn pop3( + &mut self, + ) -> Result<(BasicValueEnum, BasicValueEnum, BasicValueEnum), BinaryReaderError> { + let v3 = self.pop1()?; + let v2 = self.pop1()?; + let v1 = self.pop1()?; + Ok((v1, v2, v3)) + } + + pub fn peek1(&self) -> Result { + self.stack + .get(self.stack.len() - 1) + .ok_or(BinaryReaderError { + message: "invalid value stack", + offset: -1isize as usize, + }) + .map(|v| *v) + } + + pub fn peekn(&self, n: usize) -> Result<&[BasicValueEnum], BinaryReaderError> { + self.stack + .get(self.stack.len() - n..) + .ok_or(BinaryReaderError { + message: "invalid value stack", + offset: -1isize as usize, + }) + } + + pub fn popn_save(&mut self, n: usize) -> Result, BinaryReaderError> { + let v = self.peekn(n)?.to_vec(); + self.popn(n)?; + Ok(v) + } + + pub fn popn(&mut self, n: usize) -> Result<(), BinaryReaderError> { + if self.stack.len() < n { + return Err(BinaryReaderError { + message: "invalid value stack", + offset: -1isize as usize, + }); + } + + let new_len = self.stack.len() - n; + self.stack.truncate(new_len); + Ok(()) + } + + pub fn push_block(&mut self, next: BasicBlock, phis: SmallVec<[PhiValue; 1]>) { + self.control_stack.push(ControlFrame::Block { + next, + phis, + stack_size_snapshot: self.stack.len(), + }); + } + + pub fn push_loop(&mut self, body: BasicBlock, next: BasicBlock, phis: SmallVec<[PhiValue; 1]>) { + self.control_stack.push(ControlFrame::Loop { + body, + next, + phis, + stack_size_snapshot: self.stack.len(), + }); + } + + pub fn push_if( + &mut self, + if_then: BasicBlock, + if_else: BasicBlock, + next: BasicBlock, + phis: SmallVec<[PhiValue; 1]>, + ) { + self.control_stack.push(ControlFrame::IfElse { + if_then, + if_else, + next, + phis, + stack_size_snapshot: self.stack.len(), + if_else_state: IfElseState::If, + }); + } +} diff --git a/lib/llvm-backend/src/trampolines.rs b/lib/llvm-backend/src/trampolines.rs new file mode 100644 index 000000000..4619cb990 --- /dev/null +++ b/lib/llvm-backend/src/trampolines.rs @@ -0,0 +1,120 @@ +use crate::intrinsics::Intrinsics; +use inkwell::{ + builder::Builder, + context::Context, + module::{Linkage, Module}, + passes::PassManager, + types::{BasicType, BasicTypeEnum, FunctionType, PointerType}, + values::{BasicValue, FunctionValue, PhiValue, PointerValue}, + AddressSpace, FloatPredicate, IntPredicate, +}; +use wasmer_runtime_core::{ + module::ModuleInfo, + structures::{SliceMap, TypedIndex}, + types::{FuncSig, SigIndex, Type}, +}; + +pub fn generate_trampolines( + info: &ModuleInfo, + signatures: &SliceMap, + module: &Module, + context: &Context, + builder: &Builder, + intrinsics: &Intrinsics, +) { + for (sig_index, sig) in info.signatures.iter() { + let func_type = signatures[sig_index]; + + let trampoline_sig = intrinsics.void_ty.fn_type( + &[ + intrinsics.ctx_ptr_ty.as_basic_type_enum(), // vmctx ptr + func_type + .ptr_type(AddressSpace::Generic) + .as_basic_type_enum(), // func ptr + intrinsics.i64_ptr_ty.as_basic_type_enum(), // args ptr + intrinsics.i64_ptr_ty.as_basic_type_enum(), // returns ptr + ], + false, + ); + + let trampoline_func = module.add_function( + &format!("trmp{}", sig_index.index()), + trampoline_sig, + Some(Linkage::External), + ); + + generate_trampoline( + trampoline_func, + func_type, + sig, + context, + builder, + intrinsics, + ); + } +} + +fn generate_trampoline( + trampoline_func: FunctionValue, + sig_type: FunctionType, + func_sig: &FuncSig, + context: &Context, + builder: &Builder, + intrinsics: &Intrinsics, +) { + let entry_block = context.append_basic_block(&trampoline_func, "entry"); + builder.position_at_end(&entry_block); + + let (vmctx_ptr, func_ptr, args_ptr, returns_ptr) = match trampoline_func.get_params().as_slice() + { + &[vmctx_ptr, func_ptr, args_ptr, returns_ptr] => ( + vmctx_ptr, + func_ptr.into_pointer_value(), + args_ptr.into_pointer_value(), + returns_ptr.into_pointer_value(), + ), + _ => unimplemented!(), + }; + + let cast_ptr_ty = |wasmer_ty| match wasmer_ty { + Type::I32 => intrinsics.i32_ptr_ty, + Type::I64 => intrinsics.i64_ptr_ty, + Type::F32 => intrinsics.f32_ptr_ty, + Type::F64 => intrinsics.f64_ptr_ty, + }; + + let mut args_vec = Vec::with_capacity(func_sig.params().len() + 1); + args_vec.push(vmctx_ptr); + + for (i, param_ty) in func_sig.params().iter().enumerate() { + let index = intrinsics.i32_ty.const_int(i as _, false); + let item_pointer = unsafe { builder.build_in_bounds_gep(args_ptr, &[index], "arg_ptr") }; + + let casted_pointer_type = cast_ptr_ty(*param_ty); + + let typed_item_pointer = + builder.build_pointer_cast(item_pointer, casted_pointer_type, "typed_arg_pointer"); + + let arg = builder.build_load(typed_item_pointer, "arg"); + args_vec.push(arg); + } + + let call_site = builder.build_call(func_ptr, &args_vec, "call"); + + match func_sig.returns() { + &[] => {} + &[one_ret] => { + let ret_ptr_type = cast_ptr_ty(one_ret); + + let typed_ret_ptr = + builder.build_pointer_cast(returns_ptr, ret_ptr_type, "typed_ret_ptr"); + builder.build_store( + typed_ret_ptr, + call_site.try_as_basic_value().left().unwrap(), + ); + } + _ => unimplemented!("multi-value returns"), + } + + builder.build_return(None); +} diff --git a/lib/runtime-c-api/Cargo.toml b/lib/runtime-c-api/Cargo.toml new file mode 100644 index 000000000..201c400a0 --- /dev/null +++ b/lib/runtime-c-api/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "wasmer-runtime-c-api" +version = "0.2.1" +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.2.1" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.2.1" } +libc = "0.2" + +[lib] +crate-type = ["cdylib", "rlib", "staticlib"] + +[build-dependencies] +cbindgen = "0.8" \ No newline at end of file diff --git a/lib/runtime-c-api/README.md b/lib/runtime-c-api/README.md new file mode 100644 index 000000000..1e741cfd1 --- /dev/null +++ b/lib/runtime-c-api/README.md @@ -0,0 +1,132 @@ +

+ + Wasmer logo + +

+ +

+ + Build Status + + + License + + + Join the Wasmer Community + + + Number of downloads from crates.io + + + Read our API documentation + +

+ +# Wasmer Runtime C API + +Wasmer is a standalone JIT WebAssembly runtime, aiming to be fully +compatible with Emscripten, Rust and Go. [Learn +more](https://github.com/wasmerio/wasmer). + +This crate exposes a C and a C++ API for the Wasmer runtime. + +# Usage + +The C and C++ header files can be found in the source tree of this +crate, respectively [`wasmer.h`][wasmer_h] and +[`wasmer.hh`][wasmer_hh]. They are automatically generated, and always +up-to-date in this repository. + +Here is a simple example to use the C API: + +```c +#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); + + // Prepare the imports. + wasmer_import_t imports[] = {}; + + // Instantiate! + wasmer_instance_t *instance = NULL; + wasmer_result_t instantiation_result = wasmer_instantiate(&instance, bytes, len, imports, 0); + + assert(instantiation_result == WASMER_OK); + + // Let's call a function. + // Start by preparing the arguments. + + // Value of argument #1 is `7i32`. + wasmer_value_t argument_one; + argument_one.tag = WASM_I32; + argument_one.value.I32 = 7; + + // Value of argument #2 is `8i32`. + wasmer_value_t argument_two; + argument_two.tag = WASM_I32; + argument_two.value.I32 = 8; + + // Prepare the arguments. + wasmer_value_t arguments[] = {argument_one, argument_two}; + + // Prepare the return value. + wasmer_value_t result_one; + wasmer_value_t results[] = {result_one}; + + // Call the `sum` function with the prepared arguments and the return value. + wasmer_result_t call_result = wasmer_instance_call(instance, "sum", arguments, 2, results, 1); + + // Let's display the result. + printf("Call result: %d\n", call_result); + printf("Result: %d\n", results[0].value.I32); + + // `sum(7, 8) == 15`. + assert(results[0].value.I32 == 15); + assert(call_result == WASMER_OK); + + wasmer_instance_destroy(instance); + + return 0; +} +``` + +# Testing + +The tests can be run via `cargo test`, such as: + +```sh +$ cargo test -- --nocapture +``` + +To run tests manually, enter the `lib/runtime-c-api/tests` directory +and run the following commands: + +```sh +$ cmake . +$ make +$ make test +``` + + +# License + +Wasmer is primarily distributed under the terms of the [MIT +license][mit-license] ([LICENSE][license]). + + +[wasmer_h]: ./wasmer.h +[wasmer_hh]: ./wasmer.hh +[mit-license]: http://opensource.org/licenses/MIT +[license]: https://github.com/wasmerio/wasmer/blob/master/LICENSE diff --git a/lib/runtime-c-api/build.rs b/lib/runtime-c-api/build.rs new file mode 100644 index 000000000..1db19a197 --- /dev/null +++ b/lib/runtime-c-api/build.rs @@ -0,0 +1,52 @@ +extern crate cbindgen; + +use cbindgen::{Builder, Language}; +use std::{env, fs, path::PathBuf}; + +fn main() { + let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + let mut crate_wasmer_header_file = PathBuf::from(&crate_dir); + crate_wasmer_header_file.push("wasmer"); + + let out_dir = env::var("OUT_DIR").unwrap(); + let mut out_wasmer_header_file = PathBuf::from(&out_dir); + out_wasmer_header_file.push("wasmer"); + + // Generate the C bindings in the `OUT_DIR`. + out_wasmer_header_file.set_extension("h"); + 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(out_wasmer_header_file.as_path()); + + // Generate the C++ bindings in the `OUT_DIR`. + out_wasmer_header_file.set_extension("hh"); + 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(out_wasmer_header_file.as_path()); + + // Copy the generated C bindings from `OUT_DIR` to + // `CARGO_MANIFEST_DIR`. + crate_wasmer_header_file.set_extension("h"); + out_wasmer_header_file.set_extension("h"); + fs::copy( + out_wasmer_header_file.as_path(), + crate_wasmer_header_file.as_path(), + ) + .expect("Unable to copy the generated C bindings"); + + // Copy the generated C++ bindings from `OUT_DIR` to + // `CARGO_MANIFEST_DIR`. + crate_wasmer_header_file.set_extension("h"); + crate_wasmer_header_file.set_extension("hh"); + out_wasmer_header_file.set_extension("hh"); + fs::copy(out_wasmer_header_file, crate_wasmer_header_file) + .expect("Unable to copy the generated C++ bindings"); +} diff --git a/lib/runtime-c-api/src/lib.rs b/lib/runtime-c-api/src/lib.rs new file mode 100644 index 000000000..b63e69d9c --- /dev/null +++ b/lib/runtime-c-api/src/lib.rs @@ -0,0 +1,1612 @@ +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::module::{ExportIndex, ImportName}; +use wasmer_runtime_core::types::{ElementType, FuncSig, MemoryDescriptor, TableDescriptor, Type}; +use wasmer_runtime_core::units::{Bytes, Pages}; + +#[repr(C)] +pub struct wasmer_module_t; + +#[repr(C)] +pub struct wasmer_instance_t; + +#[repr(C)] +pub struct wasmer_instance_context_t; + +#[allow(non_camel_case_types)] +#[repr(C)] +pub enum wasmer_result_t { + WASMER_OK = 1, + WASMER_ERROR = 2, +} + +#[allow(non_camel_case_types)] +#[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_import_func_t; + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_export_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_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_import_descriptor_t; + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_import_descriptors_t; + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_export_t; + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_exports_t; + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_export_descriptor_t; + +#[repr(C)] +#[derive(Clone)] +pub struct wasmer_export_descriptors_t; + +#[allow(non_camel_case_types)] +#[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_import_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] = ::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( + 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; + } + }; + *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 delta_result = memory.grow(Pages(delta)); + match delta_result { + Ok(_) => wasmer_result_t::WASMER_OK, + Err(grow_error) => { + update_last_error(grow_error); + 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: *const wasmer_memory_t) -> uint32_t { + let memory = unsafe { &*(memory as *const 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( + 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; + } + }; + *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 delta_result = table.grow(delta); + match delta_result { + Ok(_) => wasmer_result_t::WASMER_OK, + Err(grow_error) => { + update_last_error(grow_error); + 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) }; + table.size() +} + +/// 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()) + }; + 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(); + wasmer_global_descriptor_t { + mutable: descriptor.mutable, + kind: descriptor.ty.into(), + } +} + +/// 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( + module: *mut *mut wasmer_module_t, + wasm_bytes: *mut uint8_t, + wasm_bytes_len: uint32_t, +) -> wasmer_result_t { + let bytes: &[u8] = ::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; + } + }; + *module = Box::into_raw(Box::new(new_module)) as *mut wasmer_module_t; + wasmer_result_t::WASMER_OK +} + +/// Creates a new Instance from the given module 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_module_instantiate( + module: *const wasmer_module_t, + instance: *mut *mut wasmer_instance_t, + imports: *mut wasmer_import_t, + imports_len: c_int, +) -> wasmer_result_t { + 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 => { + let mem = import.value.memory as *mut Memory; + Export::Memory((&*mem).clone()) + } + wasmer_import_export_kind::WASM_FUNCTION => { + let func_export = import.value.func as *mut Export; + (&*func_export).clone() + } + wasmer_import_export_kind::WASM_GLOBAL => { + let global = import.value.global as *mut Global; + Export::Global((&*global).clone()) + } + wasmer_import_export_kind::WASM_TABLE => { + let table = import.value.table as *mut Table; + Export::Table((&*table).clone()) + } + }; + namespace.insert(import_name, export); + } + for (module_name, namespace) in namespaces.into_iter() { + import_object.register(module_name, namespace); + } + + let module = &*(module as *const Module); + let new_instance = if let Ok(res) = module.instantiate(&import_object) { + res + } else { + update_last_error(CApiError { + msg: "error instantiating from module".to_string(), + }); + return wasmer_result_t::WASMER_ERROR; + }; + *instance = Box::into_raw(Box::new(new_instance)) as *mut wasmer_instance_t; + wasmer_result_t::WASMER_OK +} + +/// Gets export descriptors for the given module +/// +/// The caller owns the object and should call `wasmer_export_descriptors_destroy` to free it. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_export_descriptors( + module: *const wasmer_module_t, + export_descriptors: *mut *mut wasmer_export_descriptors_t, +) { + let module = &*(module as *const Module); + + let named_export_descriptors: Box = Box::new(NamedExportDescriptors( + module.info().exports.iter().map(|e| e.into()).collect(), + )); + *export_descriptors = + Box::into_raw(named_export_descriptors) as *mut wasmer_export_descriptors_t; +} + +pub struct NamedExportDescriptors(Vec); + +/// Frees the memory for the given export descriptors +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_export_descriptors_destroy( + export_descriptors: *mut wasmer_export_descriptors_t, +) { + if !export_descriptors.is_null() { + drop(Box::from_raw( + export_descriptors as *mut NamedExportDescriptors, + )); + } +} + +/// Gets the length of the export descriptors +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_export_descriptors_len( + exports: *mut wasmer_export_descriptors_t, +) -> c_int { + if exports.is_null() { + return 0; + } + (*(exports as *mut NamedExportDescriptors)).0.len() as c_int +} + +/// Gets export descriptor by index +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_export_descriptors_get( + export_descriptors: *mut wasmer_export_descriptors_t, + idx: c_int, +) -> *mut wasmer_export_descriptor_t { + if export_descriptors.is_null() { + return ptr::null_mut(); + } + let named_export_descriptors = &mut *(export_descriptors as *mut NamedExportDescriptors); + &mut (*named_export_descriptors).0[idx as usize] as *mut NamedExportDescriptor + as *mut wasmer_export_descriptor_t +} + +/// Gets name for the export descriptor +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_export_descriptor_name( + export_descriptor: *mut wasmer_export_descriptor_t, +) -> wasmer_byte_array { + let named_export_descriptor = &*(export_descriptor as *mut NamedExportDescriptor); + wasmer_byte_array { + bytes: named_export_descriptor.name.as_ptr(), + bytes_len: named_export_descriptor.name.len() as u32, + } +} + +/// Gets export descriptor kind +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_export_descriptor_kind( + export: *mut wasmer_export_descriptor_t, +) -> wasmer_import_export_kind { + let named_export_descriptor = &*(export as *mut NamedExportDescriptor); + named_export_descriptor.kind.clone() +} + +/// 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) }); + } +} + +/// Gets import descriptors for the given module +/// +/// The caller owns the object and should call `wasmer_import_descriptors_destroy` to free it. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_import_descriptors( + module: *const wasmer_module_t, + import_descriptors: *mut *mut wasmer_import_descriptors_t, +) { + let module = &*(module as *const Module); + let total_imports = module.info().imported_functions.len() + + module.info().imported_tables.len() + + module.info().imported_globals.len() + + module.info().imported_memories.len(); + let mut descriptors: Vec = Vec::with_capacity(total_imports); + + for ( + _index, + ImportName { + namespace_index, + name_index, + }, + ) in &module.info().imported_functions + { + let namespace = module.info().namespace_table.get(*namespace_index); + let name = module.info().name_table.get(*name_index); + descriptors.push(NamedImportDescriptor { + module: namespace.to_string(), + name: name.to_string(), + kind: wasmer_import_export_kind::WASM_FUNCTION, + }); + } + + for ( + _index, + ( + ImportName { + namespace_index, + name_index, + }, + _, + ), + ) in &module.info().imported_tables + { + let namespace = module.info().namespace_table.get(*namespace_index); + let name = module.info().name_table.get(*name_index); + descriptors.push(NamedImportDescriptor { + module: namespace.to_string(), + name: name.to_string(), + kind: wasmer_import_export_kind::WASM_TABLE, + }); + } + + for ( + _index, + ( + ImportName { + namespace_index, + name_index, + }, + _, + ), + ) in &module.info().imported_globals + { + let namespace = module.info().namespace_table.get(*namespace_index); + let name = module.info().name_table.get(*name_index); + descriptors.push(NamedImportDescriptor { + module: namespace.to_string(), + name: name.to_string(), + kind: wasmer_import_export_kind::WASM_GLOBAL, + }); + } + + for ( + _index, + ( + ImportName { + namespace_index, + name_index, + }, + _, + ), + ) in &module.info().imported_memories + { + let namespace = module.info().namespace_table.get(*namespace_index); + let name = module.info().name_table.get(*name_index); + descriptors.push(NamedImportDescriptor { + module: namespace.to_string(), + name: name.to_string(), + kind: wasmer_import_export_kind::WASM_MEMORY, + }); + } + + let named_import_descriptors: Box = + Box::new(NamedImportDescriptors(descriptors)); + *import_descriptors = + Box::into_raw(named_import_descriptors) as *mut wasmer_import_descriptors_t; +} + +pub struct NamedImportDescriptors(Vec); + +/// Frees the memory for the given import descriptors +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_import_descriptors_destroy( + import_descriptors: *mut wasmer_import_descriptors_t, +) { + if !import_descriptors.is_null() { + drop(Box::from_raw( + import_descriptors as *mut NamedImportDescriptors, + )); + } +} + +/// Gets the length of the import descriptors +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_import_descriptors_len( + exports: *mut wasmer_import_descriptors_t, +) -> c_int { + if exports.is_null() { + return 0; + } + (*(exports as *mut NamedImportDescriptors)).0.len() as c_int +} + +/// Gets import descriptor by index +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn wasmer_import_descriptors_get( + import_descriptors: *mut wasmer_import_descriptors_t, + idx: c_int, +) -> *mut wasmer_import_descriptor_t { + if import_descriptors.is_null() { + return ptr::null_mut(); + } + let named_import_descriptors = &mut *(import_descriptors as *mut NamedImportDescriptors); + &mut (*named_import_descriptors).0[idx as usize] as *mut NamedImportDescriptor + as *mut wasmer_import_descriptor_t +} + +/// Gets name for the import descriptor +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_import_descriptor_name( + import_descriptor: *mut wasmer_import_descriptor_t, +) -> wasmer_byte_array { + let named_import_descriptor = &*(import_descriptor as *mut NamedImportDescriptor); + wasmer_byte_array { + bytes: named_import_descriptor.name.as_ptr(), + bytes_len: named_import_descriptor.name.len() as u32, + } +} + +/// Gets module name for the import descriptor +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_import_descriptor_module_name( + import_descriptor: *mut wasmer_import_descriptor_t, +) -> wasmer_byte_array { + let named_import_descriptor = &*(import_descriptor as *mut NamedImportDescriptor); + wasmer_byte_array { + bytes: named_import_descriptor.module.as_ptr(), + bytes_len: named_import_descriptor.module.len() as u32, + } +} + +/// Gets export descriptor kind +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_import_descriptor_kind( + export: *mut wasmer_import_descriptor_t, +) -> wasmer_import_export_kind { + let named_import_descriptor = &*(export as *mut NamedImportDescriptor); + named_import_descriptor.kind.clone() +} + +/// 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( + 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 => { + let mem = import.value.memory as *mut Memory; + Export::Memory((&*mem).clone()) + } + wasmer_import_export_kind::WASM_FUNCTION => { + let func_export = import.value.func as *mut Export; + (&*func_export).clone() + } + wasmer_import_export_kind::WASM_GLOBAL => { + let global = import.value.global as *mut Global; + Export::Global((&*global).clone()) + } + wasmer_import_export_kind::WASM_TABLE => { + let table = import.value.table as *mut Table; + Export::Table((&*table).clone()) + } + }; + namespace.insert(import_name, export); + } + for (module_name, namespace) in namespaces.into_iter() { + import_object.register(module_name, namespace); + } + + let bytes: &[u8] = ::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; + } + }; + *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 = 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.is_empty() { + 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 instance_ref = &mut *(instance as *mut Instance); + let mut exports_vec: Vec = Vec::with_capacity(instance_ref.exports().count()); + for (name, export) in instance_ref.exports() { + exports_vec.push(NamedExport { + name: name.clone(), + export: export.clone(), + instance: instance as *mut Instance, + }); + } + let named_exports: Box = Box::new(NamedExports(exports_vec)); + *exports = Box::into_raw(named_exports) as *mut wasmer_exports_t; +} + +/// Sets the `data` field of the instance context. This context will be +/// passed to all imported function for instance. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_instance_context_data_set( + instance: *mut wasmer_instance_t, + data_ptr: *mut c_void, +) { + let instance_ref = unsafe { &mut *(instance as *mut Instance) }; + instance_ref.context_mut().data = data_ptr; +} + +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(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 named_exports = &mut *(exports as *mut NamedExports); + &mut (*named_exports).0[idx as usize] as *mut NamedExport as *mut wasmer_export_t +} + +/// 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, + } +} + +/// Sets the result parameter to the arity of the params of the wasmer_export_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_export_func_params_arity( + func: *const wasmer_export_func_t, + result: *mut uint32_t, +) -> wasmer_result_t { + let named_export = &*(func as *const NamedExport); + let export = &named_export.export; + if let Export::Function { ref signature, .. } = *export { + *result = signature.params().len() as uint32_t; + wasmer_result_t::WASMER_OK + } else { + update_last_error(CApiError { + msg: "func ptr error in wasmer_export_func_params_arity".to_string(), + }); + wasmer_result_t::WASMER_ERROR + } +} + +/// Sets the params buffer to the parameter types of the given wasmer_export_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_export_func_params( + func: *const wasmer_export_func_t, + params: *mut wasmer_value_tag, + params_len: c_int, +) -> wasmer_result_t { + let named_export = &*(func as *const NamedExport); + let export = &named_export.export; + 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_export_func_params".to_string(), + }); + wasmer_result_t::WASMER_ERROR + } +} + +/// Sets the returns buffer to the parameter types of the given wasmer_export_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_export_func_returns( + func: *const wasmer_export_func_t, + returns: *mut wasmer_value_tag, + returns_len: c_int, +) -> wasmer_result_t { + let named_export = &*(func as *const NamedExport); + let export = &named_export.export; + 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_export_func_returns".to_string(), + }); + wasmer_result_t::WASMER_ERROR + } +} + +/// Sets the result parameter to the arity of the returns of the wasmer_export_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_export_func_returns_arity( + func: *const wasmer_export_func_t, + result: *mut uint32_t, +) -> wasmer_result_t { + let named_export = &*(func as *const NamedExport); + let export = &named_export.export; + if let Export::Function { ref signature, .. } = *export { + *result = signature.returns().len() as uint32_t; + wasmer_result_t::WASMER_OK + } else { + update_last_error(CApiError { + msg: "func ptr error in wasmer_export_func_results_arity".to_string(), + }); + wasmer_result_t::WASMER_ERROR + } +} + +/// Sets the result parameter to the arity of the params of the wasmer_import_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_import_func_params_arity( + func: *const wasmer_import_func_t, + result: *mut uint32_t, +) -> wasmer_result_t { + let export = &*(func as *const Export); + if let Export::Function { ref signature, .. } = *export { + *result = signature.params().len() as uint32_t; + wasmer_result_t::WASMER_OK + } else { + update_last_error(CApiError { + msg: "func ptr error in wasmer_import_func_params_arity".to_string(), + }); + wasmer_result_t::WASMER_ERROR + } +} + +/// Creates new func +/// +/// The caller owns the object and should call `wasmer_import_func_destroy` to free it. +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_import_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, +) -> *mut wasmer_import_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: FuncPointer::new(func as _), + ctx: Context::Internal, + signature: Arc::new(FuncSig::new(params, returns)), + }); + Box::into_raw(export) as *mut wasmer_import_func_t +} + +/// Sets the params buffer to the parameter types of the given wasmer_import_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_import_func_params( + func: *const wasmer_import_func_t, + params: *mut wasmer_value_tag, + params_len: c_int, +) -> wasmer_result_t { + let export = &*(func as *const Export); + 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_import_func_params".to_string(), + }); + wasmer_result_t::WASMER_ERROR + } +} + +/// Sets the returns buffer to the parameter types of the given wasmer_import_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_import_func_returns( + func: *const wasmer_import_func_t, + returns: *mut wasmer_value_tag, + returns_len: c_int, +) -> wasmer_result_t { + let export = &*(func as *const Export); + 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_import_func_returns".to_string(), + }); + wasmer_result_t::WASMER_ERROR + } +} + +/// Sets the result parameter to the arity of the returns of the wasmer_import_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_import_func_returns_arity( + func: *const wasmer_import_func_t, + result: *mut uint32_t, +) -> wasmer_result_t { + let export = &*(func as *const Export); + if let Export::Function { ref signature, .. } = *export { + *result = signature.returns().len() as uint32_t; + wasmer_result_t::WASMER_OK + } else { + update_last_error(CApiError { + msg: "func ptr error in wasmer_import_func_results_arity".to_string(), + }); + wasmer_result_t::WASMER_ERROR + } +} + +/// Frees memory for the given Func +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_import_func_destroy(func: *mut wasmer_import_func_t) { + if !func.is_null() { + drop(unsafe { Box::from_raw(func as *mut Export) }); + } +} + +/// Gets export func from export +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_export_to_func( + export: *const wasmer_export_t, +) -> *const wasmer_export_func_t { + export as *const wasmer_export_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_export_func_call( + func: *const wasmer_export_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 named_export = &*(func as *mut NamedExport); + + let results: &mut [wasmer_value_t] = slice::from_raw_parts_mut(results, results_len as usize); + + let instance = &*named_export.instance; + let result = instance.call(&named_export.name, ¶ms[..]); + match result { + Ok(results_vec) => { + if !results_vec.is_empty() { + 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: *const wasmer_instance_context_t, + _memory_idx: uint32_t, +) -> *const wasmer_memory_t { + let ctx = unsafe { &*(ctx as *const Ctx) }; + let memory = ctx.memory(0); + memory as *const Memory as *const wasmer_memory_t +} + +/// Gets the `data` field within the context. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn wasmer_instance_context_data_get( + ctx: *const wasmer_instance_context_t, +) -> *mut c_void { + let ctx = unsafe { &*(ctx as *const Ctx) }; + ctx.data +} + +/// 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: *const wasmer_memory_t) -> *mut uint8_t { + let memory = mem as *const 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: wasmer_value_tag::WASM_I32, + value: wasmer_value { I32 }, + } => Value::I32(I32), + wasmer_value_t { + tag: wasmer_value_tag::WASM_I64, + value: wasmer_value { I64 }, + } => Value::I64(I64), + wasmer_value_t { + tag: wasmer_value_tag::WASM_F32, + value: wasmer_value { F32 }, + } => Value::F32(F32), + wasmer_value_t { + tag: wasmer_value_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 { + 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<&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, + } + } +} + +impl From<(&std::string::String, &ExportIndex)> for NamedExportDescriptor { + fn from((name, export_index): (&String, &ExportIndex)) -> Self { + let kind = match *export_index { + ExportIndex::Memory(_) => wasmer_import_export_kind::WASM_MEMORY, + ExportIndex::Global(_) => wasmer_import_export_kind::WASM_GLOBAL, + ExportIndex::Table(_) => wasmer_import_export_kind::WASM_TABLE, + ExportIndex::Func(_) => wasmer_import_export_kind::WASM_FUNCTION, + }; + NamedExportDescriptor { + name: name.clone(), + kind, + } + } +} + +// 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 +/// +/// ```c +/// 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 +/// +/// ```c +/// 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 NamedImportDescriptor { + module: String, + name: String, + kind: wasmer_import_export_kind, +} + +struct NamedExport { + name: String, + export: Export, + instance: *mut Instance, +} + +struct NamedExportDescriptor { + name: String, + kind: wasmer_import_export_kind, +} diff --git a/lib/runtime-c-api/tests/.gitignore b/lib/runtime-c-api/tests/.gitignore new file mode 100644 index 000000000..15e33d2cf --- /dev/null +++ b/lib/runtime-c-api/tests/.gitignore @@ -0,0 +1,23 @@ +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-imports +test-import-function +test-memory +test-module-imports +test-module +test-module-exports +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..c94c748e1 --- /dev/null +++ b/lib/runtime-c-api/tests/CMakeLists.txt @@ -0,0 +1,79 @@ +cmake_minimum_required (VERSION 2.6) +project (WasmerRuntimeCApiTests) + +add_executable(test-imports test-imports.c) +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-imports test-module-imports.c) +add_executable(test-module test-module.c) +add_executable(test-module-exports test-module-exports.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() + +enable_testing() + +set( + COMPILER_OPTIONS + # Clang or gcc + $<$,$>: + "-Werror" > + # MSVC + $<$: + "/WX" > +) + +target_link_libraries(test-imports general ${WASMER_LIB}) +target_compile_options(test-imports PRIVATE ${COMPILER_OPTIONS}) +add_test(test-imports test-imports) + +target_link_libraries(test-exports general ${WASMER_LIB}) +target_compile_options(test-exports PRIVATE ${COMPILER_OPTIONS}) +add_test(test-exports test-exports) + +target_link_libraries(test-globals general ${WASMER_LIB}) +target_compile_options(test-globals PRIVATE ${COMPILER_OPTIONS}) +add_test(test-globals test-globals) + +target_link_libraries(test-instantiate general ${WASMER_LIB}) +target_compile_options(test-instantiate PRIVATE ${COMPILER_OPTIONS}) +add_test(test-instantiate test-instantiate) + +target_link_libraries(test-import-function general ${WASMER_LIB}) +target_compile_options(test-import-function PRIVATE ${COMPILER_OPTIONS}) +add_test(test-import-function test-import-function) + +target_link_libraries(test-memory general ${WASMER_LIB}) +target_compile_options(test-memory PRIVATE ${COMPILER_OPTIONS}) +add_test(test-memory test-memory) + +target_link_libraries(test-module-imports general ${WASMER_LIB}) +target_compile_options(test-module-imports PRIVATE ${COMPILER_OPTIONS}) +add_test(test-module-imports test-module-imports) + +target_link_libraries(test-module general ${WASMER_LIB}) +target_compile_options(test-module PRIVATE ${COMPILER_OPTIONS}) +add_test(test-module test-module) + +target_link_libraries(test-module-exports general ${WASMER_LIB}) +target_compile_options(test-module-exports PRIVATE ${COMPILER_OPTIONS}) +add_test(test-module-exports test-module-exports) + +target_link_libraries(test-validate general ${WASMER_LIB}) +target_compile_options(test-validate PRIVATE ${COMPILER_OPTIONS}) +add_test(test-validate test-validate) + +target_link_libraries(test-tables general ${WASMER_LIB}) +target_compile_options(test-tables PRIVATE ${COMPILER_OPTIONS}) +add_test(test-tables test-tables) diff --git a/lib/runtime-c-api/tests/hello_wasm.wasm b/lib/runtime-c-api/tests/hello_wasm.wasm new file mode 100644 index 000000000..b2287be03 Binary files /dev/null and b/lib/runtime-c-api/tests/hello_wasm.wasm differ 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..4349133a7 --- /dev/null +++ b/lib/runtime-c-api/tests/runtime_c_api_tests.rs @@ -0,0 +1,49 @@ +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, Some("-Wdev -Werror=dev")); + 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..b282f1911 --- /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); + const wasmer_export_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_export_func_params_arity(func, ¶ms_arity); + assert(params_arity == 2); + + wasmer_value_tag *params_sig = malloc(sizeof(wasmer_value_tag) * params_arity); + wasmer_export_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_export_func_returns_arity(func, &returns_arity); + assert(returns_arity == 1); + + wasmer_value_tag *returns_sig = malloc(sizeof(wasmer_value_tag) * returns_arity); + wasmer_export_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_export_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; +} 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..ce6736deb --- /dev/null +++ b/lib/runtime-c-api/tests/test-import-function.c @@ -0,0 +1,105 @@ +#include +#include "../wasmer.h" +#include +#include +#include + +static bool print_str_called = false; +static int memory_len = 0; +static int ptr_len = 0; +static char actual_str[14] = {}; +static int actual_context_data_value = 0; + +typedef struct { + int value; +} context_data; + +void print_str(wasmer_instance_context_t *ctx, int32_t ptr, int32_t len) +{ + const 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; + + actual_context_data_value = ((context_data *) wasmer_instance_context_data_get(ctx))->value; +} + +int main() +{ + wasmer_value_tag params_sig[] = {WASM_I32, WASM_I32}; + wasmer_value_tag returns_sig[] = {}; + + printf("Creating new func\n"); + wasmer_import_func_t *func = wasmer_import_func_new((void (*)(void *)) 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 = (const uint8_t *) 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 = (const uint8_t *) 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); + + context_data* context_data = malloc(sizeof(context_data)); + int context_data_value = 42; + context_data->value = context_data_value; + wasmer_instance_context_data_set(instance, context_data); + + 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!")); + assert(context_data_value == actual_context_data_value); + + printf("Destroying func\n"); + wasmer_import_func_destroy(func); + printf("Destroy instance\n"); + wasmer_instance_destroy(instance); + free(context_data); + return 0; +} diff --git a/lib/runtime-c-api/tests/test-imports.c b/lib/runtime-c-api/tests/test-imports.c new file mode 100644 index 000000000..9c0c45467 --- /dev/null +++ b/lib/runtime-c-api/tests/test-imports.c @@ -0,0 +1,155 @@ +#include +#include "../wasmer.h" +#include +#include +#include + +bool static print_str_called = false; + +// Host function that will be imported into the Web Assembly Instance +void print_str(const wasmer_instance_context_t *ctx, int32_t ptr, int32_t len) +{ + print_str_called = true; + const 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); + printf("%.*s", len, mem_bytes + ptr); +} + +// Use the last_error API to retrieve error messages +void print_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); +} + +int main() +{ + // Create a new func to hold the parameter and signature + // of our `print_str` host function + wasmer_value_tag params_sig[] = {WASM_I32, WASM_I32}; + wasmer_value_tag returns_sig[] = {}; + wasmer_import_func_t *func = wasmer_import_func_new((void (*)(void *)) print_str, params_sig, 2, returns_sig, 0); + + // Create module name for our imports + // represented in bytes for UTF-8 compatability + const char *module_name = "env"; + wasmer_byte_array module_name_bytes; + module_name_bytes.bytes = (const uint8_t *) module_name; + module_name_bytes.bytes_len = strlen(module_name); + + // Define a function import + const char *import_name = "_print_str"; + wasmer_byte_array import_name_bytes; + import_name_bytes.bytes = (const uint8_t *) import_name; + import_name_bytes.bytes_len = strlen(import_name); + wasmer_import_t func_import; + func_import.module_name = module_name_bytes; + func_import.import_name = import_name_bytes; + func_import.tag = WASM_FUNCTION; + func_import.value.func = func; + + // Define a memory import + const char *import_memory_name = "memory"; + wasmer_byte_array import_memory_name_bytes; + import_memory_name_bytes.bytes = (const uint8_t *) import_memory_name; + import_memory_name_bytes.bytes_len = strlen(import_memory_name); + wasmer_import_t memory_import; + memory_import.module_name = module_name_bytes; + memory_import.import_name = import_memory_name_bytes; + memory_import.tag = WASM_MEMORY; + wasmer_memory_t *memory = NULL; + wasmer_limits_t descriptor; + descriptor.min = 256; + wasmer_limit_option_t max; + max.has_some = true; + max.some = 256; + descriptor.max = max; + wasmer_result_t memory_result = wasmer_memory_new(&memory, descriptor); + if (memory_result != WASMER_OK) + { + print_wasmer_error(); + } + memory_import.value.memory = memory; + + // Define a global import + const char *import_global_name = "__memory_base"; + wasmer_byte_array import_global_name_bytes; + import_global_name_bytes.bytes = (const uint8_t *) import_global_name; + import_global_name_bytes.bytes_len = strlen(import_global_name); + wasmer_import_t global_import; + global_import.module_name = module_name_bytes; + global_import.import_name = import_global_name_bytes; + global_import.tag = WASM_GLOBAL; + wasmer_value_t val; + val.tag = WASM_I32; + val.value.I32 = 1024; + wasmer_global_t *global = wasmer_global_new(val, false); + global_import.value.global = global; + + // Define a table import + const char *import_table_name = "table"; + wasmer_byte_array import_table_name_bytes; + import_table_name_bytes.bytes = (const uint8_t *) import_table_name; + import_table_name_bytes.bytes_len = strlen(import_table_name); + wasmer_import_t table_import; + table_import.module_name = module_name_bytes; + table_import.import_name = import_table_name_bytes; + table_import.tag = WASM_TABLE; + wasmer_table_t *table = NULL; + wasmer_limits_t table_descriptor; + table_descriptor.min = 256; + wasmer_limit_option_t table_max; + table_max.has_some = true; + table_max.some = 256; + table_descriptor.max = table_max; + wasmer_result_t table_result = wasmer_table_new(&table, table_descriptor); + if (table_result != WASMER_OK) + { + print_wasmer_error(); + } + table_import.value.table = table; + + // Define an array containing our imports + wasmer_import_t imports[] = {func_import, global_import, memory_import, table_import}; + + // Read the wasm file bytes + FILE *file = fopen("hello_wasm.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); + + // Creates a WebAssembly Instance from wasm bytes and imports + wasmer_instance_t *instance = NULL; + wasmer_result_t compile_result = wasmer_instantiate(&instance, bytes, len, imports, 4); + printf("Compile result: %d\n", compile_result); + if (compile_result != WASMER_OK) + { + print_wasmer_error(); + } + assert(compile_result == WASMER_OK); + + // Call the exported "hello_wasm" function of our instance + wasmer_value_t params[] = {}; + wasmer_value_t result_one; + wasmer_value_t results[] = {result_one}; + wasmer_result_t call_result = wasmer_instance_call(instance, "_hello_wasm", params, 0, results, 1); + printf("Call result: %d\n", call_result); + assert(call_result == WASMER_OK); + assert(print_str_called); + + // Use *_destroy methods to cleanup as specified in the header documentation + wasmer_import_func_destroy(func); + wasmer_global_destroy(global); + wasmer_memory_destroy(memory); + wasmer_table_destroy(table); + wasmer_instance_destroy(instance); + + return 0; +} 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..e7fae37e4 --- /dev/null +++ b/lib/runtime-c-api/tests/test-instantiate.c @@ -0,0 +1,57 @@ +#include +#include "../wasmer.h" +#include +#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; +} 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..9adf9215c --- /dev/null +++ b/lib/runtime-c-api/tests/test-memory.c @@ -0,0 +1,66 @@ +#include +#include "../wasmer.h" +#include +#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, "Failed to add pages because would exceed maximum number of pages for the memory. Left: 22, Added: 15")); + free(error_str); + + wasmer_memory_t *bad_memory = NULL; + wasmer_limits_t bad_descriptor; + bad_descriptor.min = 15; + wasmer_limit_option_t max2; + max2.has_some = true; + max2.some = 10; + bad_descriptor.max = max2; + wasmer_result_t bad_memory_result = wasmer_memory_new(&bad_memory, bad_descriptor); + printf("Bad memory result: %d\n", bad_memory_result); + assert(bad_memory_result == WASMER_ERROR); + + int error_len2 = wasmer_last_error_length(); + char *error_str2 = malloc(error_len2); + wasmer_last_error_message(error_str2, error_len2); + printf("Error str 2: `%s`\n", error_str2); + assert(0 == strcmp(error_str2, "Unable to create because the supplied descriptor is invalid: \"Max number of memory pages is less than the minimum number of pages\"")); + free(error_str2); + + printf("Destroy memory\n"); + wasmer_memory_destroy(memory); + return 0; +} diff --git a/lib/runtime-c-api/tests/test-module-exports.c b/lib/runtime-c-api/tests/test-module-exports.c new file mode 100644 index 000000000..48fe6f25b --- /dev/null +++ b/lib/runtime-c-api/tests/test-module-exports.c @@ -0,0 +1,53 @@ +#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); + + wasmer_import_t imports[] = {}; + wasmer_instance_t *instance = NULL; + wasmer_result_t instantiate_result = wasmer_module_instantiate(module, &instance, imports, 0); + printf("Instantiate result: %d\n", compile_result); + assert(instantiate_result == WASMER_OK); + + wasmer_export_descriptors_t *exports = NULL; + wasmer_export_descriptors(module, &exports); + + int exports_len = wasmer_export_descriptors_len(exports); + printf("exports_len: %d\n", exports_len); + assert(exports_len == 1); + + wasmer_export_descriptor_t *export = wasmer_export_descriptors_get(exports, 0); + + wasmer_import_export_kind kind = wasmer_export_descriptor_kind(export); + assert(kind == WASM_FUNCTION); + + wasmer_byte_array name_bytes = wasmer_export_descriptor_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]); + } + + printf("Destroy module\n"); + wasmer_module_destroy(module); + printf("Destroy exports\n"); + wasmer_export_descriptors_destroy(exports); + return 0; +} \ No newline at end of file diff --git a/lib/runtime-c-api/tests/test-module-imports.c b/lib/runtime-c-api/tests/test-module-imports.c new file mode 100644 index 000000000..b63d308f2 --- /dev/null +++ b/lib/runtime-c-api/tests/test-module-imports.c @@ -0,0 +1,56 @@ +#include +#include "../wasmer.h" +#include +#include + +int main() +{ + // 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); + + 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); + + wasmer_import_descriptors_t *imports = NULL; + wasmer_import_descriptors(module, &imports); + + int imports_len = wasmer_import_descriptors_len(imports); + printf("imports_len: %d\n", imports_len); + assert(imports_len == 1); + + wasmer_import_descriptor_t *import = wasmer_import_descriptors_get(imports, 0); + + wasmer_import_export_kind kind = wasmer_import_descriptor_kind(import); + assert(kind == WASM_FUNCTION); + + wasmer_byte_array name_bytes = wasmer_import_descriptor_name(import); + assert(name_bytes.bytes_len == 9); + char expected[] = {'p', 'r', 'i', 'n', 't', '_', 's', 't', 'r'}; + + for(int idx = 0; idx < 9; idx++){ + printf("%c\n", name_bytes.bytes[idx]); + assert(name_bytes.bytes[idx] == expected[idx]); + } + + wasmer_byte_array module_name_bytes = wasmer_import_descriptor_module_name(import); + assert(module_name_bytes.bytes_len == 3); + char module_expected[] = {'e', 'n', 'v'}; + for(int idx = 0; idx < 3; idx++){ + printf("%c\n", module_name_bytes.bytes[idx]); + assert(module_name_bytes.bytes[idx] == module_expected[idx]); + } + + printf("Destroy module\n"); + wasmer_module_destroy(module); + printf("Destroy imports\n"); + wasmer_import_descriptors_destroy(imports); + 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..062caf5b8 --- /dev/null +++ b/lib/runtime-c-api/tests/test-module.c @@ -0,0 +1,51 @@ +#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); + + wasmer_import_t imports[] = {}; + wasmer_instance_t *instance = NULL; + wasmer_result_t instantiate_result = wasmer_module_instantiate(module, &instance, imports, 0); + printf("Instantiate result: %d\n", compile_result); + assert(instantiate_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); + + printf("Destroy instance\n"); + wasmer_instance_destroy(instance); + + 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..ac093d182 --- /dev/null +++ b/lib/runtime-c-api/tests/test-tables.c @@ -0,0 +1,51 @@ +#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 == 10); + + 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; + wasmer_limit_option_t max2; + max2.has_some = true; + max2.some = 10; + bad_descriptor.max = max2; + wasmer_result_t table_bad_result = wasmer_table_new(&table_bad, bad_descriptor); + printf("Table result: %d\n", table_bad_result); + assert(table_bad_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..43d97ecb8 --- /dev/null +++ b/lib/runtime-c-api/wasmer.h @@ -0,0 +1,540 @@ +#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_module_t; + +typedef struct { + +} wasmer_export_descriptor_t; + +typedef struct { + const uint8_t *bytes; + uint32_t bytes_len; +} wasmer_byte_array; + +typedef struct { + +} wasmer_export_descriptors_t; + +typedef struct { + +} wasmer_export_func_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_export_t; + +typedef struct { + +} wasmer_exports_t; + +typedef struct { + +} wasmer_global_t; + +typedef struct { + bool mutable_; + wasmer_value_tag kind; +} wasmer_global_descriptor_t; + +typedef struct { + +} wasmer_import_descriptor_t; + +typedef struct { + +} wasmer_import_descriptors_t; + +typedef struct { + +} wasmer_import_func_t; + +typedef struct { + +} wasmer_instance_t; + +typedef struct { + +} wasmer_instance_context_t; + +typedef struct { + +} wasmer_memory_t; + +typedef struct { + +} wasmer_table_t; + +typedef union { + const wasmer_import_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 export descriptor kind + */ +wasmer_import_export_kind wasmer_export_descriptor_kind(wasmer_export_descriptor_t *export_); + +/** + * Gets name for the export descriptor + */ +wasmer_byte_array wasmer_export_descriptor_name(wasmer_export_descriptor_t *export_descriptor); + +/** + * Gets export descriptors for the given module + * The caller owns the object and should call `wasmer_export_descriptors_destroy` to free it. + */ +void wasmer_export_descriptors(const wasmer_module_t *module, + wasmer_export_descriptors_t **export_descriptors); + +/** + * Frees the memory for the given export descriptors + */ +void wasmer_export_descriptors_destroy(wasmer_export_descriptors_t *export_descriptors); + +/** + * Gets export descriptor by index + */ +wasmer_export_descriptor_t *wasmer_export_descriptors_get(wasmer_export_descriptors_t *export_descriptors, + int idx); + +/** + * Gets the length of the export descriptors + */ +int wasmer_export_descriptors_len(wasmer_export_descriptors_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_export_func_call(const wasmer_export_func_t *func, + const wasmer_value_t *params, + int params_len, + wasmer_value_t *results, + int results_len); + +/** + * Sets the params buffer to the parameter types of the given wasmer_export_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_export_func_params(const wasmer_export_func_t *func, + wasmer_value_tag *params, + int params_len); + +/** + * Sets the result parameter to the arity of the params of the wasmer_export_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_export_func_params_arity(const wasmer_export_func_t *func, uint32_t *result); + +/** + * Sets the returns buffer to the parameter types of the given wasmer_export_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_export_func_returns(const wasmer_export_func_t *func, + wasmer_value_tag *returns, + int returns_len); + +/** + * Sets the result parameter to the arity of the returns of the wasmer_export_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_export_func_returns_arity(const wasmer_export_func_t *func, + uint32_t *result); + +/** + * 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 export func from export + */ +const wasmer_export_func_t *wasmer_export_to_func(const 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); + +/** + * 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); + +/** + * Gets export descriptor kind + */ +wasmer_import_export_kind wasmer_import_descriptor_kind(wasmer_import_descriptor_t *export_); + +/** + * Gets module name for the import descriptor + */ +wasmer_byte_array wasmer_import_descriptor_module_name(wasmer_import_descriptor_t *import_descriptor); + +/** + * Gets name for the import descriptor + */ +wasmer_byte_array wasmer_import_descriptor_name(wasmer_import_descriptor_t *import_descriptor); + +/** + * Gets import descriptors for the given module + * The caller owns the object and should call `wasmer_import_descriptors_destroy` to free it. + */ +void wasmer_import_descriptors(const wasmer_module_t *module, + wasmer_import_descriptors_t **import_descriptors); + +/** + * Frees the memory for the given import descriptors + */ +void wasmer_import_descriptors_destroy(wasmer_import_descriptors_t *import_descriptors); + +/** + * Gets import descriptor by index + */ +wasmer_import_descriptor_t *wasmer_import_descriptors_get(wasmer_import_descriptors_t *import_descriptors, + int idx); + +/** + * Gets the length of the import descriptors + */ +int wasmer_import_descriptors_len(wasmer_import_descriptors_t *exports); + +/** + * Frees memory for the given Func + */ +void wasmer_import_func_destroy(wasmer_import_func_t *func); + +/** + * Creates new func + * The caller owns the object and should call `wasmer_import_func_destroy` to free it. + */ +wasmer_import_func_t *wasmer_import_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_import_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_import_func_params(const wasmer_import_func_t *func, + wasmer_value_tag *params, + int params_len); + +/** + * Sets the result parameter to the arity of the params of the wasmer_import_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_import_func_params_arity(const wasmer_import_func_t *func, uint32_t *result); + +/** + * Sets the returns buffer to the parameter types of the given wasmer_import_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_import_func_returns(const wasmer_import_func_t *func, + wasmer_value_tag *returns, + int returns_len); + +/** + * Sets the result parameter to the arity of the returns of the wasmer_import_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_import_func_returns_arity(const wasmer_import_func_t *func, + uint32_t *result); + +/** + * 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 `data` field within the context. + */ +void *wasmer_instance_context_data_get(const wasmer_instance_context_t *ctx); + +/** + * Sets the `data` field of the instance context. This context will be + * passed to all imported function for instance. + */ +void wasmer_instance_context_data_set(wasmer_instance_t *instance, void *data_ptr); + +/** + * 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(const 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 + * ```c + * 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 + * ```c + * 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(const 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(const 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); + +/** + * Creates a new Instance from the given module 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_module_instantiate(const wasmer_module_t *module, + wasmer_instance_t **instance, + wasmer_import_t *imports, + int imports_len); + +/** + * 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..76c27d46e --- /dev/null +++ b/lib/runtime-c-api/wasmer.hh @@ -0,0 +1,425 @@ +#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_module_t { + +}; + +struct wasmer_export_descriptor_t { + +}; + +struct wasmer_byte_array { + const uint8_t *bytes; + uint32_t bytes_len; +}; + +struct wasmer_export_descriptors_t { + +}; + +struct wasmer_export_func_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_export_t { + +}; + +struct wasmer_exports_t { + +}; + +struct wasmer_global_t { + +}; + +struct wasmer_global_descriptor_t { + bool mutable_; + wasmer_value_tag kind; +}; + +struct wasmer_import_descriptor_t { + +}; + +struct wasmer_import_descriptors_t { + +}; + +struct wasmer_import_func_t { + +}; + +struct wasmer_instance_t { + +}; + +struct wasmer_instance_context_t { + +}; + +struct wasmer_memory_t { + +}; + +struct wasmer_table_t { + +}; + +union wasmer_import_export_value { + const wasmer_import_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 export descriptor kind +wasmer_import_export_kind wasmer_export_descriptor_kind(wasmer_export_descriptor_t *export_); + +/// Gets name for the export descriptor +wasmer_byte_array wasmer_export_descriptor_name(wasmer_export_descriptor_t *export_descriptor); + +/// Gets export descriptors for the given module +/// The caller owns the object and should call `wasmer_export_descriptors_destroy` to free it. +void wasmer_export_descriptors(const wasmer_module_t *module, + wasmer_export_descriptors_t **export_descriptors); + +/// Frees the memory for the given export descriptors +void wasmer_export_descriptors_destroy(wasmer_export_descriptors_t *export_descriptors); + +/// Gets export descriptor by index +wasmer_export_descriptor_t *wasmer_export_descriptors_get(wasmer_export_descriptors_t *export_descriptors, + int idx); + +/// Gets the length of the export descriptors +int wasmer_export_descriptors_len(wasmer_export_descriptors_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_export_func_call(const wasmer_export_func_t *func, + const wasmer_value_t *params, + int params_len, + wasmer_value_t *results, + int results_len); + +/// Sets the params buffer to the parameter types of the given wasmer_export_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_export_func_params(const wasmer_export_func_t *func, + wasmer_value_tag *params, + int params_len); + +/// Sets the result parameter to the arity of the params of the wasmer_export_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_export_func_params_arity(const wasmer_export_func_t *func, uint32_t *result); + +/// Sets the returns buffer to the parameter types of the given wasmer_export_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_export_func_returns(const wasmer_export_func_t *func, + wasmer_value_tag *returns, + int returns_len); + +/// Sets the result parameter to the arity of the returns of the wasmer_export_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_export_func_returns_arity(const wasmer_export_func_t *func, + uint32_t *result); + +/// 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 export func from export +const wasmer_export_func_t *wasmer_export_to_func(const 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); + +/// 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); + +/// Gets export descriptor kind +wasmer_import_export_kind wasmer_import_descriptor_kind(wasmer_import_descriptor_t *export_); + +/// Gets module name for the import descriptor +wasmer_byte_array wasmer_import_descriptor_module_name(wasmer_import_descriptor_t *import_descriptor); + +/// Gets name for the import descriptor +wasmer_byte_array wasmer_import_descriptor_name(wasmer_import_descriptor_t *import_descriptor); + +/// Gets import descriptors for the given module +/// The caller owns the object and should call `wasmer_import_descriptors_destroy` to free it. +void wasmer_import_descriptors(const wasmer_module_t *module, + wasmer_import_descriptors_t **import_descriptors); + +/// Frees the memory for the given import descriptors +void wasmer_import_descriptors_destroy(wasmer_import_descriptors_t *import_descriptors); + +/// Gets import descriptor by index +wasmer_import_descriptor_t *wasmer_import_descriptors_get(wasmer_import_descriptors_t *import_descriptors, + int idx); + +/// Gets the length of the import descriptors +int wasmer_import_descriptors_len(wasmer_import_descriptors_t *exports); + +/// Frees memory for the given Func +void wasmer_import_func_destroy(wasmer_import_func_t *func); + +/// Creates new func +/// The caller owns the object and should call `wasmer_import_func_destroy` to free it. +wasmer_import_func_t *wasmer_import_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_import_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_import_func_params(const wasmer_import_func_t *func, + wasmer_value_tag *params, + int params_len); + +/// Sets the result parameter to the arity of the params of the wasmer_import_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_import_func_params_arity(const wasmer_import_func_t *func, uint32_t *result); + +/// Sets the returns buffer to the parameter types of the given wasmer_import_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_import_func_returns(const wasmer_import_func_t *func, + wasmer_value_tag *returns, + int returns_len); + +/// Sets the result parameter to the arity of the returns of the wasmer_import_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_import_func_returns_arity(const wasmer_import_func_t *func, + uint32_t *result); + +/// 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 `data` field within the context. +void *wasmer_instance_context_data_get(const wasmer_instance_context_t *ctx); + +/// Sets the `data` field of the instance context. This context will be +/// passed to all imported function for instance. +void wasmer_instance_context_data_set(wasmer_instance_t *instance, void *data_ptr); + +/// 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(const 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 +/// ```c +/// 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 +/// ```c +/// 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(const 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(const 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); + +/// Creates a new Instance from the given module 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_module_instantiate(const wasmer_module_t *module, + wasmer_instance_t **instance, + wasmer_import_t *imports, + int imports_len); + +/// 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/Cargo.toml b/lib/runtime-core/Cargo.toml index 9a748eb62..c6ac22f40 100644 --- a/lib/runtime-core/Cargo.toml +++ b/lib/runtime-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-runtime-core" -version = "0.1.2" +version = "0.2.1" description = "Wasmer runtime core library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -8,7 +8,6 @@ repository = "https://github.com/wasmerio/wasmer" edition = "2018" [dependencies] -hashbrown = "0.1" nix = "0.12.0" page_size = "0.4.1" wasmparser = "0.23.0" @@ -16,36 +15,34 @@ parking_lot = "0.7.1" lazy_static = "1.2.0" indexmap = "1.0.2" errno = "0.2.4" -libc = "0.2.48" +libc = "0.2.49" +hex = "0.3.2" # Dependencies for caching. [dependencies.serde] version = "1.0" -optional = true +# This feature is required for serde to support serializing/deserializing reference counted pointers (e.g. Rc and Arc). +features = ["rc"] [dependencies.serde_derive] version = "1.0" -optional = true [dependencies.serde_bytes] version = "0.10" -optional = true [dependencies.serde-bench] version = "0.0.7" -optional = true -[dependencies.memmap] -version = "0.7.0" -optional = true -[dependencies.sha2] +[dependencies.blake2b_simd] +version = "0.4.1" +[dependencies.digest] version = "0.8.0" -optional = true +[dependencies.hashbrown] +version = "0.1" +features = ["serde"] [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["memoryapi"] } [dev-dependencies] -wasmer-clif-backend = { path = "../clif-backend", version = "0.1.2" } field-offset = "0.1.1" [features] debug = [] -cache = ["serde/rc", "serde_derive", "serde_bytes", "hashbrown/serde", "serde-bench", "memmap", "sha2"] diff --git a/lib/runtime-core/src/backend.rs b/lib/runtime-core/src/backend.rs index a61308c1a..a09909ebe 100644 --- a/lib/runtime-core/src/backend.rs +++ b/lib/runtime-core/src/backend.rs @@ -6,24 +6,24 @@ use crate::{ types::{FuncIndex, LocalFuncIndex, Value}, vm, }; -#[cfg(feature = "cache")] + use crate::{ - cache::{Cache, Error as CacheError}, + cache::{Artifact, Error as CacheError}, module::ModuleInfo, sys::Memory, }; -use std::ptr::NonNull; +use std::{any::Any, ptr::NonNull}; pub mod sys { pub use crate::sys::*; } pub use crate::sig_registry::SigRegistry; -#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)] pub enum Backend { Cranelift, Dynasm, + LLVM, } /// This type cannot be constructed from @@ -44,15 +44,7 @@ pub trait Compiler { /// be called from inside the runtime. fn compile(&self, wasm: &[u8], _: Token) -> CompileResult; - #[cfg(feature = "cache")] - unsafe fn from_cache(&self, cache: Cache, _: Token) -> Result; - - #[cfg(feature = "cache")] - fn compile_to_backend_cache_data( - &self, - wasm: &[u8], - _: Token, - ) -> CompileResult<(Box, Vec, Memory)>; + unsafe fn from_cache(&self, cache: Artifact, _: Token) -> Result; } /// The functionality exposed by this trait is expected to be used @@ -88,7 +80,7 @@ pub trait ProtectedCaller: Send + Sync { } pub trait UserTrapper { - unsafe fn do_early_trap(&self, msg: String) -> !; + unsafe fn do_early_trap(&self, data: Box) -> !; } pub trait FuncResolver: Send + Sync { @@ -100,3 +92,10 @@ pub trait FuncResolver: Send + Sync { local_func_index: LocalFuncIndex, ) -> Option>; } + +pub trait CacheGen: Send + Sync { + fn generate_cache( + &self, + module: &ModuleInner, + ) -> Result<(Box, Box<[u8]>, Memory), CacheError>; +} diff --git a/lib/runtime-core/src/backing.rs b/lib/runtime-core/src/backing.rs index d5abca610..7a1e7239a 100644 --- a/lib/runtime-core/src/backing.rs +++ b/lib/runtime-core/src/backing.rs @@ -4,17 +4,18 @@ use crate::{ global::Global, import::ImportObject, memory::Memory, - module::{ImportName, ModuleInner}, + module::{ImportName, ModuleInfo, ModuleInner}, sig_registry::SigRegistry, structures::{BoxedMap, Map, SliceMap, TypedIndex}, table::Table, types::{ ImportedFuncIndex, ImportedGlobalIndex, ImportedMemoryIndex, ImportedTableIndex, - Initializer, LocalGlobalIndex, LocalMemoryIndex, LocalOrImport, LocalTableIndex, Value, + Initializer, LocalFuncIndex, LocalGlobalIndex, LocalMemoryIndex, LocalOrImport, + LocalTableIndex, SigIndex, Value, }, vm, }; -use std::{slice, sync::Arc}; +use std::slice; #[derive(Debug)] pub struct LocalBacking { @@ -25,6 +26,9 @@ pub struct LocalBacking { pub(crate) vm_memories: BoxedMap, pub(crate) vm_tables: BoxedMap, pub(crate) vm_globals: BoxedMap, + + pub(crate) dynamic_sigindices: BoxedMap, + pub(crate) local_functions: BoxedMap, } // impl LocalBacking { @@ -47,6 +51,9 @@ impl LocalBacking { let vm_tables = Self::finalize_tables(module, imports, &mut tables, vmctx); let vm_globals = Self::finalize_globals(&mut globals); + let dynamic_sigindices = Self::generate_sigindices(&module.info); + let local_functions = Self::generate_local_functions(module); + Self { memories, tables, @@ -55,9 +62,37 @@ impl LocalBacking { vm_memories, vm_tables, vm_globals, + + dynamic_sigindices, + local_functions, } } + fn generate_local_functions(module: &ModuleInner) -> BoxedMap { + (0..module.info.func_assoc.len() - module.info.imported_functions.len()) + .map(|index| { + module + .func_resolver + .get(module, LocalFuncIndex::new(index)) + .unwrap() + .as_ptr() as *const _ + }) + .collect::>() + .into_boxed_map() + } + + fn generate_sigindices(info: &ModuleInfo) -> BoxedMap { + info.signatures + .iter() + .map(|(_, signature)| { + let signature = SigRegistry.lookup_signature_ref(signature); + let sig_index = SigRegistry.lookup_sig_index(signature); + vm::SigId(sig_index.index() as u32) + }) + .collect::>() + .into_boxed_map() + } + fn generate_memories(module: &ModuleInner) -> BoxedMap { let mut memories = Map::with_capacity(module.info.memories.len()); for (_, &desc) in &module.info.memories { @@ -91,7 +126,7 @@ impl LocalBacking { } } as usize; - match init.memory_index.local_or_import(module) { + match init.memory_index.local_or_import(&module.info) { LocalOrImport::Local(local_memory_index) => { let memory_desc = module.info.memories[local_memory_index]; let data_top = init_base + init.data.len(); @@ -159,7 +194,7 @@ impl LocalBacking { } } as usize; - match init.table_index.local_or_import(module) { + match init.table_index.local_or_import(&module.info) { LocalOrImport::Local(local_table_index) => { let table = &tables[local_table_index]; @@ -172,12 +207,13 @@ impl LocalBacking { table.anyfunc_direct_access_mut(|elements| { for (i, &func_index) in init.elements.iter().enumerate() { let sig_index = module.info.func_assoc[func_index]; - let signature = &module.info.signatures[sig_index]; - let sig_id = vm::SigId( - SigRegistry.lookup_sig_index(Arc::clone(&signature)).index() as u32, - ); + // let signature = &module.info.signatures[sig_index]; + let signature = SigRegistry + .lookup_signature_ref(&module.info.signatures[sig_index]); + let sig_id = + vm::SigId(SigRegistry.lookup_sig_index(signature).index() as u32); - let (func, ctx) = match func_index.local_or_import(module) { + let (func, ctx) = match func_index.local_or_import(&module.info) { LocalOrImport::Local(local_func_index) => ( module .func_resolver @@ -210,12 +246,13 @@ impl LocalBacking { table.anyfunc_direct_access_mut(|elements| { for (i, &func_index) in init.elements.iter().enumerate() { let sig_index = module.info.func_assoc[func_index]; - let signature = &module.info.signatures[sig_index]; - let sig_id = vm::SigId( - SigRegistry.lookup_sig_index(Arc::clone(&signature)).index() as u32, - ); + let signature = SigRegistry + .lookup_signature_ref(&module.info.signatures[sig_index]); + // let signature = &module.info.signatures[sig_index]; + let sig_id = + vm::SigId(SigRegistry.lookup_sig_index(signature).index() as u32); - let (func, ctx) = match func_index.local_or_import(module) { + let (func, ctx) = match func_index.local_or_import(&module.info) { LocalOrImport::Local(local_func_index) => ( module .func_resolver @@ -369,7 +406,7 @@ fn import_functions( }, ) in &module.info.imported_functions { - let sig_index = module.info.func_assoc[index.convert_up(module)]; + let sig_index = module.info.func_assoc[index.convert_up(&module.info)]; let expected_sig = &module.info.signatures[sig_index]; let namespace = module.info.namespace_table.get(*namespace_index); @@ -384,7 +421,7 @@ fn import_functions( ctx, signature, }) => { - if *expected_sig == signature { + if *expected_sig == *signature { functions.push(vm::ImportedFunc { func: func.inner(), vmctx: match ctx { @@ -396,8 +433,8 @@ fn import_functions( link_errors.push(LinkError::IncorrectImportSignature { namespace: namespace.to_string(), name: name.to_string(), - expected: expected_sig.clone(), - found: signature.clone(), + expected: (*expected_sig).clone(), + found: (*signature).clone(), }); } } diff --git a/lib/runtime-core/src/cache.rs b/lib/runtime-core/src/cache.rs index e166599e8..1cb804655 100644 --- a/lib/runtime-core/src/cache.rs +++ b/lib/runtime-core/src/cache.rs @@ -1,14 +1,9 @@ -use crate::{module::ModuleInfo, sys::Memory}; -use memmap::Mmap; -use serde_bench::{deserialize, serialize}; -use sha2::{Digest, Sha256}; -use std::{ - fs::File, - io::{self, Seek, SeekFrom, Write}, - mem, - path::Path, - slice, +use crate::{ + module::{Module, ModuleInfo}, + sys::Memory, }; +use blake2b_simd::blake2bp; +use std::{fmt, io, mem, slice}; #[derive(Debug)] pub enum InvalidFileType { @@ -26,23 +21,92 @@ pub enum Error { InvalidatedCache, } +impl From for Error { + fn from(io_err: io::Error) -> Self { + Error::IoError(io_err) + } +} + +/// The hash of a wasm module. +/// +/// Used as a key when loading and storing modules in a [`Cache`]. +/// +/// [`Cache`]: trait.Cache.html +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct WasmHash([u8; 32], [u8; 32]); + +impl WasmHash { + /// Hash a wasm module. + /// + /// # Note: + /// This does no verification that the supplied data + /// is, in fact, a wasm module. + pub fn generate(wasm: &[u8]) -> Self { + let mut first_part = [0u8; 32]; + let mut second_part = [0u8; 32]; + + let mut state = blake2bp::State::new(); + state.update(wasm); + + let hasher = state.finalize(); + let generic_array = hasher.as_bytes(); + + first_part.copy_from_slice(&generic_array[0..32]); + second_part.copy_from_slice(&generic_array[32..64]); + WasmHash(first_part, second_part) + } + + /// Create the hexadecimal representation of the + /// stored hash. + pub fn encode(self) -> String { + hex::encode(&self.into_array() as &[u8]) + } + + pub(crate) fn into_array(self) -> [u8; 64] { + let mut total = [0u8; 64]; + total[0..32].copy_from_slice(&self.0); + total[32..64].copy_from_slice(&self.1); + total + } +} + const CURRENT_CACHE_VERSION: u64 = 0; +static WASMER_CACHE_MAGIC: [u8; 8] = *b"WASMER\0\0"; /// The header of a cache file. #[repr(C, packed)] -struct CacheHeader { +struct ArtifactHeader { magic: [u8; 8], // [W, A, S, M, E, R, \0, \0] version: u64, data_len: u64, - wasm_hash: [u8; 32], // Sha256 of the wasm in binary format. } -impl CacheHeader { - pub fn read_from_slice(buffer: &[u8]) -> Result<(&CacheHeader, &[u8]), Error> { - if buffer.len() >= mem::size_of::() { - if &buffer[..8] == "WASMER\0\0".as_bytes() { - let (header_slice, body_slice) = buffer.split_at(mem::size_of::()); - let header = unsafe { &*(header_slice.as_ptr() as *const CacheHeader) }; +impl ArtifactHeader { + pub fn read_from_slice(buffer: &[u8]) -> Result<(&Self, &[u8]), Error> { + if buffer.len() >= mem::size_of::() { + if &buffer[..8] == &WASMER_CACHE_MAGIC { + let (header_slice, body_slice) = buffer.split_at(mem::size_of::()); + let header = unsafe { &*(header_slice.as_ptr() as *const ArtifactHeader) }; + + if header.version == CURRENT_CACHE_VERSION { + Ok((header, body_slice)) + } else { + Err(Error::InvalidatedCache) + } + } else { + Err(Error::InvalidFile(InvalidFileType::InvalidMagic)) + } + } else { + Err(Error::InvalidFile(InvalidFileType::InvalidSize)) + } + } + + pub fn read_from_slice_mut(buffer: &mut [u8]) -> Result<(&mut Self, &mut [u8]), Error> { + if buffer.len() >= mem::size_of::() { + if &buffer[..8] == &WASMER_CACHE_MAGIC { + let (header_slice, body_slice) = + buffer.split_at_mut(mem::size_of::()); + let header = unsafe { &mut *(header_slice.as_ptr() as *mut ArtifactHeader) }; if header.version == CURRENT_CACHE_VERSION { Ok((header, body_slice)) @@ -58,72 +122,53 @@ impl CacheHeader { } pub fn as_slice(&self) -> &[u8] { - let ptr = self as *const CacheHeader as *const u8; - unsafe { slice::from_raw_parts(ptr, mem::size_of::()) } + let ptr = self as *const ArtifactHeader as *const u8; + unsafe { slice::from_raw_parts(ptr, mem::size_of::()) } } } #[derive(Serialize, Deserialize)] -struct CacheInner { +struct ArtifactInner { info: Box, #[serde(with = "serde_bytes")] - backend_metadata: Vec, + backend_metadata: Box<[u8]>, compiled_code: Memory, } -pub struct Cache { - inner: CacheInner, - wasm_hash: Box<[u8; 32]>, +pub struct Artifact { + inner: ArtifactInner, } -impl Cache { - pub(crate) fn new( - wasm: &[u8], +impl Artifact { + pub(crate) fn from_parts( info: Box, - backend_metadata: Vec, + backend_metadata: Box<[u8]>, compiled_code: Memory, ) -> Self { - let wasm_hash = hash_data(wasm); - Self { - inner: CacheInner { + inner: ArtifactInner { info, backend_metadata, compiled_code, }, - wasm_hash: Box::new(wasm_hash), } } - pub fn open

(path: P) -> Result - where - P: AsRef, - { - let file = File::open(path).map_err(|e| Error::IoError(e))?; + pub fn deserialize(bytes: &[u8]) -> Result { + let (_, body_slice) = ArtifactHeader::read_from_slice(bytes)?; - let mmap = unsafe { Mmap::map(&file).map_err(|e| Error::IoError(e))? }; + let inner = serde_bench::deserialize(body_slice) + .map_err(|e| Error::DeserializeError(format!("{:#?}", e)))?; - let (header, body_slice) = CacheHeader::read_from_slice(&mmap[..])?; - - let inner = - deserialize(body_slice).map_err(|e| Error::DeserializeError(format!("{:#?}", e)))?; - - Ok(Cache { - inner, - wasm_hash: Box::new(header.wasm_hash), - }) + Ok(Artifact { inner }) } pub fn info(&self) -> &ModuleInfo { &self.inner.info } - pub fn wasm_hash(&self) -> &[u8; 32] { - &self.wasm_hash - } - #[doc(hidden)] - pub fn consume(self) -> (ModuleInfo, Vec, Memory) { + pub fn consume(self) -> (ModuleInfo, Box<[u8]>, Memory) { ( *self.inner.info, self.inner.backend_metadata, @@ -131,51 +176,34 @@ impl Cache { ) } - pub fn store

(&self, path: P) -> Result<(), Error> - where - P: AsRef, - { - let mut file = File::create(path).map_err(|e| Error::IoError(e))?; - - let mut buffer = Vec::new(); - - serialize(&mut buffer, &self.inner).map_err(|e| Error::SerializeError(e.to_string()))?; - - let data_len = buffer.len() as u64; - - file.seek(SeekFrom::Start(mem::size_of::() as u64)) - .map_err(|e| Error::IoError(e))?; - - file.write(buffer.as_slice()) - .map_err(|e| Error::IoError(e))?; - - file.seek(SeekFrom::Start(0)) - .map_err(|e| Error::Unknown(e.to_string()))?; - - let wasm_hash = { - let mut array = [0u8; 32]; - array.copy_from_slice(&*self.wasm_hash); - array - }; - - let cache_header = CacheHeader { - magic: [ - 'W' as u8, 'A' as u8, 'S' as u8, 'M' as u8, 'E' as u8, 'R' as u8, 0, 0, - ], + pub fn serialize(&self) -> Result, Error> { + let cache_header = ArtifactHeader { + magic: WASMER_CACHE_MAGIC, version: CURRENT_CACHE_VERSION, - data_len, - wasm_hash, + data_len: 0, }; - file.write(cache_header.as_slice()) - .map_err(|e| Error::IoError(e))?; + let mut buffer = cache_header.as_slice().to_vec(); - Ok(()) + serde_bench::serialize(&mut buffer, &self.inner) + .map_err(|e| Error::SerializeError(e.to_string()))?; + + let data_len = (buffer.len() - mem::size_of::()) as u64; + + let (header, _) = ArtifactHeader::read_from_slice_mut(&mut buffer)?; + header.data_len = data_len; + + Ok(buffer) } } -pub fn hash_data(data: &[u8]) -> [u8; 32] { - let mut array = [0u8; 32]; - array.copy_from_slice(Sha256::digest(data).as_slice()); - array +/// A generic cache for storing and loading compiled wasm modules. +/// +/// The `wasmer-runtime` supplies a naive `FileSystemCache` api. +pub trait Cache { + type LoadError: fmt::Debug; + type StoreError: fmt::Debug; + + fn load(&self, key: WasmHash) -> Result; + fn store(&mut self, key: WasmHash, module: Module) -> Result<(), Self::StoreError>; } diff --git a/lib/runtime-core/src/error.rs b/lib/runtime-core/src/error.rs index df35d077e..124b2d23a 100644 --- a/lib/runtime-core/src/error.rs +++ b/lib/runtime-core/src/error.rs @@ -1,8 +1,10 @@ use crate::types::{ - FuncSig, GlobalDescriptor, MemoryDescriptor, MemoryIndex, TableDescriptor, TableIndex, Type, + FuncSig, GlobalDescriptor, MemoryDescriptor, MemoryIndex, TableDescriptor, TableIndex, Type, Value, }; use std::sync::Arc; use wasmparser::BinaryReaderError; +use core::borrow::Borrow; +use std::any::Any; pub type Result = std::result::Result; pub type CompileResult = std::result::Result; @@ -36,6 +38,19 @@ impl PartialEq for CompileError { } } +impl std::fmt::Display for CompileError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + CompileError::InternalError { msg } => { + write!(f, "Internal compiler error: \"{}\"", msg) + } + CompileError::ValidationError { msg } => write!(f, "Validation error \"{}\"", msg), + } + } +} + +impl std::error::Error for CompileError {} + /// This is returned when the runtime is unable to /// correctly link the module with the provided imports. /// @@ -51,8 +66,8 @@ pub enum LinkError { IncorrectImportSignature { namespace: String, name: String, - expected: Arc, - found: Arc, + expected: FuncSig, + found: FuncSig, }, ImportNotFound { namespace: String, @@ -84,34 +99,42 @@ impl PartialEq for LinkError { } } +impl std::fmt::Display for LinkError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + LinkError::ImportNotFound {namespace, name} => write!(f, "Import not found, namespace: {}, name: {}", namespace, name), + LinkError::IncorrectGlobalDescriptor {namespace, name,expected,found} => { + write!(f, "Incorrect global descriptor, namespace: {}, name: {}, expected global descriptor: {:?}, found global descriptor: {:?}", namespace, name, expected, found) + }, + LinkError::IncorrectImportSignature{namespace, name,expected,found} => { + write!(f, "Incorrect import signature, namespace: {}, name: {}, expected signature: {}, found signature: {}", namespace, name, expected, found) + } + LinkError::IncorrectImportType{namespace, name,expected,found} => { + write!(f, "Incorrect import type, namespace: {}, name: {}, expected type: {}, found type: {}", namespace, name, expected, found) + } + LinkError::IncorrectMemoryDescriptor{namespace, name,expected,found} => { + write!(f, "Incorrect memory descriptor, namespace: {}, name: {}, expected memory descriptor: {:?}, found memory descriptor: {:?}", namespace, name, expected, found) + }, + LinkError::IncorrectTableDescriptor{namespace, name,expected,found} => { + write!(f, "Incorrect table descriptor, namespace: {}, name: {}, expected table descriptor: {:?}, found table descriptor: {:?}", namespace, name, expected, found) + }, + } + } +} + +impl std::error::Error for LinkError {} + /// This is the error type returned when calling /// a webassembly function. /// /// The main way to do this is `Instance.call`. /// /// Comparing two `RuntimeError`s always evaluates to false. -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum RuntimeError { - OutOfBoundsAccess { - memory: MemoryIndex, - addr: Option, - }, - TableOutOfBounds { - table: TableIndex, - }, - IndirectCallSignature { - table: TableIndex, - }, - IndirectCallToNull { - table: TableIndex, - }, - IllegalArithmeticOperation, - User { - msg: String, - }, - Unknown { - msg: String, - }, + Trap { msg: Box }, + Exception { data: Box<[Value]> }, + Panic { data: Box }, } impl PartialEq for RuntimeError { @@ -120,22 +143,31 @@ impl PartialEq for RuntimeError { } } +impl std::fmt::Display for RuntimeError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + RuntimeError::Trap { ref msg } => { + write!(f, "WebAssembly trap occured during runtime: {}", msg) + } + RuntimeError::Exception { ref data } => { + write!(f, "Uncaught WebAssembly exception: {:?}", data) + } + RuntimeError::Panic { data: _ } => write!(f, "User-defined \"panic\""), + } + } +} + +impl std::error::Error for RuntimeError {} + /// This error type is produced by resolving a wasm function /// given its name. /// /// Comparing two `ResolveError`s always evaluates to false. #[derive(Debug, Clone)] pub enum ResolveError { - Signature { - expected: Arc, - found: Vec, - }, - ExportNotFound { - name: String, - }, - ExportWrongType { - name: String, - }, + Signature { expected: FuncSig, found: Vec }, + ExportNotFound { name: String }, + ExportWrongType { name: String }, } impl PartialEq for ResolveError { @@ -144,6 +176,31 @@ impl PartialEq for ResolveError { } } +impl std::fmt::Display for ResolveError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + ResolveError::ExportNotFound { name } => write!(f, "Export not found: {}", name), + ResolveError::ExportWrongType { name } => write!(f, "Export wrong type: {}", name), + ResolveError::Signature { expected, found } => { + let found = found + .as_slice() + .iter() + .map(|p| p.to_string()) + .collect::>() + .join(", "); + let expected: &FuncSig = expected.borrow(); + write!( + f, + "Parameters of type [{}] did not match signature {}", + found, expected + ) + } + } + } +} + +impl std::error::Error for ResolveError {} + /// This error type is produced by calling a wasm function /// exported from a module. /// @@ -151,7 +208,7 @@ impl PartialEq for ResolveError { /// be the `CallError::Runtime(RuntimeError)` variant. /// /// Comparing two `CallError`s always evaluates to false. -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum CallError { Resolve(ResolveError), Runtime(RuntimeError), @@ -163,12 +220,24 @@ impl PartialEq for CallError { } } +impl std::fmt::Display for CallError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + CallError::Resolve(resolve_error) => write!(f, "Call error: {}", resolve_error), + CallError::Runtime(runtime_error) => write!(f, "Call error: {}", runtime_error), + } + } +} + +impl std::error::Error for CallError {} + /// This error type is produced when creating something, /// like a `Memory` or a `Table`. #[derive(Debug, Clone)] pub enum CreationError { UnableToCreateMemory, UnableToCreateTable, + InvalidDescriptor(String), } impl PartialEq for CreationError { @@ -177,12 +246,28 @@ impl PartialEq for CreationError { } } +impl std::fmt::Display for CreationError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + CreationError::UnableToCreateMemory => write!(f, "Unable to Create Memory"), + CreationError::UnableToCreateTable => write!(f, "Unable to Create Table"), + CreationError::InvalidDescriptor(msg) => write!( + f, + "Unable to create because the supplied descriptor is invalid: \"{}\"", + msg + ), + } + } +} + +impl std::error::Error for CreationError {} + /// The amalgamation of all errors that can occur /// during the compilation, instantiation, or execution /// of a webassembly module. /// /// Comparing two `Error`s always evaluates to false. -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum Error { CompileError(CompileError), LinkError(Vec), @@ -245,3 +330,130 @@ impl From for CallError { CallError::Resolve(resolve_err) } } + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Error::CompileError(err) => write!(f, "compile error: {}", err), + Error::LinkError(errs) => { + if errs.len() == 1 { + write!(f, "link error: {}", errs[0]) + } else { + write!(f, "{} link errors:", errs.len())?; + for (i, err) in errs.iter().enumerate() { + write!(f, " ({} of {}) {}", i + 1, errs.len(), err)?; + } + Ok(()) + } + } + Error::RuntimeError(err) => write!(f, "runtime error: {}", err), + Error::ResolveError(err) => write!(f, "resolve error: {}", err), + Error::CallError(err) => write!(f, "call error: {}", err), + Error::CreationError(err) => write!(f, "creation error: {}", err), + } + } +} + +impl std::error::Error for Error {} + +#[derive(Debug)] +pub enum GrowError { + MemoryGrowError, + TableGrowError, + ExceededMaxPages(PageError), + ExceededMaxPagesForMemory(usize, usize), + CouldNotProtectMemory(MemoryProtectionError), + CouldNotCreateMemory(MemoryCreationError), +} + +impl std::fmt::Display for GrowError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + GrowError::MemoryGrowError => write!(f, "Unable to grow memory"), + GrowError::TableGrowError => write!(f, "Unable to grow table"), + GrowError::ExceededMaxPages(e) => write!(f, "Grow Error: {}", e), + GrowError::ExceededMaxPagesForMemory(left, added) => write!(f, "Failed to add pages because would exceed maximum number of pages for the memory. Left: {}, Added: {}", left, added), + GrowError::CouldNotCreateMemory(e) => write!(f, "Grow Error: {}", e), + GrowError::CouldNotProtectMemory(e) => write!(f, "Grow Error: {}", e), + } + } +} + +impl std::error::Error for GrowError {} + +#[derive(Debug)] +pub enum PageError { + // left, right, added + ExceededMaxPages(usize, usize, usize), +} + +impl std::fmt::Display for PageError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + PageError::ExceededMaxPages(left, right, added) => write!(f, "Failed to add pages because would exceed maximum number of pages. Left: {}, Right: {}, Pages added: {}", left, right, added), + } + } +} +impl std::error::Error for PageError {} + +impl Into for PageError { + fn into(self) -> GrowError { + GrowError::ExceededMaxPages(self) + } +} + +#[derive(Debug)] +pub enum MemoryCreationError { + VirtualMemoryAllocationFailed(usize, String), + CouldNotCreateMemoryFromFile(std::io::Error), +} + +impl std::fmt::Display for MemoryCreationError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + MemoryCreationError::VirtualMemoryAllocationFailed(size, msg) => write!( + f, + "Allocation virtual memory with size {} failed. \nErrno message: {}", + size, msg + ), + MemoryCreationError::CouldNotCreateMemoryFromFile(e) => write!(f, "IO Error: {}", e), + } + } +} +impl std::error::Error for MemoryCreationError {} + +impl Into for MemoryCreationError { + fn into(self) -> GrowError { + GrowError::CouldNotCreateMemory(self) + } +} + +impl From for MemoryCreationError { + fn from(io_error: std::io::Error) -> Self { + MemoryCreationError::CouldNotCreateMemoryFromFile(io_error) + } +} + +#[derive(Debug)] +pub enum MemoryProtectionError { + ProtectionFailed(usize, usize, String), +} + +impl std::fmt::Display for MemoryProtectionError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + MemoryProtectionError::ProtectionFailed(start, size, msg) => write!( + f, + "Allocation virtual memory starting at {} with size {} failed. \nErrno message: {}", + start, size, msg + ), + } + } +} +impl std::error::Error for MemoryProtectionError {} + +impl Into for MemoryProtectionError { + fn into(self) -> GrowError { + GrowError::CouldNotProtectMemory(self) + } +} diff --git a/lib/runtime-core/src/import.rs b/lib/runtime-core/src/import.rs index cf833ec6e..59d9da555 100644 --- a/lib/runtime-core/src/import.rs +++ b/lib/runtime-core/src/import.rs @@ -1,5 +1,9 @@ use crate::export::Export; use hashbrown::{hash_map::Entry, HashMap}; +use std::{ + cell::{Ref, RefCell}, + rc::Rc, +}; pub trait LikeNamespace { fn get_export(&self, name: &str) -> Option; @@ -32,19 +36,19 @@ impl IsExport for Export { /// }, /// }; /// -/// fn foo(n: i32, _: &mut Ctx) -> i32 { +/// fn foo(_: &mut Ctx, n: i32) -> i32 { /// n /// } /// ``` pub struct ImportObject { - map: HashMap>, + map: Rc>>>, } impl ImportObject { /// Create a new `ImportObject`. pub fn new() -> Self { Self { - map: HashMap::new(), + map: Rc::new(RefCell::new(HashMap::new())), } } @@ -67,7 +71,9 @@ impl ImportObject { S: Into, N: LikeNamespace + 'static, { - match self.map.entry(name.into()) { + let mut map = self.map.borrow_mut(); + + match map.entry(name.into()) { Entry::Vacant(empty) => { empty.insert(Box::new(namespace)); None @@ -76,8 +82,20 @@ impl ImportObject { } } - pub fn get_namespace(&self, namespace: &str) -> Option<&(dyn LikeNamespace + 'static)> { - self.map.get(namespace).map(|namespace| &**namespace) + pub fn get_namespace(&self, namespace: &str) -> Option> { + let map_ref = self.map.borrow(); + + if map_ref.contains_key(namespace) { + Some(Ref::map(map_ref, |map| &*map[namespace])) + } else { + None + } + } + + pub fn clone_ref(&self) -> Self { + Self { + map: Rc::clone(&self.map), + } } } diff --git a/lib/runtime-core/src/instance.rs b/lib/runtime-core/src/instance.rs index 055bd5331..e069c381b 100644 --- a/lib/runtime-core/src/instance.rs +++ b/lib/runtime-core/src/instance.rs @@ -7,6 +7,7 @@ use crate::{ import::{ImportObject, LikeNamespace}, memory::Memory, module::{ExportIndex, Module, ModuleInner}, + sig_registry::SigRegistry, table::Table, typed_func::{Func, Safe, WasmTypeList}, types::{FuncIndex, FuncSig, GlobalIndex, LocalOrImport, MemoryIndex, TableIndex, Value}, @@ -38,6 +39,8 @@ impl Drop for InstanceInner { pub struct Instance { module: Arc, inner: Box, + #[allow(dead_code)] + import_object: ImportObject, } impl Instance { @@ -63,7 +66,11 @@ impl Instance { *inner.vmctx = vm::Ctx::new(&mut inner.backing, &mut inner.import_backing, &module) }; - let instance = Instance { module, inner }; + let instance = Instance { + module, + inner, + import_object: imports.clone_ref(), + }; if let Some(start_index) = instance.module.info.start_func { instance.call_with_index(start_index, &[])?; @@ -112,23 +119,24 @@ impl Instance { .func_assoc .get(*func_index) .expect("broken invariant, incorrect func index"); - let signature = &self.module.info.signatures[sig_index]; + let signature = + SigRegistry.lookup_signature_ref(&self.module.info.signatures[sig_index]); if signature.params() != Args::types() || signature.returns() != Rets::types() { Err(ResolveError::Signature { - expected: Arc::clone(&signature), + expected: (*signature).clone(), found: Args::types().to_vec(), })?; } - let ctx = match func_index.local_or_import(&*self.module) { + let ctx = match func_index.local_or_import(&self.module.info) { LocalOrImport::Local(_) => self.inner.vmctx, LocalOrImport::Import(imported_func_index) => { self.inner.import_backing.vm_functions[imported_func_index].vmctx } }; - let func_ptr = match func_index.local_or_import(&self.module) { + let func_ptr = match func_index.local_or_import(&self.module.info) { LocalOrImport::Local(local_func_index) => self .module .func_resolver @@ -183,7 +191,8 @@ impl Instance { .func_assoc .get(*func_index) .expect("broken invariant, incorrect func index"); - let signature = Arc::clone(&self.module.info.signatures[sig_index]); + let signature = + SigRegistry.lookup_signature_ref(&self.module.info.signatures[sig_index]); Ok(DynFunc { signature, @@ -288,7 +297,7 @@ impl Instance { })? } - let vmctx = match func_index.local_or_import(&self.module) { + let vmctx = match func_index.local_or_import(&self.module.info) { LocalOrImport::Local(_) => self.inner.vmctx, LocalOrImport::Import(imported_func_index) => { self.inner.import_backing.vm_functions[imported_func_index].vmctx @@ -355,7 +364,7 @@ impl InstanceInner { .get(func_index) .expect("broken invariant, incorrect func index"); - let (func_ptr, ctx) = match func_index.local_or_import(module) { + let (func_ptr, ctx) = match func_index.local_or_import(&module.info) { LocalOrImport::Local(local_func_index) => ( module .func_resolver @@ -374,17 +383,14 @@ impl InstanceInner { } }; - let signature = &module.info.signatures[sig_index]; + let signature = SigRegistry.lookup_signature_ref(&module.info.signatures[sig_index]); + // let signature = &module.info.signatures[sig_index]; - ( - unsafe { FuncPointer::new(func_ptr) }, - ctx, - Arc::clone(signature), - ) + (unsafe { FuncPointer::new(func_ptr) }, ctx, signature) } fn get_memory_from_index(&self, module: &ModuleInner, mem_index: MemoryIndex) -> Memory { - match mem_index.local_or_import(module) { + match mem_index.local_or_import(&module.info) { LocalOrImport::Local(local_mem_index) => self.backing.memories[local_mem_index].clone(), LocalOrImport::Import(imported_mem_index) => { self.import_backing.memories[imported_mem_index].clone() @@ -393,7 +399,7 @@ impl InstanceInner { } fn get_global_from_index(&self, module: &ModuleInner, global_index: GlobalIndex) -> Global { - match global_index.local_or_import(module) { + match global_index.local_or_import(&module.info) { LocalOrImport::Local(local_global_index) => { self.backing.globals[local_global_index].clone() } @@ -404,7 +410,7 @@ impl InstanceInner { } fn get_table_from_index(&self, module: &ModuleInner, table_index: TableIndex) -> Table { - match table_index.local_or_import(module) { + match table_index.local_or_import(&module.info) { LocalOrImport::Local(local_table_index) => { self.backing.tables[local_table_index].clone() } @@ -454,15 +460,15 @@ impl<'a> DynFunc<'a> { /// # Ok(()) /// # } /// ``` - pub fn call(&mut self, params: &[Value]) -> CallResult> { + pub fn call(&self, params: &[Value]) -> CallResult> { if !self.signature.check_param_value_types(params) { Err(ResolveError::Signature { - expected: self.signature.clone(), + expected: (*self.signature).clone(), found: params.iter().map(|val| val.ty()).collect(), })? } - let vmctx = match self.func_index.local_or_import(self.module) { + let vmctx = match self.func_index.local_or_import(&self.module.info) { LocalOrImport::Local(_) => self.instance_inner.vmctx, LocalOrImport::Import(imported_func_index) => { self.instance_inner.import_backing.vm_functions[imported_func_index].vmctx @@ -488,7 +494,7 @@ impl<'a> DynFunc<'a> { } pub fn raw(&self) -> *const vm::Func { - match self.func_index.local_or_import(self.module) { + match self.func_index.local_or_import(&self.module.info) { LocalOrImport::Local(local_func_index) => self .module .func_resolver diff --git a/lib/runtime-core/src/lib.rs b/lib/runtime-core/src/lib.rs index 18c3ae76c..663e17534 100644 --- a/lib/runtime-core/src/lib.rs +++ b/lib/runtime-core/src/lib.rs @@ -2,7 +2,6 @@ #[macro_use] extern crate field_offset; -#[cfg(feature = "cache")] #[macro_use] extern crate serde_derive; @@ -11,7 +10,7 @@ mod macros; #[doc(hidden)] pub mod backend; mod backing; -#[cfg(feature = "cache")] + pub mod cache; pub mod error; pub mod export; @@ -35,6 +34,8 @@ use self::error::CompileResult; #[doc(inline)] pub use self::error::Result; #[doc(inline)] +pub use self::import::IsExport; +#[doc(inline)] pub use self::instance::Instance; #[doc(inline)] pub use self::module::Module; @@ -42,8 +43,7 @@ pub use self::module::Module; pub use self::typed_func::Func; use std::sync::Arc; -#[cfg(feature = "cache")] -use self::cache::{Cache, Error as CacheError}; +use self::cache::{Artifact, Error as CacheError}; pub mod prelude { pub use crate::import::{ImportObject, Namespace}; @@ -88,21 +88,8 @@ pub fn validate(wasm: &[u8]) -> bool { } } -#[cfg(feature = "cache")] -pub fn compile_to_cache_with( - wasm: &[u8], - compiler: &dyn backend::Compiler, -) -> CompileResult { - let token = backend::Token::generate(); - let (info, backend_metadata, compiled_code) = - compiler.compile_to_backend_cache_data(wasm, token)?; - - Ok(Cache::new(wasm, info, backend_metadata, compiled_code)) -} - -#[cfg(feature = "cache")] pub unsafe fn load_cache_with( - cache: Cache, + cache: Artifact, compiler: &dyn backend::Compiler, ) -> std::result::Result { let token = backend::Token::generate(); diff --git a/lib/runtime-core/src/macros.rs b/lib/runtime-core/src/macros.rs index 620f65046..a7bdff1b0 100644 --- a/lib/runtime-core/src/macros.rs +++ b/lib/runtime-core/src/macros.rs @@ -38,7 +38,7 @@ macro_rules! func { /// }, /// }; /// -/// fn foo(n: i32, _: &mut Ctx) -> i32 { +/// fn foo(_: &mut Ctx, n: i32) -> i32 { /// n /// } /// ``` diff --git a/lib/runtime-core/src/memory/dynamic.rs b/lib/runtime-core/src/memory/dynamic.rs index bfe731a83..332a37acb 100644 --- a/lib/runtime-core/src/memory/dynamic.rs +++ b/lib/runtime-core/src/memory/dynamic.rs @@ -1,3 +1,4 @@ +use crate::error::GrowError; use crate::{ error::CreationError, sys, @@ -14,9 +15,9 @@ pub const DYNAMIC_GUARD_SIZE: usize = 4096; /// when first created. Over time, as it grows, it may reallocate to /// a different location and size. /// -/// Dynamic memories are signifigantly faster to create than static +/// Dynamic memories are significantly faster to create than static /// memories and use much less virtual memory, however, they require -/// the webassembly module to bounds-check memory accesses. +/// the WebAssembly module to bounds-check memory accesses. /// /// While, a dynamic memory could use a vector of some sort as its /// backing memory, we use mmap (or the platform-equivalent) to allow @@ -65,26 +66,29 @@ impl DynamicMemory { self.current } - pub fn grow(&mut self, delta: Pages, local: &mut vm::LocalMemory) -> Option { + pub fn grow(&mut self, delta: Pages, local: &mut vm::LocalMemory) -> Result { if delta == Pages(0) { - return Some(self.current); + return Ok(self.current); } - let new_pages = self.current.checked_add(delta)?; + let new_pages = self.current.checked_add(delta).map_err(|e| e.into())?; if let Some(max) = self.max { if new_pages > max { - return None; + return Err(GrowError::ExceededMaxPagesForMemory( + new_pages.0 as usize, + max.0 as usize, + )); } } - let mut new_memory = - sys::Memory::with_size(new_pages.bytes().0 + DYNAMIC_GUARD_SIZE).ok()?; + let mut new_memory = sys::Memory::with_size(new_pages.bytes().0 + DYNAMIC_GUARD_SIZE) + .map_err(|e| e.into())?; unsafe { new_memory .protect(0..new_pages.bytes().0, sys::Protect::ReadWrite) - .ok()?; + .map_err(|e| e.into())?; new_memory.as_slice_mut()[..self.current.bytes().0] .copy_from_slice(&self.memory.as_slice()[..self.current.bytes().0]); @@ -97,7 +101,7 @@ impl DynamicMemory { let old_pages = self.current; self.current = new_pages; - Some(old_pages) + Ok(old_pages) } pub fn as_slice(&self) -> &[u8] { diff --git a/lib/runtime-core/src/memory/mod.rs b/lib/runtime-core/src/memory/mod.rs index 30fab393e..4a9dccba7 100644 --- a/lib/runtime-core/src/memory/mod.rs +++ b/lib/runtime-core/src/memory/mod.rs @@ -1,5 +1,5 @@ use crate::{ - error::CreationError, + error::{CreationError, GrowError}, export::Export, import::IsExport, memory::dynamic::DYNAMIC_GUARD_SIZE, @@ -63,6 +63,15 @@ impl Memory { /// # } /// ``` pub fn new(desc: MemoryDescriptor) -> Result { + if let Some(max) = desc.maximum { + if max < desc.minimum { + return Err(CreationError::InvalidDescriptor( + "Max number of memory pages is less than the minimum number of pages" + .to_string(), + )); + } + } + let variant = if !desc.shared { MemoryVariant::Unshared(UnsharedMemory::new(desc)?) } else { @@ -80,8 +89,8 @@ impl Memory { self.desc } - /// Grow this memory by the specfied number of pages. - pub fn grow(&self, delta: Pages) -> Option { + /// Grow this memory by the specified number of pages. + pub fn grow(&self, delta: Pages) -> Result { match &self.variant { MemoryVariant::Unshared(unshared_mem) => unshared_mem.grow(delta), MemoryVariant::Shared(shared_mem) => shared_mem.grow(delta), @@ -235,7 +244,7 @@ impl UnsharedMemory { }) } - pub fn grow(&self, delta: Pages) -> Option { + pub fn grow(&self, delta: Pages) -> Result { let mut storage = self.internal.storage.borrow_mut(); let mut local = self.internal.local.get(); @@ -283,7 +292,7 @@ impl SharedMemory { Ok(Self { desc }) } - pub fn grow(&self, _delta: Pages) -> Option { + pub fn grow(&self, _delta: Pages) -> Result { unimplemented!() } @@ -297,3 +306,21 @@ impl Clone for SharedMemory { unimplemented!() } } + +#[cfg(test)] +mod memory_tests { + + use super::{Memory, MemoryDescriptor, Pages}; + + #[test] + fn test_initial_memory_size() { + let unshared_memory = Memory::new(MemoryDescriptor { + minimum: Pages(10), + maximum: Some(Pages(20)), + shared: false, + }) + .unwrap(); + assert_eq!(unshared_memory.size(), Pages(10)); + } + +} diff --git a/lib/runtime-core/src/memory/static_/unshared.rs b/lib/runtime-core/src/memory/static_/unshared.rs index 420fdc716..b9e66de0a 100644 --- a/lib/runtime-core/src/memory/static_/unshared.rs +++ b/lib/runtime-core/src/memory/static_/unshared.rs @@ -1,3 +1,4 @@ +use crate::error::GrowError; use crate::{ error::CreationError, memory::static_::{SAFE_STATIC_GUARD_SIZE, SAFE_STATIC_HEAP_SIZE}, @@ -61,27 +62,30 @@ impl StaticMemory { self.current } - pub fn grow(&mut self, delta: Pages, local: &mut vm::LocalMemory) -> Option { + pub fn grow(&mut self, delta: Pages, local: &mut vm::LocalMemory) -> Result { if delta == Pages(0) { - return Some(self.current); + return Ok(self.current); } - let new_pages = self.current.checked_add(delta)?; + let new_pages = self.current.checked_add(delta).map_err(|e| e.into())?; if let Some(max) = self.max { if new_pages > max { - return None; + return Err(GrowError::ExceededMaxPagesForMemory( + new_pages.0 as usize, + max.0 as usize, + )); } } - unsafe { + let _ = unsafe { self.memory .protect( self.current.bytes().0..new_pages.bytes().0, sys::Protect::ReadWrite, ) - .ok()?; - } + .map_err(|e| e.into()) + }?; local.bound = new_pages.bytes().0; @@ -89,7 +93,7 @@ impl StaticMemory { self.current = new_pages; - Some(old_pages) + Ok(old_pages) } pub fn as_slice(&self) -> &[u8] { diff --git a/lib/runtime-core/src/module.rs b/lib/runtime-core/src/module.rs index 0fdb24018..212c197c3 100644 --- a/lib/runtime-core/src/module.rs +++ b/lib/runtime-core/src/module.rs @@ -1,6 +1,7 @@ use crate::{ backend::{Backend, FuncResolver, ProtectedCaller}, - error::Result, + cache::{Artifact, Error as CacheError}, + error, import::ImportObject, structures::{Map, TypedIndex}, typed_func::EARLY_TRAPPER, @@ -12,6 +13,8 @@ use crate::{ }, Instance, }; + +use crate::backend::CacheGen; use hashbrown::HashMap; use indexmap::IndexMap; use std::sync::Arc; @@ -22,10 +25,12 @@ pub struct ModuleInner { pub func_resolver: Box, pub protected_caller: Box, + pub cache_gen: Box, + pub info: ModuleInfo, } -#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] +#[derive(Clone, Serialize, Deserialize)] pub struct ModuleInfo { // This are strictly local and the typsystem ensures that. pub memories: Map, @@ -46,7 +51,7 @@ pub struct ModuleInfo { pub start_func: Option, pub func_assoc: Map, - pub signatures: Map>, + pub signatures: Map, pub backend: Backend, pub namespace_table: StringTable, @@ -60,7 +65,9 @@ pub struct ModuleInfo { /// /// [`compile`]: fn.compile.html /// [`compile_with`]: fn.compile_with.html -pub struct Module(#[doc(hidden)] pub Arc); +pub struct Module { + inner: Arc, +} impl Module { pub(crate) fn new(inner: Arc) -> Self { @@ -68,7 +75,7 @@ impl Module { EARLY_TRAPPER .with(|ucell| *ucell.get() = Some(inner.protected_caller.get_early_trapper())); } - Module(inner) + Module { inner } } /// Instantiate a WebAssembly module with the provided [`ImportObject`]. @@ -93,23 +100,38 @@ impl Module { /// # Ok(()) /// # } /// ``` - pub fn instantiate(&self, import_object: &ImportObject) -> Result { - Instance::new(Arc::clone(&self.0), import_object) + pub fn instantiate(&self, import_object: &ImportObject) -> error::Result { + Instance::new(Arc::clone(&self.inner), import_object) + } + + pub fn cache(&self) -> Result { + let (info, backend_metadata, code) = self.inner.cache_gen.generate_cache(&self.inner)?; + Ok(Artifact::from_parts(info, backend_metadata, code)) + } + + pub fn info(&self) -> &ModuleInfo { + &self.inner.info + } +} + +impl Clone for Module { + fn clone(&self) -> Self { + Self { + inner: Arc::clone(&self.inner), + } } } impl ModuleInner {} #[doc(hidden)] -#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] -#[derive(Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct ImportName { pub namespace_index: NamespaceIndex, pub name_index: NameIndex, } -#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] pub enum ExportIndex { Func(FuncIndex), Memory(MemoryIndex), @@ -118,8 +140,7 @@ pub enum ExportIndex { } /// A data initializer for linear memory. -#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] -#[derive(Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct DataInitializer { /// The index of the memory to initialize. pub memory_index: MemoryIndex, @@ -131,8 +152,7 @@ pub struct DataInitializer { } /// A WebAssembly table initializer. -#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] -#[derive(Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct TableInitializer { /// The index of a table to initialize. pub table_index: TableIndex, @@ -193,8 +213,7 @@ impl StringTableBuilder { } } -#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] -#[derive(Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct StringTable { table: Map, buffer: String, @@ -217,8 +236,7 @@ impl StringTable { } } -#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct NamespaceIndex(u32); impl TypedIndex for NamespaceIndex { @@ -233,8 +251,7 @@ impl TypedIndex for NamespaceIndex { } } -#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct NameIndex(u32); impl TypedIndex for NameIndex { diff --git a/lib/runtime-core/src/sig_registry.rs b/lib/runtime-core/src/sig_registry.rs index 77c9a0d3c..1f6d87b4f 100644 --- a/lib/runtime-core/src/sig_registry.rs +++ b/lib/runtime-core/src/sig_registry.rs @@ -49,4 +49,20 @@ impl SigRegistry { let global = (*GLOBAL_SIG_REGISTRY).read(); Arc::clone(&global.sig_assoc[sig_index]) } + + pub fn lookup_signature_ref(&self, func_sig: &FuncSig) -> Arc { + let mut global = (*GLOBAL_SIG_REGISTRY).write(); + let global = &mut *global; + + let func_table = &mut global.func_table; + let sig_assoc = &mut global.sig_assoc; + + if func_table.contains_key(func_sig) { + Arc::clone(&sig_assoc[func_table[func_sig]]) + } else { + let arc = Arc::new(func_sig.clone()); + func_table.insert(Arc::clone(&arc), sig_assoc.push(Arc::clone(&arc))); + arc + } + } } diff --git a/lib/runtime-core/src/structures/map.rs b/lib/runtime-core/src/structures/map.rs index f67000b6d..add5b0257 100644 --- a/lib/runtime-core/src/structures/map.rs +++ b/lib/runtime-core/src/structures/map.rs @@ -8,8 +8,7 @@ use std::{ }; /// Dense item map -#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] -#[derive(Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct Map where K: TypedIndex, @@ -57,6 +56,10 @@ where pub fn into_boxed_map(self) -> BoxedMap { BoxedMap::new(self.elems.into_boxed_slice()) } + + pub fn into_vec(self) -> Vec { + self.elems + } } impl Map diff --git a/lib/runtime-core/src/sys/mod.rs b/lib/runtime-core/src/sys/mod.rs index 4079a506d..0bfc134c2 100644 --- a/lib/runtime-core/src/sys/mod.rs +++ b/lib/runtime-core/src/sys/mod.rs @@ -10,18 +10,16 @@ pub use self::unix::*; #[cfg(windows)] pub use self::windows::*; -#[cfg(feature = "cache")] use serde::{ de::{self, SeqAccess, Visitor}, ser::SerializeStruct, Deserialize, Deserializer, Serialize, Serializer, }; -#[cfg(feature = "cache")] + use serde_bytes::Bytes; -#[cfg(feature = "cache")] + use std::fmt; -#[cfg(feature = "cache")] impl Serialize for Memory { fn serialize(&self, serializer: S) -> Result where @@ -36,7 +34,6 @@ impl Serialize for Memory { } } -#[cfg(feature = "cache")] impl<'de> Deserialize<'de> for Memory { fn deserialize(deserializer: D) -> Result where diff --git a/lib/runtime-core/src/sys/unix/memory.rs b/lib/runtime-core/src/sys/unix/memory.rs index d1579d958..9ffb663eb 100644 --- a/lib/runtime-core/src/sys/unix/memory.rs +++ b/lib/runtime-core/src/sys/unix/memory.rs @@ -1,3 +1,5 @@ +use crate::error::MemoryCreationError; +use crate::error::MemoryProtectionError; use errno; use nix::libc; use page_size; @@ -16,13 +18,13 @@ pub struct Memory { } impl Memory { - pub fn from_file_path

(path: P, protection: Protect) -> Result + pub fn from_file_path

(path: P, protection: Protect) -> Result where P: AsRef, { - let file = File::open(path).map_err(|e| e.to_string())?; + let file = File::open(path)?; - let file_len = file.metadata().map_err(|e| e.to_string())?.len(); + let file_len = file.metadata()?.len(); let raw_fd = RawFd::from_file(file); @@ -38,7 +40,10 @@ impl Memory { }; if ptr == -1 as _ { - Err(errno::errno().to_string()) + Err(MemoryCreationError::VirtualMemoryAllocationFailed( + file_len as usize, + errno::errno().to_string(), + )) } else { Ok(Self { ptr: ptr as *mut u8, @@ -84,7 +89,7 @@ impl Memory { } } - pub fn with_size(size: usize) -> Result { + pub fn with_size(size: usize) -> Result { if size == 0 { return Ok(Self { ptr: ptr::null_mut(), @@ -108,7 +113,10 @@ impl Memory { }; if ptr == -1 as _ { - Err(errno::errno().to_string()) + Err(MemoryCreationError::VirtualMemoryAllocationFailed( + size, + errno::errno().to_string(), + )) } else { Ok(Self { ptr: ptr as *mut u8, @@ -123,7 +131,7 @@ impl Memory { &mut self, range: impl RangeBounds, protection: Protect, - ) -> Result<(), String> { + ) -> Result<(), MemoryProtectionError> { let protect = protection.to_protect_const(); let range_start = match range.start_bound() { @@ -147,7 +155,11 @@ impl Memory { let success = libc::mprotect(start as _, size, protect as i32); if success == -1 { - Err(errno::errno().to_string()) + Err(MemoryProtectionError::ProtectionFailed( + start as usize, + size, + errno::errno().to_string(), + )) } else { self.protection = protection; Ok(()) @@ -205,14 +217,35 @@ impl Drop for Memory { } } -#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +impl Clone for Memory { + fn clone(&self) -> Self { + let temp_protection = if self.protection.is_writable() { + self.protection + } else { + Protect::ReadWrite + }; + + let mut new = Memory::with_size_protect(self.size, temp_protection).unwrap(); + unsafe { + new.as_slice_mut().copy_from_slice(self.as_slice()); + + if temp_protection != self.protection { + new.protect(.., self.protection).unwrap(); + } + } + + new + } +} + +#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)] #[allow(dead_code)] pub enum Protect { None, Read, ReadWrite, ReadExec, + ReadWriteExec, } impl Protect { @@ -222,6 +255,7 @@ impl Protect { Protect::Read => 1, Protect::ReadWrite => 1 | 2, Protect::ReadExec => 1 | 4, + Protect::ReadWriteExec => 1 | 2 | 4, } } diff --git a/lib/runtime-core/src/sys/windows/memory.rs b/lib/runtime-core/src/sys/windows/memory.rs index baf4d27a5..d47388170 100644 --- a/lib/runtime-core/src/sys/windows/memory.rs +++ b/lib/runtime-core/src/sys/windows/memory.rs @@ -1,3 +1,5 @@ +use crate::error::MemoryCreationError; +use crate::error::MemoryProtectionError; use page_size; use std::ops::{Bound, RangeBounds}; use std::{ptr, slice}; @@ -44,7 +46,7 @@ impl Memory { } } - pub fn with_size(size: usize) -> Result { + pub fn with_size(size: usize) -> Result { if size == 0 { return Ok(Self { ptr: ptr::null_mut(), @@ -58,7 +60,10 @@ impl Memory { let ptr = unsafe { VirtualAlloc(ptr::null_mut(), size, MEM_RESERVE, PAGE_NOACCESS) }; if ptr.is_null() { - Err("unable to allocate memory".to_string()) + Err(MemoryCreationError::VirtualMemoryAllocationFailed( + size, + "unable to allocate memory".to_string(), + )) } else { Ok(Self { ptr: ptr as *mut u8, @@ -72,7 +77,7 @@ impl Memory { &mut self, range: impl RangeBounds, protect: Protect, - ) -> Result<(), String> { + ) -> Result<(), MemoryProtectionError> { let protect_const = protect.to_protect_const(); let range_start = match range.start_bound() { @@ -98,7 +103,11 @@ impl Memory { let ptr = VirtualAlloc(start as _, size, MEM_COMMIT, protect_const); if ptr.is_null() { - Err("unable to protect memory".to_string()) + Err(MemoryProtectionError::ProtectionFailed( + start as usize, + size, + "unable to protect memory".to_string(), + )) } else { self.protection = protect; Ok(()) @@ -150,13 +159,34 @@ impl Drop for Memory { fn drop(&mut self) { if !self.ptr.is_null() { let success = unsafe { VirtualFree(self.ptr as _, self.size, MEM_DECOMMIT) }; - assert_eq!(success, 0, "failed to unmap memory: {}", errno::errno()); + // If the function succeeds, the return value is nonzero. + assert_eq!(success, 1, "failed to unmap memory: {}", errno::errno()); } } } -#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +impl Clone for Memory { + fn clone(&self) -> Self { + let temp_protection = if self.protection.is_writable() { + self.protection + } else { + Protect::ReadWrite + }; + + let mut new = Memory::with_size_protect(self.size, temp_protection).unwrap(); + unsafe { + new.as_slice_mut().copy_from_slice(self.as_slice()); + + if temp_protection != self.protection { + new.protect(.., self.protection).unwrap(); + } + } + + new + } +} + +#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)] #[allow(dead_code)] pub enum Protect { None, diff --git a/lib/runtime-core/src/table/anyfunc.rs b/lib/runtime-core/src/table/anyfunc.rs index 8e48a00d1..8312e2506 100644 --- a/lib/runtime-core/src/table/anyfunc.rs +++ b/lib/runtime-core/src/table/anyfunc.rs @@ -53,10 +53,7 @@ impl AnyfuncTable { desc: TableDescriptor, local: &mut vm::LocalTable, ) -> Result, CreationError> { - let initial_table_backing_len = match desc.maximum { - Some(max) => max, - None => desc.minimum, - } as usize; + let initial_table_backing_len = desc.minimum as usize; let mut storage = Box::new(AnyfuncTable { backing: vec![vm::Anyfunc::null(); initial_table_backing_len], diff --git a/lib/runtime-core/src/table/mod.rs b/lib/runtime-core/src/table/mod.rs index db4b943c8..1c97b89f3 100644 --- a/lib/runtime-core/src/table/mod.rs +++ b/lib/runtime-core/src/table/mod.rs @@ -11,6 +11,7 @@ mod anyfunc; pub use self::anyfunc::Anyfunc; use self::anyfunc::AnyfuncTable; +use crate::error::GrowError; pub enum Element<'a> { Anyfunc(Anyfunc<'a>), @@ -50,6 +51,14 @@ impl Table { /// # } /// ``` pub fn new(desc: TableDescriptor) -> Result { + if let Some(max) = desc.maximum { + if max < desc.minimum { + return Err(CreationError::InvalidDescriptor( + "Max table size is less than the minimum size".to_string(), + )); + } + } + let mut local = vm::LocalTable { base: ptr::null_mut(), count: 0, @@ -100,15 +109,15 @@ impl Table { } /// Grow this table by `delta`. - pub fn grow(&self, delta: u32) -> Option { + pub fn grow(&self, delta: u32) -> Result { if delta == 0 { - return Some(self.size()); + return Ok(self.size()); } match &mut *self.storage.borrow_mut() { - (TableStorage::Anyfunc(ref mut anyfunc_table), ref mut local) => { - anyfunc_table.grow(delta, local) - } + (TableStorage::Anyfunc(ref mut anyfunc_table), ref mut local) => anyfunc_table + .grow(delta, local) + .ok_or(GrowError::TableGrowError), } } @@ -140,3 +149,21 @@ impl fmt::Debug for Table { .finish() } } + +#[cfg(test)] +mod table_tests { + + use super::{ElementType, Table, TableDescriptor}; + + #[test] + fn test_initial_table_size() { + let table = Table::new(TableDescriptor { + element: ElementType::Anyfunc, + minimum: 10, + maximum: Some(20), + }) + .unwrap(); + assert_eq!(table.size(), 10); + } + +} diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index 9cbcdc911..4191f347e 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -6,7 +6,7 @@ use crate::{ types::{FuncSig, Type, WasmExternType}, vm::Ctx, }; -use std::{cell::UnsafeCell, fmt, marker::PhantomData, mem, panic, ptr, sync::Arc}; +use std::{any::Any, cell::UnsafeCell, marker::PhantomData, mem, panic, ptr, sync::Arc}; thread_local! { pub static EARLY_TRAPPER: UnsafeCell>> = UnsafeCell::new(None); @@ -40,14 +40,14 @@ pub trait TrapEarly where Rets: WasmTypeList, { - fn report(self) -> Result; + fn report(self) -> Result>; } impl TrapEarly for Rets where Rets: WasmTypeList, { - fn report(self) -> Result { + fn report(self) -> Result> { Ok(self) } } @@ -55,10 +55,10 @@ where impl TrapEarly for Result where Rets: WasmTypeList, - E: fmt::Debug, + E: Any, { - fn report(self) -> Result { - self.map_err(|err| format!("Error: {:?}", err)) + fn report(self) -> Result> { + self.map_err(|err| Box::new(err) as Box) } } @@ -138,9 +138,9 @@ impl WasmTypeList for (A,) { } #[allow(non_snake_case)] unsafe fn call(self, f: *const (), ctx: *mut Ctx) -> Rets { - let f: extern "C" fn(A, *mut Ctx) -> Rets = mem::transmute(f); + let f: extern "C" fn(*mut Ctx, A) -> Rets = mem::transmute(f); let (a,) = self; - f(a, ctx) + f(ctx, a) } } @@ -154,8 +154,8 @@ where } macro_rules! impl_traits { - ( $struct_name:ident, $( $x:ident ),* ) => { - #[repr(C)] + ( [$repr:ident] $struct_name:ident, $( $x:ident ),* ) => { + #[repr($repr)] pub struct $struct_name <$( $x ),*> ( $( $x ),* ); impl< $( $x: WasmExternType, )* > WasmTypeList for ( $( $x ),* ) { @@ -175,41 +175,33 @@ macro_rules! impl_traits { } #[allow(non_snake_case)] unsafe fn call(self, f: *const (), ctx: *mut Ctx) -> Rets { - let f: extern fn( $( $x, )* *mut Ctx) -> Rets::CStruct = mem::transmute(f); + let f: extern fn(*mut Ctx $( ,$x )*) -> Rets::CStruct = mem::transmute(f); #[allow(unused_parens)] let ( $( $x ),* ) = self; - let c_struct = f( $( $x, )* ctx); + let c_struct = f(ctx $( ,$x )*); Rets::from_c_struct(c_struct) } } - impl< $( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly, FN: Fn( $( $x, )* &mut Ctx) -> Trap> ExternalFunction<($( $x ),*), Rets> for FN { + impl< $( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly, FN: Fn( &mut Ctx $( ,$x )* ) -> Trap> ExternalFunction<($( $x ),*), Rets> for FN { #[allow(non_snake_case)] fn to_raw(&self) -> *const () { assert_eq!(mem::size_of::(), 0, "you cannot use a closure that captures state for `Func`."); - extern fn wrap<$( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly, FN: Fn( $( $x, )* &mut Ctx) -> Trap>( $( $x: $x, )* ctx: &mut Ctx) -> Rets::CStruct { + extern fn wrap<$( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly, FN: Fn( &mut Ctx $( ,$x )* ) -> Trap>( ctx: &mut Ctx $( ,$x: $x )* ) -> Rets::CStruct { let f: FN = unsafe { mem::transmute_copy(&()) }; - let msg = match panic::catch_unwind(panic::AssertUnwindSafe(|| { - f( $( $x, )* ctx).report() + let err = match panic::catch_unwind(panic::AssertUnwindSafe(|| { + f( ctx $( ,$x )* ).report() })) { Ok(Ok(returns)) => return returns.into_c_struct(), Ok(Err(err)) => err, - Err(err) => { - if let Some(s) = err.downcast_ref::<&str>() { - s.to_string() - } else if let Some(s) = err.downcast_ref::() { - s.clone() - } else { - "a panic occurred, but no additional information is available".to_string() - } - }, + Err(err) => err, }; unsafe { if let Some(early_trapper) = &*EARLY_TRAPPER.with(|ucell| ucell.get()) { - early_trapper.do_early_trap(msg) + early_trapper.do_early_trap(err) } else { eprintln!("panic handling not setup"); std::process::exit(1) @@ -234,18 +226,19 @@ macro_rules! impl_traits { }; } -impl_traits!(S0,); -impl_traits!(S1, A); -impl_traits!(S2, A, B); -impl_traits!(S3, A, B, C); -impl_traits!(S4, A, B, C, D); -impl_traits!(S5, A, B, C, D, E); -impl_traits!(S6, A, B, C, D, E, F); -impl_traits!(S7, A, B, C, D, E, F, G); -impl_traits!(S8, A, B, C, D, E, F, G, H); -impl_traits!(S9, A, B, C, D, E, F, G, H, I); -impl_traits!(S10, A, B, C, D, E, F, G, H, I, J); -impl_traits!(S11, A, B, C, D, E, F, G, H, I, J, K); +impl_traits!([C] S0,); +impl_traits!([transparent] S1, A); +impl_traits!([C] S2, A, B); +impl_traits!([C] S3, A, B, C); +impl_traits!([C] S4, A, B, C, D); +impl_traits!([C] S5, A, B, C, D, E); +impl_traits!([C] S6, A, B, C, D, E, F); +impl_traits!([C] S7, A, B, C, D, E, F, G); +impl_traits!([C] S8, A, B, C, D, E, F, G, H); +impl_traits!([C] S9, A, B, C, D, E, F, G, H, I); +impl_traits!([C] S10, A, B, C, D, E, F, G, H, I, J); +impl_traits!([C] S11, A, B, C, D, E, F, G, H, I, J, K); +impl_traits!([C] S12, A, B, C, D, E, F, G, H, I, J, K, L); impl<'a, Args, Rets, Safety> IsExport for Func<'a, Args, Rets, Safety> where @@ -271,7 +264,7 @@ mod tests { use super::*; #[test] fn test_call() { - fn foo(a: i32, b: i32, _ctx: &mut Ctx) -> (i32, i32) { + fn foo(_ctx: &mut Ctx, a: i32, b: i32) -> (i32, i32) { (a, b) } @@ -282,7 +275,7 @@ mod tests { fn test_imports() { use crate::{func, imports}; - fn foo(a: i32, _ctx: &mut Ctx) -> i32 { + fn foo(_ctx: &mut Ctx, a: i32) -> i32 { a } diff --git a/lib/runtime-core/src/types.rs b/lib/runtime-core/src/types.rs index d30b1ecb2..e1967ba68 100644 --- a/lib/runtime-core/src/types.rs +++ b/lib/runtime-core/src/types.rs @@ -1,10 +1,9 @@ use crate::error::{CompileError, CompileResult}; -use crate::{memory::MemoryType, module::ModuleInner, structures::TypedIndex, units::Pages}; +use crate::{memory::MemoryType, module::ModuleInfo, module::ModuleInner, structures::TypedIndex, units::Pages}; use std::{borrow::Cow, mem}; /// Represents a WebAssembly type. -#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Type { /// The `i32` type. I32, @@ -30,12 +29,18 @@ impl Type { } } } + +impl std::fmt::Display for Type { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + /// Represents a WebAssembly value. /// /// As the number of types in WebAssembly expand, /// this structure will expand as well. -#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, PartialEq)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub enum Value { /// The `i32` type. I32(i32), @@ -180,15 +185,13 @@ impl ValueType for f64 { } } -#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] pub enum ElementType { /// Any wasm function. Anyfunc, } -#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, Copy)] +#[derive(Serialize, Deserialize, Debug, Clone, Copy)] pub struct TableDescriptor { /// Type of data stored in this table. pub element: ElementType, @@ -212,8 +215,7 @@ impl TableDescriptor { /// A const value initializer. /// Over time, this will be able to represent more and more /// complex expressions. -#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, PartialEq)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub enum Initializer { /// Corresponds to a `const.*` instruction. Const(Value), @@ -221,24 +223,21 @@ pub enum Initializer { GetGlobal(ImportedGlobalIndex), } -#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] pub struct GlobalDescriptor { pub mutable: bool, pub ty: Type, } /// A wasm global. -#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] -#[derive(Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct GlobalInit { pub desc: GlobalDescriptor, pub init: Initializer, } /// A wasm memory. -#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] pub struct MemoryDescriptor { /// The minimum number of allowed pages. pub minimum: Pages, @@ -270,8 +269,7 @@ impl MemoryDescriptor { /// The signature of a function that is either implemented /// in a wasm module or exposed to wasm by the host. -#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)] pub struct FuncSig { params: Cow<'static, [Type]>, returns: Cow<'static, [Type]>, @@ -307,6 +305,24 @@ impl FuncSig { } } +impl std::fmt::Display for FuncSig { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + let params = self + .params + .iter() + .map(|p| p.to_string()) + .collect::>() + .join(", "); + let returns = self + .returns + .iter() + .map(|p| p.to_string()) + .collect::>() + .join(", "); + write!(f, "[{}] -> [{}]", params, returns) + } +} + pub trait LocalImport { type Local: TypedIndex; type Import: TypedIndex; @@ -315,7 +331,7 @@ pub trait LocalImport { #[rustfmt::skip] macro_rules! define_map_index { ($ty:ident) => { - #[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] + #[derive(Serialize, Deserialize)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct $ty (u32); impl TypedIndex for $ty { @@ -355,23 +371,23 @@ define_map_index![ macro_rules! define_local_or_import { ($ty:ident, $local_ty:ident, $imported_ty:ident, $imports:ident) => { impl $ty { - pub fn local_or_import(self, module: &ModuleInner) -> LocalOrImport<$ty> { - if self.index() < module.info.$imports.len() { + pub fn local_or_import(self, info: &ModuleInfo) -> LocalOrImport<$ty> { + if self.index() < info.$imports.len() { LocalOrImport::Import(::Import::new(self.index())) } else { - LocalOrImport::Local(::Local::new(self.index() - module.info.$imports.len())) + LocalOrImport::Local(::Local::new(self.index() - info.$imports.len())) } } } impl $local_ty { - pub fn convert_up(self, module: &ModuleInner) -> $ty { - $ty ((self.index() + module.info.$imports.len()) as u32) + pub fn convert_up(self, info: &ModuleInfo) -> $ty { + $ty ((self.index() + info.$imports.len()) as u32) } } impl $imported_ty { - pub fn convert_up(self, _module: &ModuleInner) -> $ty { + pub fn convert_up(self, _info: &ModuleInfo) -> $ty { $ty (self.index() as u32) } } @@ -391,8 +407,7 @@ define_local_or_import![ (GlobalIndex | (LocalGlobalIndex, ImportedGlobalIndex): imported_globals), ]; -#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct SigIndex(u32); impl TypedIndex for SigIndex { #[doc(hidden)] diff --git a/lib/runtime-core/src/units.rs b/lib/runtime-core/src/units.rs index 8fa54474b..56d501438 100644 --- a/lib/runtime-core/src/units.rs +++ b/lib/runtime-core/src/units.rs @@ -1,3 +1,4 @@ +use crate::error::PageError; use std::{ fmt, ops::{Add, Sub}, @@ -7,17 +8,20 @@ const WASM_PAGE_SIZE: usize = 65_536; const WASM_MAX_PAGES: usize = 65_536; /// Units of WebAssembly pages (as specified to be 65,536 bytes). -#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct Pages(pub u32); impl Pages { - pub fn checked_add(self, rhs: Pages) -> Option { + pub fn checked_add(self, rhs: Pages) -> Result { let added = (self.0 as usize) + (rhs.0 as usize); if added <= WASM_MAX_PAGES { - Some(Pages(added as u32)) + Ok(Pages(added as u32)) } else { - None + Err(PageError::ExceededMaxPages( + self.0 as usize, + rhs.0 as usize, + added, + )) } } @@ -33,8 +37,7 @@ impl fmt::Debug for Pages { } /// Units of WebAssembly memory in terms of 8-bit bytes. -#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))] -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct Bytes(pub usize); impl fmt::Debug for Bytes { diff --git a/lib/runtime-core/src/vm.rs b/lib/runtime-core/src/vm.rs index 94090c675..d26b52345 100644 --- a/lib/runtime-core/src/vm.rs +++ b/lib/runtime-core/src/vm.rs @@ -34,6 +34,14 @@ pub struct Ctx { /// A pointer to an array of imported functions, indexed by `FuncIndex`. pub imported_funcs: *mut ImportedFunc, + /// A pointer to an array of signature ids. Conceptually, this maps + /// from a static, module-local signature id to a runtime-global + /// signature id. This is used to allow call-indirect to other + /// modules safely. + pub(crate) dynamic_sigindices: *const SigId, + + pub(crate) local_functions: *const *const Func, + local_backing: *mut LocalBacking, import_backing: *mut ImportBacking, module: *const ModuleInner, @@ -59,6 +67,9 @@ impl Ctx { imported_globals: import_backing.vm_globals.as_mut_ptr(), imported_funcs: import_backing.vm_functions.as_mut_ptr(), + dynamic_sigindices: local_backing.dynamic_sigindices.as_ptr(), + local_functions: local_backing.local_functions.as_ptr(), + local_backing, import_backing, module, @@ -86,6 +97,9 @@ impl Ctx { imported_globals: import_backing.vm_globals.as_mut_ptr(), imported_funcs: import_backing.vm_functions.as_mut_ptr(), + dynamic_sigindices: local_backing.dynamic_sigindices.as_ptr(), + local_functions: local_backing.local_functions.as_ptr(), + local_backing, import_backing, module, @@ -116,7 +130,7 @@ impl Ctx { pub fn memory(&self, mem_index: u32) -> &Memory { let module = unsafe { &*self.module }; let mem_index = MemoryIndex::new(mem_index as usize); - match mem_index.local_or_import(module) { + match mem_index.local_or_import(&module.info) { LocalOrImport::Local(local_mem_index) => unsafe { let local_backing = &*self.local_backing; &local_backing.memories[local_mem_index] @@ -163,12 +177,17 @@ impl Ctx { pub fn offset_signatures() -> u8 { 7 * (mem::size_of::() as u8) } + + pub fn offset_local_functions() -> u8 { + 8 * (mem::size_of::() as u8) + } } enum InnerFunc {} /// Used to provide type safety (ish) for passing around function pointers. /// The typesystem ensures this cannot be dereferenced since an /// empty enum cannot actually exist. +#[repr(C)] pub struct Func(InnerFunc); /// An imported function, which contains the vmctx that owns this function. @@ -354,6 +373,11 @@ mod vm_offset_tests { Ctx::offset_imported_funcs() as usize, offset_of!(Ctx => imported_funcs).get_byte_offset(), ); + + assert_eq!( + Ctx::offset_local_functions() as usize, + offset_of!(Ctx => local_functions).get_byte_offset(), + ); } #[test] @@ -459,6 +483,9 @@ mod vm_ctx_tests { vm_memories: Map::new().into_boxed_map(), vm_tables: Map::new().into_boxed_map(), vm_globals: Map::new().into_boxed_map(), + + dynamic_sigindices: Map::new().into_boxed_map(), + local_functions: Map::new().into_boxed_map(), }; let mut import_backing = ImportBacking { memories: Map::new().into_boxed_map(), @@ -495,7 +522,10 @@ mod vm_ctx_tests { fn generate_module() -> ModuleInner { use super::Func; - use crate::backend::{Backend, FuncResolver, ProtectedCaller, Token, UserTrapper}; + use crate::backend::{ + sys::Memory, Backend, CacheGen, FuncResolver, ProtectedCaller, Token, UserTrapper, + }; + use crate::cache::{Error as CacheError, WasmHash}; use crate::error::RuntimeResult; use crate::types::{FuncIndex, LocalFuncIndex, Value}; use hashbrown::HashMap; @@ -526,10 +556,19 @@ mod vm_ctx_tests { unimplemented!() } } + impl CacheGen for Placeholder { + fn generate_cache( + &self, + module: &ModuleInner, + ) -> Result<(Box, Box<[u8]>, Memory), CacheError> { + unimplemented!() + } + } ModuleInner { func_resolver: Box::new(Placeholder), protected_caller: Box::new(Placeholder), + cache_gen: Box::new(Placeholder), info: ModuleInfo { memories: Map::new(), globals: Map::new(), diff --git a/lib/runtime-core/src/vmcalls.rs b/lib/runtime-core/src/vmcalls.rs index 8a6e683b5..b428fb24e 100644 --- a/lib/runtime-core/src/vmcalls.rs +++ b/lib/runtime-core/src/vmcalls.rs @@ -13,23 +13,22 @@ use crate::{ // +****************************+ pub unsafe extern "C" fn local_static_memory_grow( + ctx: &mut vm::Ctx, memory_index: LocalMemoryIndex, delta: Pages, - ctx: &mut vm::Ctx, ) -> i32 { let local_memory = *ctx.memories.add(memory_index.index()); let memory = (*local_memory).memory as *mut StaticMemory; - if let Some(old) = (*memory).grow(delta, &mut *local_memory) { - old.0 as i32 - } else { - -1 + match (*memory).grow(delta, &mut *local_memory) { + Ok(old) => old.0 as i32, + Err(_) => -1, } } pub unsafe extern "C" fn local_static_memory_size( - memory_index: LocalMemoryIndex, ctx: &vm::Ctx, + memory_index: LocalMemoryIndex, ) -> Pages { let local_memory = *ctx.memories.add(memory_index.index()); let memory = (*local_memory).memory as *mut StaticMemory; @@ -38,23 +37,22 @@ pub unsafe extern "C" fn local_static_memory_size( } pub unsafe extern "C" fn local_dynamic_memory_grow( + ctx: &mut vm::Ctx, memory_index: LocalMemoryIndex, delta: Pages, - ctx: &mut vm::Ctx, ) -> i32 { let local_memory = *ctx.memories.add(memory_index.index()); let memory = (*local_memory).memory as *mut DynamicMemory; - if let Some(old) = (*memory).grow(delta, &mut *local_memory) { - old.0 as i32 - } else { - -1 + match (*memory).grow(delta, &mut *local_memory) { + Ok(old) => old.0 as i32, + Err(_) => -1, } } pub unsafe extern "C" fn local_dynamic_memory_size( - memory_index: LocalMemoryIndex, ctx: &vm::Ctx, + memory_index: LocalMemoryIndex, ) -> Pages { let local_memory = *ctx.memories.add(memory_index.index()); let memory = (*local_memory).memory as *mut DynamicMemory; @@ -67,23 +65,22 @@ pub unsafe extern "C" fn local_dynamic_memory_size( // +****************************+ pub unsafe extern "C" fn imported_static_memory_grow( + ctx: &mut vm::Ctx, import_memory_index: ImportedMemoryIndex, delta: Pages, - ctx: &mut vm::Ctx, ) -> i32 { let local_memory = *ctx.imported_memories.add(import_memory_index.index()); let memory = (*local_memory).memory as *mut StaticMemory; - if let Some(old) = (*memory).grow(delta, &mut *local_memory) { - old.0 as i32 - } else { - -1 + match (*memory).grow(delta, &mut *local_memory) { + Ok(old) => old.0 as i32, + Err(_) => -1, } } pub unsafe extern "C" fn imported_static_memory_size( - import_memory_index: ImportedMemoryIndex, ctx: &vm::Ctx, + import_memory_index: ImportedMemoryIndex, ) -> Pages { let local_memory = *ctx.imported_memories.add(import_memory_index.index()); let memory = (*local_memory).memory as *mut StaticMemory; @@ -92,23 +89,22 @@ pub unsafe extern "C" fn imported_static_memory_size( } pub unsafe extern "C" fn imported_dynamic_memory_grow( + ctx: &mut vm::Ctx, memory_index: ImportedMemoryIndex, delta: Pages, - ctx: &mut vm::Ctx, ) -> i32 { let local_memory = *ctx.imported_memories.add(memory_index.index()); let memory = (*local_memory).memory as *mut DynamicMemory; - if let Some(old) = (*memory).grow(delta, &mut *local_memory) { - old.0 as i32 - } else { - -1 + match (*memory).grow(delta, &mut *local_memory) { + Ok(old) => old.0 as i32, + Err(_) => -1, } } pub unsafe extern "C" fn imported_dynamic_memory_size( - memory_index: ImportedMemoryIndex, ctx: &vm::Ctx, + memory_index: ImportedMemoryIndex, ) -> Pages { let local_memory = *ctx.imported_memories.add(memory_index.index()); let memory = (*local_memory).memory as *mut DynamicMemory; @@ -121,9 +117,9 @@ pub unsafe extern "C" fn imported_dynamic_memory_size( // +****************************+ pub unsafe extern "C" fn local_table_grow( + ctx: &mut vm::Ctx, table_index: LocalTableIndex, delta: u32, - ctx: &mut vm::Ctx, ) -> i32 { let _ = table_index; let _ = delta; @@ -131,7 +127,7 @@ pub unsafe extern "C" fn local_table_grow( unimplemented!() } -pub unsafe extern "C" fn local_table_size(table_index: LocalTableIndex, ctx: &vm::Ctx) -> u32 { +pub unsafe extern "C" fn local_table_size(ctx: &vm::Ctx, table_index: LocalTableIndex) -> u32 { let _ = table_index; let _ = ctx; unimplemented!() diff --git a/lib/runtime/Cargo.toml b/lib/runtime/Cargo.toml index 0debeeeed..0b7a3ea43 100644 --- a/lib/runtime/Cargo.toml +++ b/lib/runtime/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-runtime" -version = "0.1.4" +version = "0.2.1" description = "Wasmer runtime library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -9,13 +9,33 @@ edition = "2018" readme = "README.md" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.1.2" } -wasmer-clif-backend = { path = "../clif-backend", version = "0.1.2", optional = true } wasmer-dynasm-backend = { path = "../dynasm-backend", optional = true } lazy_static = "1.2.0" +memmap = "0.7.0" + +[dependencies.wasmer-runtime-core] +path = "../runtime-core" +version = "0.2.1" + +[dependencies.wasmer-clif-backend] +path = "../clif-backend" +version = "0.2.0" +optional = true + +[dev-dependencies] +tempfile = "3.0.7" +criterion = "0.2" +wabt = "0.7.4" + +[target.'cfg(not(windows))'.dependencies.wasmer-llvm-backend] +path = "../llvm-backend" [features] default = ["default-compiler"] default-compiler = ["wasmer-clif-backend", "wasmer-dynasm-backend"] cache = ["default-compiler"] debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"] + +[[bench]] +name = "nginx" +harness = false diff --git a/lib/runtime/README.md b/lib/runtime/README.md index bd9bb0f0c..5e5542c8d 100644 --- a/lib/runtime/README.md +++ b/lib/runtime/README.md @@ -51,7 +51,7 @@ fn main() -> error::Result<()> { // We're not importing anything, so make an empty import object. let import_object = imports! {}; - let mut instance = instantiate(WASM, import_object)?; + let mut instance = instantiate(WASM, &import_object)?; let values = instance .func("add_one")? diff --git a/lib/runtime/benches/nginx.rs b/lib/runtime/benches/nginx.rs new file mode 100644 index 000000000..1466addb1 --- /dev/null +++ b/lib/runtime/benches/nginx.rs @@ -0,0 +1,53 @@ +#[macro_use] +extern crate criterion; +use criterion::Criterion; +use tempfile::tempdir; +use wasmer_runtime::{ + cache::{Cache, FileSystemCache, WasmHash}, + compile, validate, +}; + +static NGINX_WASM: &'static [u8] = include_bytes!("../../../examples/nginx/nginx.wasm"); + +fn compile_module() { + compile(NGINX_WASM).unwrap(); +} + +fn load_module(hash: WasmHash, cache: &impl Cache) { + cache.load(hash).expect("could not load module"); +} + +fn hashing_benchmark(c: &mut Criterion) { + c.bench_function("nginx HASH", |b| b.iter(|| WasmHash::generate(NGINX_WASM))); +} + +fn validate_benchmark(c: &mut Criterion) { + c.bench_function("nginx validate", |b| b.iter(|| validate(NGINX_WASM))); +} + +fn compile_benchmark(c: &mut Criterion) { + c.bench_function("nginx compile", |b| b.iter(|| compile_module())); +} + +fn load_benchmark(c: &mut Criterion) { + c.bench_function("nginx load", |b| { + let tempdir = tempdir().unwrap(); + let mut cache = unsafe { + FileSystemCache::new(tempdir.path()).expect("unable to create file system cache") + }; + let module = compile(NGINX_WASM).unwrap(); + let wasm_hash = WasmHash::generate(NGINX_WASM); + cache + .store(wasm_hash, module) + .expect("unable to store into cache"); + + b.iter(|| load_module(wasm_hash, &cache)) + }); +} + +criterion_group! { + name = benches; + config = Criterion::default().sample_size(10); + targets = validate_benchmark, hashing_benchmark, compile_benchmark, load_benchmark +} +criterion_main!(benches); diff --git a/lib/runtime/examples/call.rs b/lib/runtime/examples/call.rs new file mode 100644 index 000000000..dbd29c7e8 --- /dev/null +++ b/lib/runtime/examples/call.rs @@ -0,0 +1,58 @@ +use wasmer_runtime::{compile, error, imports, Ctx, Func, Value}; + +use wabt::wat2wasm; + +static WAT: &'static str = r#" + (module + (type (;0;) (func (result i32))) + (func $dbz (result i32) + i32.const 42 + i32.const 0 + i32.div_u + ) + (export "dbz" (func $dbz)) + ) +"#; + +// static WAT2: &'static str = r#" +// (module +// (type $t0 (func (param i32))) +// (type $t1 (func)) +// (func $print_i32 (export "print_i32") (type $t0) (param $lhs i32)) +// (func $print (export "print") (type $t1)) +// (table $table (export "table") 10 20 anyfunc) +// (memory $memory (export "memory") 1 2) +// (global $global_i32 (export "global_i32") i32 (i32.const 666))) +// "#; + +fn get_wasm() -> Vec { + wat2wasm(WAT).unwrap() +} + +fn foobar(ctx: &mut Ctx) -> i32 { + 42 +} + +fn main() -> Result<(), error::Error> { + let wasm = get_wasm(); + + let module = compile(&wasm)?; + + // let import_module = compile(&wat2wasm(WAT2).unwrap())?; + // let import_instance = import_module.instantiate(&imports! {})?; + + // let imports = imports! { + // "spectest" => import_instance, + // }; + + println!("instantiating"); + let instance = module.instantiate(&imports! {})?; + + let foo = instance.dyn_func("dbz")?; + + let result = foo.call(&[]); + + println!("result: {:?}", result); + + Ok(()) +} diff --git a/lib/runtime/src/cache.rs b/lib/runtime/src/cache.rs index ceb6d171e..6ebbf1017 100644 --- a/lib/runtime/src/cache.rs +++ b/lib/runtime/src/cache.rs @@ -1,128 +1,108 @@ use crate::Module; -use std::path::Path; -use wasmer_runtime_core::cache::{hash_data, Cache as CoreCache}; +use memmap::Mmap; +use std::{ + fs::{create_dir_all, File}, + io::{self, Write}, + path::PathBuf, +}; -pub use wasmer_runtime_core::cache::Error; +use wasmer_runtime_core::cache::Error as CacheError; +pub use wasmer_runtime_core::cache::{Artifact, Cache, WasmHash}; -/// On-disk storage of compiled WebAssembly. +/// Representation of a directory that contains compiled wasm artifacts. /// -/// A `Cache` can be used to quickly reload already -/// compiled WebAssembly from a previous execution -/// during which the wasm was explicitly compiled -/// as a `Cache`. +/// The `FileSystemCache` type implements the [`Cache`] trait, which allows it to be used +/// generically when some sort of cache is required. +/// +/// [`Cache`]: trait.Cache.html /// /// # Usage: /// +/// ```rust +/// use wasmer_runtime::cache::{Cache, FileSystemCache, WasmHash}; +/// +/// # use wasmer_runtime::{Module, error::CacheError}; +/// fn store_module(module: Module) -> Result { +/// // Create a new file system cache. +/// // This is unsafe because we can't ensure that the artifact wasn't +/// // corrupted or tampered with. +/// let mut fs_cache = unsafe { FileSystemCache::new("some/directory/goes/here")? }; +/// // Compute a key for a given WebAssembly binary +/// let key = WasmHash::generate(&[]); +/// // Store a module into the cache given a key +/// fs_cache.store(key, module.clone())?; +/// Ok(module) +/// } /// ``` -/// use wasmer_runtime::{compile_cache, Cache}; -/// -/// # use wasmer_runtime::error::{CompileResult, CacheError}; -/// # fn make_cache(wasm: &[u8]) -> CompileResult<()> { -/// // Make a cache. -/// let cache = compile_cache(wasm)?; -/// -/// # Ok(()) -/// # } -/// # fn usage_cache(cache: Cache) -> Result<(), CacheError> { -/// // Store the cache in a file. -/// cache.store("some_cache_file")?; -/// -/// // Load the cache. -/// let cache = Cache::load("some_cache_file")?; -/// let module = unsafe { cache.into_module()? }; -/// # Ok(()) -/// # } -/// ``` -/// -/// # Performance Characteristics: -/// -/// Loading caches from files has been optimized for latency. -/// There is still more work to do that will reduce -/// loading time, especially for very large modules, -/// but it will require signifigant internal work. -/// -/// # Drawbacks: -/// -/// Due to internal shortcomings, you cannot convert -/// a module into a `Cache`. This means that compiling -/// into a `Cache` and then converting into a module -/// has more overhead than directly compiling -/// into a [`Module`]. -/// -/// [`Module`]: struct.Module.html -pub struct Cache(pub(crate) CoreCache); +pub struct FileSystemCache { + path: PathBuf, +} -impl Cache { - /// Load a `Cache` from the file specified by `path`. +impl FileSystemCache { + /// Construct a new `FileSystemCache` around the specified directory. /// - /// # Usage: - /// - /// ``` - /// use wasmer_runtime::Cache; - /// # use wasmer_runtime::error::CacheError; - /// - /// # fn load_cache() -> Result<(), CacheError> { - /// let cache = Cache::load("some_file.cache")?; - /// # Ok(()) - /// # } - /// ``` - pub fn load>(path: P) -> Result { - CoreCache::open(path).map(|core_cache| Cache(core_cache)) - } + /// # Note: + /// This method is unsafe because there's no way to ensure the artifacts + /// stored in this cache haven't been corrupted or tampered with. + pub unsafe fn new>(path: P) -> io::Result { + let path: PathBuf = path.into(); - /// Convert a `Cache` into a [`Module`]. - /// - /// [`Module`]: struct.Module.html - /// - /// # Usage: - /// - /// ``` - /// use wasmer_runtime::Cache; - /// - /// # use wasmer_runtime::error::CacheError; - /// # fn cache2module(cache: Cache) -> Result<(), CacheError> { - /// let module = unsafe { cache.into_module()? }; - /// # Ok(()) - /// # } - /// ``` - /// - /// # Notes: - /// - /// This method is unsafe because the runtime cannot confirm - /// that this cache was not tampered with or corrupted. - pub unsafe fn into_module(self) -> Result { - let default_compiler = super::default_compiler(); - - wasmer_runtime_core::load_cache_with(self.0, default_compiler) - } - - /// Compare the Sha256 hash of the wasm this cache was build - /// from with some other WebAssembly. - /// - /// The main use-case for this is invalidating old caches. - pub fn compare_wasm(&self, wasm: &[u8]) -> bool { - let param_wasm_hash = hash_data(wasm); - self.0.wasm_hash() as &[u8] == ¶m_wasm_hash as &[u8] - } - - /// Store this cache in a file. - /// - /// # Notes: - /// - /// If a file exists at the specified path, it will be overwritten. - /// - /// # Usage: - /// - /// ``` - /// use wasmer_runtime::Cache; - /// - /// # use wasmer_runtime::error::CacheError; - /// # fn store_cache(cache: Cache) -> Result<(), CacheError> { - /// cache.store("some_file.cache")?; - /// # Ok(()) - /// # } - /// ``` - pub fn store>(&self, path: P) -> Result<(), Error> { - self.0.store(path) + if path.exists() { + let metadata = path.metadata()?; + if metadata.is_dir() { + if !metadata.permissions().readonly() { + Ok(Self { path }) + } else { + // This directory is readonly. + Err(io::Error::new( + io::ErrorKind::PermissionDenied, + format!("the supplied path is readonly: {}", path.display()), + )) + } + } else { + // This path points to a file. + Err(io::Error::new( + io::ErrorKind::PermissionDenied, + format!( + "the supplied path already points to a file: {}", + path.display() + ), + )) + } + } else { + // Create the directory and any parent directories if they don't yet exist. + create_dir_all(&path)?; + Ok(Self { path }) + } + } +} + +impl Cache for FileSystemCache { + type LoadError = CacheError; + type StoreError = CacheError; + + fn load(&self, key: WasmHash) -> Result { + let filename = key.encode(); + let mut new_path_buf = self.path.clone(); + new_path_buf.push(filename); + let file = File::open(new_path_buf)?; + let mmap = unsafe { Mmap::map(&file)? }; + + let serialized_cache = Artifact::deserialize(&mmap[..])?; + unsafe { wasmer_runtime_core::load_cache_with(serialized_cache, super::default_compiler()) } + } + + fn store(&mut self, key: WasmHash, module: Module) -> Result<(), CacheError> { + let filename = key.encode(); + let mut new_path_buf = self.path.clone(); + new_path_buf.push(filename); + + let serialized_cache = module.cache()?; + let buffer = serialized_cache.serialize()?; + + let mut file = File::create(new_path_buf)?; + file.write_all(&buffer)?; + + Ok(()) } } diff --git a/lib/runtime/src/lib.rs b/lib/runtime/src/lib.rs index 5e991a5ea..478408334 100644 --- a/lib/runtime/src/lib.rs +++ b/lib/runtime/src/lib.rs @@ -99,8 +99,7 @@ pub mod wasm { } pub mod error { - #[cfg(feature = "cache")] - pub use super::cache::Error as CacheError; + pub use wasmer_runtime_core::cache::Error as CacheError; pub use wasmer_runtime_core::error::*; } @@ -109,15 +108,10 @@ pub mod units { pub use wasmer_runtime_core::units::{Bytes, Pages}; } -#[cfg(feature = "cache")] -mod cache; +pub mod cache; -#[cfg(feature = "default-compiler")] use wasmer_runtime_core::backend::Compiler; -#[cfg(feature = "cache")] -pub use self::cache::Cache; - /// Compile WebAssembly binary code into a [`Module`]. /// This function is useful if it is necessary to /// compile a module before it can be instantiated @@ -131,7 +125,6 @@ pub use self::cache::Cache; /// binary code of the wasm module you want to compile. /// # Errors: /// If the operation fails, the function returns `Err(error::CompileError::...)`. -#[cfg(feature = "default-compiler")] pub fn compile(wasm: &[u8]) -> error::CompileResult { wasmer_runtime_core::compile_with(&wasm[..], default_compiler()) } @@ -154,44 +147,23 @@ pub fn compile(wasm: &[u8]) -> error::CompileResult { /// `error::CompileError`, `error::LinkError`, or /// `error::RuntimeError` (all combined into an `error::Error`), /// depending on the cause of the failure. -#[cfg(feature = "default-compiler")] pub fn instantiate(wasm: &[u8], import_object: &ImportObject) -> error::Result { let module = compile(wasm)?; module.instantiate(import_object) } -/// Compile wasm into a [`Cache`] that can be stored to a file or -/// converted into [`Module`]. -/// -/// [`Cache`]: struct.Cache.html -/// [`Module`]: struct.Module.html -/// -/// # Usage: -/// -/// ``` -/// # use wasmer_runtime::error::CompileResult; -/// use wasmer_runtime::compile_cache; -/// -/// # fn make_cache(wasm: &[u8]) -> CompileResult<()> { -/// let cache = compile_cache(wasm)?; -/// # Ok(()) -/// # } -/// ``` -#[cfg(feature = "cache")] -pub fn compile_cache(wasm: &[u8]) -> error::CompileResult { - let default_compiler = default_compiler(); - - wasmer_runtime_core::compile_to_cache_with(wasm, default_compiler) - .map(|core_cache| Cache(core_cache)) -} - -#[cfg(feature = "default-compiler")] fn default_compiler() -> &'static dyn Compiler { use lazy_static::lazy_static; - use wasmer_dynasm_backend::SinglePassCompiler; + + #[cfg(feature = "llvm")] + use wasmer_llvm_backend::LLVMCompiler as DefaultCompiler; + + #[cfg(not(feature = "llvm"))] + use wasmer_dynasm_backend::SinglePassCompiler as DefaultCompiler; + // use wasmer_clif_backend::CraneliftCompiler as DefaultCompiler; // TODO Fix default lazy_static! { - static ref DEFAULT_COMPILER: SinglePassCompiler = { SinglePassCompiler {} }; + static ref DEFAULT_COMPILER: DefaultCompiler = { DefaultCompiler::new() }; } &*DEFAULT_COMPILER as &dyn Compiler diff --git a/lib/spectests/Cargo.toml b/lib/spectests/Cargo.toml index c02e8269d..fea4560c5 100644 --- a/lib/spectests/Cargo.toml +++ b/lib/spectests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-spectests" -version = "0.1.2" +version = "0.2.0" description = "Wasmer spectests library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -9,15 +9,20 @@ edition = "2018" build = "build/mod.rs" [dependencies] -wasmer-runtime-core = { path = "../runtime-core" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.2.0" } [build-dependencies] wabt = "0.7.2" [dev-dependencies] -wasmer-clif-backend = { path = "../clif-backend", version = "0.1.2" } +wasmer-clif-backend = { path = "../clif-backend", version = "0.2.0" } wabt = "0.7.2" +[target.'cfg(not(windows))'.dev-dependencies] +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.1.0" } + [features] -default = ["fast-tests", "wasmer-runtime-core/cache", "wasmer-clif-backend/cache"] -fast-tests = [] \ No newline at end of file +default = ["fast-tests"] +fast-tests = [] +clif = [] +llvm = [] \ No newline at end of file diff --git a/lib/spectests/build/spectests.rs b/lib/spectests/build/spectests.rs index 54d9404d6..c1d28ca29 100644 --- a/lib/spectests/build/spectests.rs +++ b/lib/spectests/build/spectests.rs @@ -77,12 +77,12 @@ const TESTS: &[&str] = &[ static COMMON: &'static str = r##" use std::{{f32, f64}}; use wabt::wat2wasm; -use wasmer_clif_backend::CraneliftCompiler; use wasmer_runtime_core::import::ImportObject; use wasmer_runtime_core::types::Value; use wasmer_runtime_core::{{Instance, module::Module}}; use wasmer_runtime_core::error::Result; use wasmer_runtime_core::vm::Ctx; +use wasmer_runtime_core::backend::Compiler; static IMPORT_MODULE: &str = r#" (module @@ -95,9 +95,28 @@ static IMPORT_MODULE: &str = r#" (global $global_i32 (export "global_i32") i32 (i32.const 666))) "#; +#[cfg(feature = "clif")] +fn get_compiler() -> impl Compiler { + use wasmer_clif_backend::CraneliftCompiler; + CraneliftCompiler::new() +} + +#[cfg(feature = "llvm")] +fn get_compiler() -> impl Compiler { + use wasmer_llvm_backend::LLVMCompiler; + LLVMCompiler::new() +} + +#[cfg(not(any(feature = "llvm", feature = "clif")))] +fn get_compiler() -> impl Compiler { + panic!("compiler not specified, activate a compiler via features"); + use wasmer_clif_backend::CraneliftCompiler; + CraneliftCompiler::new() +} + pub fn generate_imports() -> ImportObject { let wasm_binary = wat2wasm(IMPORT_MODULE.as_bytes()).expect("WAST not valid or malformed"); - let module = wasmer_runtime_core::compile_with(&wasm_binary[..], &CraneliftCompiler::new()) + let module = wasmer_runtime_core::compile_with(&wasm_binary[..], &get_compiler()) .expect("WASM can't be compiled"); let instance = module .instantiate(&ImportObject::new()) @@ -358,7 +377,7 @@ fn test_module_{}() {{ let module_str = \"{}\"; println!(\"{{}}\", module_str); let wasm_binary = wat2wasm(module_str.as_bytes()).expect(\"WAST not valid or malformed\"); - let module = wasmer_runtime_core::compile_with(&wasm_binary[..], &CraneliftCompiler::new()).expect(\"WASM can't be compiled\"); + let module = wasmer_runtime_core::compile_with(&wasm_binary[..], &get_compiler()).expect(\"WASM can't be compiled\"); module.instantiate(&generate_imports()).expect(\"WASM can't be instantiated\") }}\n", self.last_module, @@ -381,7 +400,7 @@ fn test_module_{}() {{ "#[test] fn {}_assert_invalid() {{ let wasm_binary = {:?}; - let module = wasmer_runtime_core::compile_with(&wasm_binary, &CraneliftCompiler::new()); + let module = wasmer_runtime_core::compile_with(&wasm_binary, &get_compiler()); assert!(module.is_err(), \"WASM should not compile as is invalid\"); }}\n", command_name, @@ -512,7 +531,7 @@ fn {}_assert_invalid() {{ "#[test] fn {}_assert_malformed() {{ let wasm_binary = {:?}; - let compilation = wasmer_runtime_core::compile_with(&wasm_binary, &CraneliftCompiler::new()); + let compilation = wasmer_runtime_core::compile_with(&wasm_binary, &get_compiler()); assert!(compilation.is_err(), \"WASM should not compile as is malformed\"); }}\n", command_name, @@ -568,7 +587,7 @@ fn {}_assert_malformed() {{ let assertion = if expected.len() > 0 && is_nan(&expected[0]) { format!( "let expected = {expected_result}; - if let {return_type_destructure} = result.clone().unwrap().first().unwrap() {{ + if let {return_type_destructure} = result.as_ref().unwrap().first().unwrap() {{ assert!((*result as {return_type}).is_nan()); assert_eq!((*result as {return_type}).is_sign_positive(), (expected as {return_type}).is_sign_positive()); }} else {{ diff --git a/lib/spectests/examples/simple/main.rs b/lib/spectests/examples/simple/main.rs index fb215ee21..595bb3680 100644 --- a/lib/spectests/examples/simple/main.rs +++ b/lib/spectests/examples/simple/main.rs @@ -1,8 +1,6 @@ -use std::fs::remove_file; use wabt::wat2wasm; -use wasmer_clif_backend::CraneliftCompiler; use wasmer_runtime_core::{ - cache::Cache, + backend::Compiler, error, global::Global, memory::Memory, @@ -12,13 +10,31 @@ use wasmer_runtime_core::{ units::Pages, }; +#[cfg(feature = "clif")] +fn get_compiler() -> impl Compiler { + use wasmer_clif_backend::CraneliftCompiler; + CraneliftCompiler::new() +} + +#[cfg(feature = "llvm")] +fn get_compiler() -> impl Compiler { + use wasmer_llvm_backend::LLVMCompiler; + LLVMCompiler::new() +} + +#[cfg(not(any(feature = "llvm", feature = "clif")))] +fn get_compiler() -> impl Compiler { + panic!("compiler not specified, activate a compiler via features"); + use wasmer_clif_backend::CraneliftCompiler; + CraneliftCompiler::new() +} + static EXAMPLE_WASM: &'static [u8] = include_bytes!("simple.wasm"); fn main() -> error::Result<()> { - let compiler = CraneliftCompiler::new(); let wasm_binary = wat2wasm(IMPORT_MODULE.as_bytes()).expect("WAST not valid or malformed"); - let inner_module = wasmer_runtime_core::compile_with(&wasm_binary, &CraneliftCompiler::new())?; + let inner_module = wasmer_runtime_core::compile_with(&wasm_binary, &get_compiler())?; let memory = Memory::new(MemoryDescriptor { minimum: Pages(1), @@ -53,7 +69,7 @@ fn main() -> error::Result<()> { "env" => inner_instance, }; - let outer_module = wasmer_runtime_core::compile_with(EXAMPLE_WASM, &CraneliftCompiler::new())?; + let outer_module = wasmer_runtime_core::compile_with(EXAMPLE_WASM, &get_compiler())?; let outer_instance = outer_module.instantiate(&outer_imports)?; let ret = outer_instance.call("main", &[Value::I32(42)])?; println!("ret: {:?}", ret); @@ -61,7 +77,7 @@ fn main() -> error::Result<()> { Ok(()) } -fn print_num(n: i32, ctx: &mut vm::Ctx) -> Result { +fn print_num(ctx: &mut vm::Ctx, n: i32) -> Result { println!("print_num({})", n); let memory: &Memory = ctx.memory(0); 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/lib/spectests/tests/semantics.rs b/lib/spectests/tests/semantics.rs index c364dca29..0bffa9dca 100644 --- a/lib/spectests/tests/semantics.rs +++ b/lib/spectests/tests/semantics.rs @@ -31,7 +31,7 @@ mod tests { match result { Err(err) => match err { - CallError::Runtime(RuntimeError::Unknown { msg }) => { + CallError::Runtime(RuntimeError::Trap { msg }) => { assert!(!msg.contains("segmentation violation")); assert!(!msg.contains("bus error")); } diff --git a/lib/win-exception-handler/Cargo.toml b/lib/win-exception-handler/Cargo.toml new file mode 100644 index 000000000..18855212b --- /dev/null +++ b/lib/win-exception-handler/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "wasmer-win-exception-handler" +version = "0.2.0" +description = "Wasmer runtime exception handling for Windows" +license = "MIT" +authors = ["The Wasmer Engineering Team "] +repository = "https://github.com/wasmerio/wasmer" +edition = "2018" + +[target.'cfg(windows)'.dependencies] +wasmer-runtime-core = { path = "../runtime-core", version = "0.2.0" } +winapi = { version = "0.3", features = ["winbase", "errhandlingapi", "minwindef", "minwinbase", "winnt"] } +libc = "0.2.49" + +[build-dependencies] +cmake = "0.1.35" +bindgen = "0.46.0" +regex = "1.0.6" diff --git a/lib/win-exception-handler/build.rs b/lib/win-exception-handler/build.rs new file mode 100644 index 000000000..7376c6329 --- /dev/null +++ b/lib/win-exception-handler/build.rs @@ -0,0 +1,11 @@ +use cmake::Config; + +fn main() { + #[cfg(target_os = "windows")] + { + let project_name = "exception_handling"; + let dst = Config::new(project_name).build(); + println!("cargo:rustc-link-search=native={}", dst.display()); + println!("cargo:rustc-link-lib=static={}", project_name); + } +} diff --git a/lib/win-exception-handler/exception_handling/.gitignore b/lib/win-exception-handler/exception_handling/.gitignore new file mode 100644 index 000000000..8eee68fef --- /dev/null +++ b/lib/win-exception-handler/exception_handling/.gitignore @@ -0,0 +1 @@ +cmake-build-* diff --git a/lib/win-exception-handler/exception_handling/CMakeLists.txt b/lib/win-exception-handler/exception_handling/CMakeLists.txt new file mode 100644 index 000000000..47376c78f --- /dev/null +++ b/lib/win-exception-handler/exception_handling/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.0) +project(exception_handling C) + +add_library(exception_handling STATIC exception_handling.c) + +install(TARGETS exception_handling DESTINATION .) diff --git a/lib/win-exception-handler/exception_handling/exception_handling.c b/lib/win-exception-handler/exception_handling/exception_handling.c new file mode 100644 index 000000000..eb45b204d --- /dev/null +++ b/lib/win-exception-handler/exception_handling/exception_handling.c @@ -0,0 +1,86 @@ +#include +#include +#include "exception_handling.h" + +#define CALL_FIRST 1 + +__declspec(thread) jmp_buf jmpBuf; +__declspec(thread) PVOID caughtExceptionAddress; +__declspec(thread) DWORD64 caughtInstructionPointer; +__declspec(thread) PVOID savedStackPointer; +__declspec(thread) BOOL exceptionHandlerInstalled = FALSE; +__declspec(thread) BOOL alreadyHandlingException = FALSE; +__declspec(thread) PVOID handle; + +void longjmpOutOfHere() { + longjmp(jmpBuf, 1); +} + +/// Get the current address that we use to jmp, the no inline is important +static __declspec(noinline) void *get_callee_frame_address(void) { + return _AddressOfReturnAddress(); +} + +static LONG WINAPI +exceptionHandler(struct _EXCEPTION_POINTERS *ExceptionInfo) { + EXCEPTION_RECORD* pExceptionRecord = ExceptionInfo->ExceptionRecord; + PCONTEXT pCONTEXT = ExceptionInfo->ContextRecord; + caughtExceptionAddress = pExceptionRecord->ExceptionAddress; + caughtInstructionPointer = pCONTEXT->Rip; + if (alreadyHandlingException == TRUE) { + return EXCEPTION_CONTINUE_SEARCH; + } + alreadyHandlingException = TRUE; + + // Basically, here, we coerce the os to resume us into a context that calls `longjmp` instead of just continuing. + // Presumably, we cannot `longjmp` out of the signal/exception context, like we can on unix. + pCONTEXT->Rip = (uintptr_t)(&longjmpOutOfHere); + pCONTEXT->Rsp = (uintptr_t)(savedStackPointer); + return EXCEPTION_CONTINUE_EXECUTION; +} + +static void removeExceptionHandler() { + if (exceptionHandlerInstalled == FALSE) { + return; + } + RemoveVectoredExceptionHandler(handle); + exceptionHandlerInstalled = FALSE; +} + +uint8_t callProtected(trampoline_t trampoline, + const struct wasmer_instance_context_t* ctx, + const struct func_t* func, + const uint64_t* param_vec, + uint64_t* return_vec, + struct call_protected_result_t* out_result) { + + // install exception handler + if (exceptionHandlerInstalled == FALSE) { + exceptionHandlerInstalled = TRUE; + handle = AddVectoredExceptionHandler(CALL_FIRST, exceptionHandler); + } + + // jmp jmp jmp! + int signum = setjmp(jmpBuf); + if (signum == 0) { + // save the stack pointer + savedStackPointer = get_callee_frame_address(); + trampoline(ctx, func, param_vec, return_vec); + out_result->code = 0; + out_result->exceptionAddress = 0; + out_result->instructionPointer = 0; + + removeExceptionHandler(); + return TRUE; + } + + out_result->code = (uint64_t)signum; + out_result->exceptionAddress = (uint64_t)caughtExceptionAddress; + out_result->instructionPointer = caughtInstructionPointer; + + caughtExceptionAddress = 0; + caughtInstructionPointer = 0; + + removeExceptionHandler(); + return FALSE; +} diff --git a/lib/win-exception-handler/exception_handling/exception_handling.h b/lib/win-exception-handler/exception_handling/exception_handling.h new file mode 100644 index 000000000..cd5472149 --- /dev/null +++ b/lib/win-exception-handler/exception_handling/exception_handling.h @@ -0,0 +1,25 @@ +#ifndef WASMER_EXCEPTION_HANDLING_H +#define WASMER_EXCEPTION_HANDLING_H + +#include + +struct func_t; +struct wasmer_instance_context_t; + +typedef void(*trampoline_t)(struct wasmer_instance_context_t*, const struct func_t*, const uint64_t*, uint64_t*); + +struct call_protected_result_t { + uint64_t code; + uint64_t exceptionAddress; + uint64_t instructionPointer; +}; + +uint8_t callProtected( + trampoline_t trampoline, + const struct wasmer_instance_context_t* ctx, + const struct func_t* func, + const uint64_t* param_vec, + uint64_t* return_vec, + struct call_protected_result_t* out_result); + +#endif //WASMER_EXCEPTION_HANDLING_H diff --git a/lib/win-exception-handler/src/exception_handling.rs b/lib/win-exception-handler/src/exception_handling.rs new file mode 100644 index 000000000..04c3c7f2e --- /dev/null +++ b/lib/win-exception-handler/src/exception_handling.rs @@ -0,0 +1,53 @@ +use std::ffi::c_void; +use wasmer_runtime_core::vm::{Ctx, Func}; + +type Trampoline = unsafe extern "C" fn(*mut Ctx, *const Func, *const u64, *mut u64) -> c_void; +type CallProtectedResult = Result<(), CallProtectedData>; + +#[repr(C)] +pub struct CallProtectedData { + pub code: u64, + pub exceptionAddress: u64, + pub instructionPointer: u64, +} + +extern "C" { + #[link_name = "callProtected"] + pub fn __call_protected( + trampoline: Trampoline, + ctx: *mut Ctx, + func: *const Func, + param_vec: *const u64, + return_vec: *mut u64, + out_result: *mut CallProtectedData, + ) -> u8; +} + +pub fn _call_protected( + trampoline: Trampoline, + ctx: *mut Ctx, + func: *const Func, + param_vec: *const u64, + return_vec: *mut u64, +) -> CallProtectedResult { + let mut out_result = CallProtectedData { + code: 0, + exceptionAddress: 0, + instructionPointer: 0, + }; + let result = unsafe { + __call_protected( + trampoline, + ctx, + func, + param_vec, + return_vec, + &mut out_result, + ) + }; + if result == 1 { + Ok(()) + } else { + Err(out_result) + } +} diff --git a/lib/win-exception-handler/src/lib.rs b/lib/win-exception-handler/src/lib.rs new file mode 100644 index 000000000..bc4a142b5 --- /dev/null +++ b/lib/win-exception-handler/src/lib.rs @@ -0,0 +1,5 @@ +#[cfg(windows)] +mod exception_handling; + +#[cfg(windows)] +pub use self::exception_handling::*; diff --git a/src/bin/wasmer.rs b/src/bin/wasmer.rs index 4ebfb76d3..e36e99cb6 100644 --- a/src/bin/wasmer.rs +++ b/src/bin/wasmer.rs @@ -1,5 +1,6 @@ extern crate structopt; +use std::env; use std::fs::File; use std::io; use std::io::Read; @@ -11,6 +12,7 @@ use structopt::StructOpt; use wasmer::webassembly::InstanceABI; use wasmer::*; use wasmer_emscripten; +use wasmer_runtime::cache::{Cache as BaseCache, FileSystemCache, WasmHash}; #[derive(Debug, StructOpt)] #[structopt(name = "wasmer", about = "Wasm execution runtime.")] @@ -20,6 +22,10 @@ enum CLIOptions { #[structopt(name = "run")] Run(Run), + /// Wasmer cache + #[structopt(name = "cache")] + Cache(Cache), + /// Update wasmer to the latest version #[structopt(name = "self-update")] SelfUpdate, @@ -27,8 +33,9 @@ enum CLIOptions { #[derive(Debug, StructOpt)] struct Run { - #[structopt(short = "d", long = "debug")] - debug: bool, + // Disable the cache + #[structopt(long = "disable-cache")] + disable_cache: bool, /// Input file #[structopt(parse(from_os_str))] @@ -39,6 +46,15 @@ struct Run { args: Vec, } +#[derive(Debug, StructOpt)] +enum Cache { + #[structopt(name = "clean")] + Clean, + + #[structopt(name = "dir")] + Dir, +} + /// Read the contents of a file fn read_file_contents(path: &PathBuf) -> Result, io::Error> { let mut buffer: Vec = Vec::new(); @@ -49,8 +65,26 @@ fn read_file_contents(path: &PathBuf) -> Result, io::Error> { Ok(buffer) } +fn get_cache_dir() -> PathBuf { + match env::var("WASMER_CACHE_DIR") { + Ok(dir) => PathBuf::from(dir), + Err(_) => { + // We use a temporal directory for saving cache files + let mut temp_dir = env::temp_dir(); + temp_dir.push("wasmer"); + temp_dir + } + } +} + /// Execute a wasm/wat file fn execute_wasm(options: &Run) -> Result<(), String> { + // force disable caching on windows + #[cfg(target_os = "windows")] + let disable_cache = true; + #[cfg(not(target_os = "windows"))] + let disable_cache = options.disable_cache; + let wasm_path = &options.path; let mut wasm_binary: Vec = read_file_contents(wasm_path).map_err(|err| { @@ -66,10 +100,46 @@ fn execute_wasm(options: &Run) -> Result<(), String> { .map_err(|e| format!("Can't convert from wast to wasm: {:?}", e))?; } - let module = webassembly::compile(&wasm_binary[..]) - .map_err(|e| format!("Can't compile module: {:?}", e))?; + let module = if !disable_cache { + // If we have cache enabled - let (_abi, import_object, em_globals) = if wasmer_emscripten::is_emscripten_module(&module) { + // We generate a hash for the given binary, so we can use it as key + // for the Filesystem cache + let hash = WasmHash::generate(&wasm_binary); + + let wasmer_cache_dir = get_cache_dir(); + + // We create a new cache instance. + // It could be possible to use any other kinds of caching, as long as they + // implement the Cache trait (with save and load functions) + let mut cache = unsafe { + FileSystemCache::new(wasmer_cache_dir).map_err(|e| format!("Cache error: {:?}", e))? + }; + + // cache.load will return the Module if it's able to deserialize it properly, and an error if: + // * The file is not found + // * The file exists, but it's corrupted or can't be converted to a module + let module = match cache.load(hash) { + Ok(module) => { + // We are able to load the module from cache + module + } + Err(_) => { + let module = webassembly::compile(&wasm_binary[..]) + .map_err(|e| format!("Can't compile module: {:?}", e))?; + + // We save the module into a cache file + cache.store(hash, module.clone()).unwrap(); + module + } + }; + module + } else { + webassembly::compile(&wasm_binary[..]) + .map_err(|e| format!("Can't compile module: {:?}", e))? + }; + + let (_abi, import_object, _em_globals) = if wasmer_emscripten::is_emscripten_module(&module) { let mut emscripten_globals = wasmer_emscripten::EmscriptenGlobals::new(&module); ( InstanceABI::Emscripten, @@ -113,6 +183,27 @@ fn main() { let options = CLIOptions::from_args(); match options { CLIOptions::Run(options) => run(options), + #[cfg(not(target_os = "windows"))] CLIOptions::SelfUpdate => update::self_update(), + #[cfg(target_os = "windows")] + CLIOptions::SelfUpdate => { + println!("Self update is not supported on Windows. Use install instructions on the Wasmer homepage: https://wasmer.io"); + } + #[cfg(not(target_os = "windows"))] + CLIOptions::Cache(cache) => match cache { + Cache::Clean => { + use std::fs; + let cache_dir = get_cache_dir(); + fs::remove_dir_all(cache_dir.clone()).expect("Can't remove cache dir"); + fs::create_dir(cache_dir.clone()).expect("Can't create cache dir"); + } + Cache::Dir => { + println!("{}", get_cache_dir().to_string_lossy()); + } + }, + #[cfg(target_os = "windows")] + CLIOptions::Cache(_) => { + println!("Caching is disabled for Windows."); + } } } diff --git a/src/installer/wasmer.iss b/src/installer/wasmer.iss new file mode 100644 index 000000000..f844eb4da --- /dev/null +++ b/src/installer/wasmer.iss @@ -0,0 +1,71 @@ +[Setup] +AppName=Wasmer +AppVersion=1.5 +DefaultDirName={pf}\Wasmer +DefaultGroupName=Wasmer +Compression=lzma2 +SolidCompression=yes +OutputDir=.\ +DisableProgramGroupPage=yes +ChangesEnvironment=yes +OutputBaseFilename=WasmerInstaller + +[Files] +Source: "..\..\target\release\wasmer.exe"; DestDir: "{app}\bin" + +[Code] +const EnvironmentKey = 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'; + +procedure EnvAddPath(Path: string); +var + Paths: string; +begin + { Retrieve current path (use empty string if entry not exists) } + if not RegQueryStringValue(HKEY_LOCAL_MACHINE, EnvironmentKey, 'Path', Paths) + then Paths := ''; + + { Skip if string already found in path } + if Pos(';' + Uppercase(Path) + ';', ';' + Uppercase(Paths) + ';') > 0 then exit; + + { App string to the end of the path variable } + Paths := Paths + ';'+ Path +';' + + { Overwrite (or create if missing) path environment variable } + if RegWriteStringValue(HKEY_LOCAL_MACHINE, EnvironmentKey, 'Path', Paths) + then Log(Format('The [%s] added to PATH: [%s]', [Path, Paths])) + else Log(Format('Error while adding the [%s] to PATH: [%s]', [Path, Paths])); +end; + +procedure EnvRemovePath(Path: string); +var + Paths: string; + P: Integer; +begin + { Skip if registry entry not exists } + if not RegQueryStringValue(HKEY_LOCAL_MACHINE, EnvironmentKey, 'Path', Paths) then + exit; + + { Skip if string not found in path } + P := Pos(';' + Uppercase(Path) + ';', ';' + Uppercase(Paths) + ';'); + if P = 0 then exit; + + { Update path variable } + Delete(Paths, P - 1, Length(Path) + 1); + + { Overwrite path environment variable } + if RegWriteStringValue(HKEY_LOCAL_MACHINE, EnvironmentKey, 'Path', Paths) + then Log(Format('The [%s] removed from PATH: [%s]', [Path, Paths])) + else Log(Format('Error while removing the [%s] from PATH: [%s]', [Path, Paths])); +end; + +procedure CurStepChanged(CurStep: TSetupStep); +begin + if CurStep = ssPostInstall + then EnvAddPath(ExpandConstant('{app}') +'\bin'); +end; + +procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep); +begin + if CurUninstallStep = usPostUninstall + then EnvRemovePath(ExpandConstant('{app}') +'\bin'); +end; \ No newline at end of file